淘宝跨站脚本漏洞

(此漏洞厂商已解决)

 

缘起

我在公司是做应用安全的,平时主要关注于“防”。由于工作繁忙,少有时间来尝试攻击别人的系统。但是攻防本一家,做了那么多防治安全问题的解决方案,也该试试手,把防护经验用在攻击中,看看效果如何?

第一个问题,进行什么类型的攻击?时间有限,已经是深夜了,为了节省时间,还是选跨站脚本注入吧。遍地都是XSS,哪个上规模的网站上没有XSS问题反倒怪了。第二个问题是,选什么网站?XSS漏洞分布广,攻击门槛低,为了保证有一定的挑战性,我选择了淘宝。第三个问题,攻击哪个页面的哪个字段?简单起见,就选择首页的第一个输入框,商品搜索。

好,动手。我打开IE来到淘宝。

投石问路

首先,我在商品搜索框中输入swx<>&"'。其中字串swx是为了方便在页面输出中查找注入的内容,后面的几个乱字符,则是HTML中可能会引起跨站脚本的常见敏感字符。点击搜索,结果页面出来了,没有什么语法或者显示上的错误。有一个现象,就是页面的标题,以及页面最上方的搜索框里面,搜索关键字已经变成了swx&lt;&gt;&amp;&quot;',除小于号之外的四个字符都被HTML编码了。这说明开发人员有点心虚,或者缺乏统一的编码解决方案,导致在不同的地方编码了两次,多余了,不影响安全,但是改变了用户的搜索关键字。

第一步注入没有明显结果很正常,只是扔几个怪字符过去,探探路而已。第二步要做的事情是分析HTML的源代码,看看我们给的那几个探路的字符,到了敌人那里下场如何?

蛛丝马迹

选择View Source,打开HTML源代码,然后搜索swx关键字,UltraEdit显示有71处发现,还好不算太多,一个一个看。基本上,注入的那几个特殊字符在页面的各处都被施加了HTML编码,包括head里面、HTML body里面、JavaScript字符串值里面等等。有些地方还确实被HTML编码了两次,如前面所推测。

值得注意的是,单引号则始终没有被编码,这是唯一保持原样的字符。但是因为页面的开发人员在HTML里面和JavaScript里面始终使用双引号来包住字符,单引号无法逃出双引号构成的牢笼,也就不能产生语法意义了。

其实直接输入HTML标签的跨站脚本攻击,比如<script>alert(1)</script>,成功的可能性比较小,除非目标网站太烂了。一般只要开发人员稍微考虑一下安全问题,都会把<>这样最基本的危险字符干掉。

页面里用的都是HTML编码,包括JavaScript代码里面也是。而JavaScript编码似乎被忽略了,所以我把目光移向JavaScript里面注入的那段内容:

var word = "swx&lt;&gt;&amp;&quot;'";

单引号无辜的呆在那里,似乎在等待援军。看来得尝试更多的特殊字符,目标,JavaScript

掩护

第二波,我只输入了一个反斜杠字符/。反斜杠是JavaScript里面字符串常量非常重要的一个字符,也就是编码字符。因为JavaScript字符串可以用被单引号或者双引号括起来的,并且只能是单行的。但是如果字符串的内容本身就需要包含引号或者换行符怎么办?这时就需要进行编码,比如双引号可以被编码为/x22或者/"。其中22是双引号的ASCII编码值的16进制形式。

在商品搜索栏输入/。点击搜索按钮,出问题了。结果页面中有JavaScript语法错误:Unterminated String Constant,未结束的字符串常量。

Bingo

