Discuz存储型XSS(1)

之前提交到某平台的漏洞,现在已经修复

漏洞证明

  1. 漏洞需要开启后台四方格功能
  2. 发表新帖子,在帖子标题中设置为payload
    <img src=1 onerror=alert(1)>
  3. 然后在首页鼠标放在帖子准备点击我们刚才的发表的帖子时,触发onmouseover事件,执行我们的代码。

    漏洞分析

    鼠标放在帖子上准备点击时,触发onmouseover事件。onmouseover事件中调用的showTip函数最终调用到了_showTip函数

_showTip函数中使用getAtrribute取了tip属性值,然后又放入innerHTML。
重点是getAtrribute函数获取属性值时会自动解码实体编码后的值,这样&#x003c;img src=1 onerror=alert(1)&#x003e;就变成<img src=1 onerror=alert(1)>

另外此漏洞中的payload只能用
&#x003c;img src=1 onerror=alert(1)&#x003e;
而不能用
&#x3c;img src=1 onerror=alert(1)&#x3e;
这是由于Discuz中dhtmlspecialchars函数实现问题。

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
function dhtmlspecialchars($string, $flags = null) {
if(is_array($string)) {
foreach($string as $key => $val) {
$string[$key] = dhtmlspecialchars($val, $flags);
}
} else {
if($flags === null) {
$string = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $string);
if(strpos($string, '&amp;#') !== false) {
$string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
} //在这一行又将 &amp; 解码成 &,导致htmlspecailchars和php中的htmlspecailchars函数差异
} else {
if(PHP_VERSION < '5.4.0') {
$string = htmlspecialchars($string, $flags);
} else {
if(strtolower(CHARSET) == 'utf-8') {
$charset = 'UTF-8';
} else {
$charset = 'ISO-8859-1';
}
$string = htmlspecialchars($string, $flags, $charset);
}
}
}
return $string;
}

这个差异可以这么解释

1
2
htmlspecialchars(htmlspecialchars("&#x003c;")); 值是"&amp;amp;#x003c;"
dhtmlspecialchars(dhtmlspecialchars("&#x003c;")); 值是"&#x003c;"

而上面tip属性值就是两次dhtmlspecialchars了标题的值,输入存到数据库一次,输出时一次。

1
tip=dhtmlspecialchars(dhtmlspecialchars(标题));

漏洞总结

  1. dhtmlspecialchars函数和实际的htmlspecialchars函数有差异
  2. getAttribute函数会将属性值解码