DOM XSS寻踪

让我们继续寻找DOM XSS,在上一节里,在地址栏里输入的内容,很容易出现在了源代码里,然后我们发现源代码里是一个DOM 操作,通过js 的Unicode 转义,我们将利用代码植入到了innerHTML 指向的内容中,同时绕过了过滤。

关于编码的顺序问题,可以参考我之前总结的一篇文章,比较清晰。

那么,如果我们在源代码里,定位不到我们在URL 里的参数呢,其实这并没有太多不同。只是因为网页直接通过脚本,通过DOM 操作,修改了或者添加了某些标签,源码中看不到。但只需要进入调试工具里,就能找到了。

这里拉来一个老漏洞,现已修复:

1
http://qt.qq.com/video/play_video.htm?sid=aaaaaa

这样一个地址,我们跑去源代码里,是不会直接找到输出的,其实这也是更为常见的情况:

此时我们应该去到调试工具里找,在审查元素里,我们看到了输出的位置:

按照以往的方法,我们仍然是使用常识构造的方式,去看那些写法是被过滤的,然后尝试去构造攻击模式。另一种方法,则是去resources 中,去查看脚本,是那个脚本执行了什么操作,让变量进入了标签,了解清楚了之后,可以对症下药的创造攻击向量。

我们直接对sid 这个参数在resource 中搜索,会找到响应的处理函数。在这里,是一个叫getUrlPara 的函数:

进一步,定位到该函数的定义,通过分析该函数,我们能了解脚本在获得该参数后的操作,在该函数里,我们发现,该函数对 location.href 中的尖括号和引号已经进行了过滤处理,但实际上,这段代码实际上是不太正确。

因为在进行处理之前,拿到的href 已经经过了URL 编码,该函数不会对任何符号进行处理。即使是浏览器不做编码处理,如果我们预先对它进行编码处理,也会跳过函数中的过滤。然后让我们再回到函数调用之后的上下文。

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
var sid=getUrlPara("sid");
if(!sid || sid==""){
document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-10px;">抱歉,视频不存在!</div>';
}else{
var flash_ver=GetSwfVer();
if(flash_ver == -1){
document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-30px;">抱歉,您还没有安装flash插件<br/>请<a target="_blank" href="http://www.macromedia.com/go/getflashplayer">下载</a>10.0以上的flash播放器<br/>安装flash后,请<a href="javascript:location.reload();">点此刷新</a></div>';
}else if(flash_ver.split('.')[0]<10){
document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-30px;">抱歉,您的flash版本过低<br/>请<a target="_blank" href="http://www.macromedia.com/go/getflashplayer">下载</a>10.0以上的flash播放器<br/>安装flash后,请<a href="javascript:location.reload();">点此刷新</a></div>';
}else{
sid=decodeURIComponent(sid).trim().replace(/([\'\"])/g,'\\\\$1');
if(!is_valid_sid(sid)){
document.getElementById("dv_video").innerHTML='<div class="errmsg" style="margin-top:-10px;">无法打开视频文件,视频地址不合法!</div>';
}else{
insertFlash("dv_video","f",sid,"100%","100%");
}
}
}

这里,通过decodeURLComponent 将编码后的参数,又解码成了原符号,而后边调用的insertFlash 操作,未经过滤的将sid 写进了页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function insertFlash(elm, eleid, url, w, h) {
if (!document.getElementById(elm)) return;
var str = '';
str += '<object width="' + w + '" height="' + h + '" id="' + eleid + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0">';
str += '<param name="movie" value="' + url + '" />';
str += '<param name="allowScriptAccess" value="never" />';
str += '<param name="allowFullscreen" value="true" />';
str += '<param name="wmode" value="transparent" />';
str += '<param name="quality" value="autohigh" />';
str += '<embed width="' + w + '" height="' + h + '" name="' + eleid + '" src="' + url + '" quality="autohigh" swLiveConnect="always" wmode="transparent" allowScriptAccess="never" allowFullscreen="true" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>';
str += '</object>';
document.getElementById(elm).innerHTML = str
}

我们很容易的想象到了构造< img src=# onerror=alert(1)>,对尖括号进行URL 编码就可以了。实际上,就这一个漏洞,我们不仅可以使用URL 编码的方式。结合我之前说的浏览器的解析顺序,在这里,从URL 获得的参数,进入脚本,脚本调用DOM 操作,修改DOM 树,所以我们用Unicode编码也能最后得到解析。

我们始终说,安全编码是一个不容易的技术,因为一步疏漏就会在最终造成满盘皆输。所以,对于开发者,想要真正构建安全的程序,就必须对程序所涉及的技术框架了如指掌。比如对于Web,应该对浏览器的原理,HTTP/HTTPS,各种编码原理,JS&CSS&HTML,PHP&ASP,的安全部分都有一定了解,才能在构建程序的时候,抓住最关键的部分,确保不出问题。

script>