其实这么简单的case淘宝没有测试到,实在是不应该。一个简单有效的测试做法是,往输入框里扔上一堆乱字符~`!@#$%^&#32;*()_+-={}|[]//x22:”;’<>,.?/*,如果在结果页面中,这些字符都能原样的显示在界面上,没有任何语法报错或者页面显示上的异常,那么基本上可以说跨站脚本的可能性就比较小了,包括SQL注入漏洞也顺带的被验证了。

第二次注入尝试后,HTML代码里是这样的:var word = "/";。显然,反斜杠兄弟成功打入敌人内部。后面紧跟着的双引号和反斜杠变成了一个整体的编码/",双引号被吃掉了,从而原来用以包住字符串的一对引号只剩下了前一个,所以页面报“未结束的字符串常量”错误。正因为反斜杠的特殊性,任何字符都可以被它所编码。任何字符,当然也包括大于号小于号这一对XSS攻击最基本的字符。

现在,我们试一下,利用JS的这个字符编码机制,让反斜杠掩护刚才被干掉的大于号、小于号和双引号字符潜入到word变量的值里面,一旦这三个字符可以潜入敌后,那么脚本执行也就有了希望。我们想赋给word变量的值是<>",用反斜杠编码掩护之后,变成/x3c/x3e/x22,注意,这里之所以用16进制ASCII码的编码方式,是因为/"这样的编码中双引号还是以明文出现,会被Web服务器上的编码逻辑发现并干掉。而/x22看起来就非常像良民,逃过检查的可能性比较大。

输入/x3c/x3e/x22,点击搜索,然后在结果的HTML文本里查找word =得到:

var word = "/x3c/x3e/x22";

三个特殊字符可以注进去,这意味着,现在我们已经可以在word变量的值里面任意写HTML标签代码,比如类似<script>alert(1);</script>这样的。另一个有价值的发现,就是把页面卷屏到一半的位置时,屏幕右方的显示出现混乱,本来应该是有问题就打听一下的栏目标题,变成了" target="_blank">有问题就打听一下,说明HTML语法已经被破坏。JS异常和页面显示错乱都很可能意味着XSS,因为这两个现象都说明输入的特殊字符已经起了某些语法上的作用,而不仅仅是当做字符显示或者保存在HTML中。

侦查

存在跨站脚本注入漏洞的可能性看来很大,但是为了构造有效的攻击字串,我们需要跟踪分析word变量的使用。通过View Source Code,查找跟踪word变量的值的传播和使用路径,得到以下代码,注释是我加的 (网站开发人员可不会主动在注释里面写明漏洞)

 

// 注入内容原样出现在JS代码的字符串常量中,但是在赋值给word

// JS引擎会进行解码,所以word变量的值是<>"三个字符

var word = "/x3c/x3e/x22";

//word变量的值直接拼到了moreDating后面,是个URL

var moreDating="http://dating.xxx.com/search_question.htm"+"?q="+word;

var _hebin = function(a, b){

    ...

if (a != 0) {...}

    _html += '...美容达人心得榜</a></div>';

else {

// moreDating URL直接作为<a>标签的href属性值

// 显然,这里只要moreDating变量的值包含一个小于号(已经有了!)

// 就可以强行终止<a>标签,然后后面就可以自由书写自己的脚本标签了

       _html += '<div class="Title"><a href='+moreDating+' target="_blank">有问题就打听一下</a></div>';

       

}

if(a>0||b>0){

layer.innerHTML += _html;

}

 

阅读过这段代码,基本上可以确定跨站脚本攻击的整个流程是通的。用户可以输入经过JS编码的HTML标签,然后被解码并存储到word变量,word变量又被拼接到moreDating这个URL的后面作为参数,而moreDating在满足特定条件(a等于0b大于0)时,会被拼装到HTML里面去,并输出到页面。

_hebin这个函数在什么条件下会被触发?粗略读了一下代码之后,发现逻辑不是很清晰。算了,先不浪费时间来研究这个。让事实说话吧。

发动

上一节的那段代码转了两道弯,实际上可以等价简化为下面这样:

<a href=http://.../?q=  +word+  target="_blank">。其中word变量的前后分别被拼上了一小段HTML代码,然后被输出到页面。如果我们能通过适当设置word的值,让这一行代码变成下面这样:

<a href=http://xxx/?q=><img src=javascript:alert(1)  target="_blank"> 那么JS脚本就会被执行了。大家也许发现后面那个看起来多余的target=,这其实不要紧,只是给HTML标签赋予一个无意义的属性值。上面字串的中间黑体字部分就是word变量应当具有的值,但是我们不能直接输入它,那样会被HTML编码逻辑捕捉到。把这个值用JS编码过后,就可以躲过HTML编码,得到我们需要输入的搜索关键字的值为:

/x3e/x3cimg src=javascript:alert(1) 我把这段文本拷贝出来,贴到商品搜索输入框,点击搜索按钮。等了几秒钟后,“当”的一声响,警告框弹了出来。成功了。对应上面的搜索关键字的URL是:http://search1.taobao.com/browse/0/n-g,lr4dgzk4pazwg2lnm4qhg4tdhvvgc5tbonrxe2lqoq5gc3dfoj2cqmjj-------2-------b--40--commend-0-all-0.htm?at_topsearch=1&ssid=s1-e

直接访问这个URL,也会弹出警告框。用这个漏洞可以干些什么?那就看读者的想象力和JavaScript编程的功力了。

后来经过验证,这个攻击在FireFox里没有效果,只有在IE里可以弹出对话框。具体原因我也懒得去深究了。实际攻击时也没有上面写的这么顺利,我先试<script>标签,注入了但是没执行,再试<a οnmοuseοver=,可以执行,但是onmouseover触发不好用,最后试<img src=才获得了理想的结果。另外,注入的脚本要生效还有一个条件(大概是,不完全确定),就是输入的关键字,在“打听一下”里面必须要能搜到结果,并且在“美容达人心得榜”里面必须搜不到结果,只有在这两个条件都满足时,“打听一下”的界面才会显示出来,从而注入JS才会被实际插入到页面的HTML代码里并触发执行。如果想尝试用其他的注入语法或者注入其他的JS语句,这个问题需要注意一下。

回顾

淘宝的开发人员在页面里只使用了HTML编码,并且只编码了<>&"这几个最常见的特殊字符,这是上面这个漏洞的主要原因。这似乎也是一个在Web开发安全编程中比较常见的误解。其实要全面防治跨站脚本,仅仅对这个几个特殊字符进行HTML编码是远远不够的,JavaScriptURLCSS、字符集、Web服务器、浏览器环境等等,任何一个环节的疏漏,都可能会引起跨站脚本注入问题。而需要编码的可能有危险的字符,当然也远远不止那几个。

一个广泛推荐的做法是,使用白名单进行字符编码。白名单的意思就是说,除了字母数字这些个无论何时也没有特殊语法含义的普通字符之外,其他所有的特殊字符都进行编码。假如淘宝的开发人员采用了白名单编码方法,就算他们没有意识到JavaScript需要自己特有的编码逻辑,也可以防治前面演示的攻击。因为反斜杠会被HTML编码为&#92;,导致var word = "/x3c/x3e/x22";会变成var word = "&#92;x3c&#92;x3e&#92;x22";。反斜杠在JS代码里消失了,大于号等字符被伪装,但是再也脱不下伪装,无法还原并实施攻击了。

对于那些逻辑简单的网站,白名单方式的HTML编码算是比较简单有效的跨站脚本解决方法。但是要彻底解决问题,尤其是对于功能复杂,有大量混合使用JavaScriptHTML标签、事件处理等语法元素的Web应用程序,如果不做好正确的、足够的编码,仅仅依赖于白名单方法,毕竟不是正道,总还是有缺陷的。下面就有个例子。

一点引申

<script>function showDetails(param) {}</script>

<body οnlοad="showDetails('&#39;&#41;&#59;alert&#40;&#39;1')">Tom</body>

这是一段HTML代码,粗体字部分是用户输入的一些数据,已经被进行了白名单方式的HTML编码,也就是说所有的特殊字符都被编码了。但是这里面还会存在跨站脚本问题吗?读者可以把这段代码另存到一个HTML文件里面,用浏览器执行试试看结果,想想为什么。如果有机会,再和大家多谈谈跨站脚本攻防的话题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值