本文选取了DedeCMS_v5.7(存储型XSS)、phpmywind5.3(存储型XSS,编辑后触发)、Yiqicms1.9(存储型XSS,多次提交绕过)等进行了环境的复现、漏洞的绕过、漏洞的验证、漏洞的简单修复、及利用

0x01 存储型XSS

1.1 DedeCMS_v5.7

  • 下载地址:

    链接:https://pan.baidu.com/s/116RsJoRbQ9rpVaq_64QUqA
    提取码:eiuo

  • 本地环境配置

    windows+phpstudy+php5.4.45

    将下载后的软件放于WWW目录下,按步骤安装即可

  • 测试说明

    • 上述下载的版本存在的XSS漏洞以及修复,但是亦可能存在未发现的XSS漏洞,此博文为了理解XSS原理,以便自身对XSS测试思路理解
    • 在已修复状态下,复现漏洞环境,并分析修复代码
  • 复现的漏洞要求:

    该漏洞 通过用户在编写订单收货地址的相关参数 注入 XSS Payload,导致 前台查看订单的页面和后台管理员查看订单详情的页面都会被 XSS。
    所以说,可以用来打管理员 Cookie 。

  • 准备:

    1.首先管理员登录后台/dede,添加商品销售栏目
    2.为商品栏目添加一个商品

  • 前台用户选定商品添加购物车:(这里方便测试,选定admin用户)

  • 前台用户编辑订单的收货地址,在这里 address,des,email,postname 都是存在 XSS 的,插入 XSS Payload

  • 查看订单详情,发现插入的XSS Payload都被过滤(说明在提交订单后,对订单内容进行了XSS过滤)

  • 由于订单页面:carbuyaction.php,本地文件查看找到如下代码

    1
    2
    3
    4
    5
    6
    7
    8

    $address = cn_substrR(trim(RemoveXSS($address)),200);
    $des = cn_substrR(RemoveXSS($des),100);
    $postname = cn_substrR(trim(RemoveXSS($postname)),15);
    $tel = preg_replace("#[^-0-9,\/\| ]#", "", $tel);
    $zip = preg_replace("#[^0-9]#", "", $zip);
    $email = cn_substrR(RemoveXSS($email),255);
    if(empty($tel))

    分析:这段代码分别进行了,RemoveXSS、trim、cn_substrR等相应处理,先将地址的RemoveXSS函数过滤去掉,再进行XSS payload提交

  • 再次,提交商品,查看提交XSS payload的订单详情

    分析:发现未使用RemoveXSS函数过滤时,地址存在XSS漏洞

  • 查看后台管理员中,商品订单记录

  • RemoveXSS函数查找

    添加如下代码放置对应文件中(carbuyaction.php),重新提交订单,即可显示函数具体位置

    //测试RemoveXSS位置
    $func = new ReflectionFunction(‘RemoveXSS’);
    var_dump($func->getFileName());

  • 查找结果

  • RemoveXSS代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

function RemoveXSS($val) {
$val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}

$ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);

$found = true;
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(&#0{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2);
$val = preg_replace($pattern, $replacement, $val);
if ($val_before == $val) {
$found = false;
}
}
}
return $val;
}

1.2 PHPMyWind_5.3

  • 下载地址:

    链接:https://pan.baidu.com/s/1ujXDwr4KOcd1iwbtJlnhEg
    提取码:3n1n

  • 采用字符串6666”;!–’=&{()}<>6666`作为简洁探测XSS注入点。注入后查看页面源代码并且寻找是否存在666&quot;等字样来确认是否存在漏洞

  • 后台查看留言模块,对留言进行修改

  • 审查留言,右键点击框架源代码,并搜索6666查看输入位置,并分析

分析:过滤了双引号,左右尖括号
  • 查看本地文件,(message.php),找到如下代码

    1
    2
    $contact  = htmlspecialchars($contact);
    $content = htmlspecialchars($content);

    分析:可知对输入内容利用htmlspecialchars进行了HTML实体转换

  • 跟进content参数。PHPMyWind_5.3/admin/message_update.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>修改留言</title>
<link href="templates/style/admin.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="templates/js/jquery.min.js"></script>
<script type="text/javascript" src="templates/js/checkf.func.js"></script>
<script type="text/javascript" src="editor/kindeditor-min.js"></script>
<script type="text/javascript" src="editor/lang/zh_CN.js"></script>
</head>
<body>
<?php
$row = $dosql->GetOne("SELECT * FROM `#@__message` WHERE `id`=$id");
?>
<div class="formHeader"> <span class="title">修改留言</span> <a href="javascript:location.reload();" class="reload">刷新</a> </div>
<form name="form" id="form" method="post" action="message_save.php">
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="formTable">
<tr>
<td width="25%" height="40" align="right">用户名:</td>
<td width="75%"><strong><?php echo $row['nickname'] ?></strong></td>
</tr>
<tr>
<td height="40" align="right">联系方式:</td>
<td><input type="text" name="contact" id="contact" class="input" value="<?php echo $row['contact'] ?>" /></td>
</tr>
<tr>
<td height="198" align="right">留言内容:</td>
<td><textarea name="content" id="content"><?php echo $row['content'] ?></textarea>
<script>
  • 这行代码<td><textarea name="content" id="content"><?php echo $row['content'] ?></textarea>后台直接取出content参数,数据并未进行转义操作。
    PHPMyWind_5.3/shoppingcart.php 留言板地址
    以ing开头(可以是其他)

  • 构造XSS payload:"><img src=x onerror=alert(2001)><"也即注入留言内容中

1.3 yiqicms-v1.9-20150904

  • 前期XSS注入点探测省略,直接对留言标题进行XSS payload提交

  • 后台查看在线留言列表,弹出1,留言详情查看也会弹出1,F12审查元素


  • 但是在利用的过程中如果是弹出cookie的话,发现有长度限制,尝试是否可以通过多次提交,使用/ /的方式绕过。

    • 第一次提交:
    • 第二次提交:
  • 查看留言列表,成功弹窗cookie信息,F12审查元素

  • 综上更多的利用方式,亦可以在一定长度下进行多次提交,巧妙利用注释

  • 审查comment.php,对输入信息仅进行了长度限制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    if(!preg_match("/^.{1,30}$/",$msgtitle))
    {
    ShowMsg("请输入正确的标题");
    exit();
    }
    if(!preg_match("/^.{1,10}$/",$msgname))
    {
    ShowMsg("请输入您的姓名");
    exit();
    }
    if(!preg_match("/^.{1,20}$/",$msgcontact))
    {
    ShowMsg("请输入正确的联系方式");
    exit();
    }
    if(!preg_match("/^.{1,200}$/",$msgcontent))
    {
    ShowMsg("请输入正确的留言内容");
    exit();
    }

    $msgcontent = safeCheck($msgcontent);

    $userip = $_SERVER["REMOTE_ADDR"];;
    $sql = "INSERT INTO yiqi_comments (cid ,title ,name,contact,content,ip,adddate)" . "VALUES (NULL, '$msgtitle', '$msgname', '$msgcontact','$msgcontent', '$userip', null)";
    $result = $yiqi_db->query(CheckSql($sql));

参考链接