Test API endpoints that accept request bodies for Mass Assignment - the flaw where the framework binds client-supplied properties to
backend data models without an allowlist, letting an attacker inject
admin flags, role escalations, or financial-state fields that the UI
never exposes. Also covers HTTP Parameter Pollution (HPP) - duplicate
parameters that bypass validation. This skill implements WSTG-INPV-04
adjacencies and maps findings to CWE-915 (Improperly Controlled
Modification of Dynamically-Determined Object Attributes) and CWE-235
(Improper Handling of Extra Parameters). The goal is to hand the API
team a concrete list of unprotected write paths with before/after
object-state evidence and DTO / allowlist remediation.
-
Inventory write endpoints [Hacking APIs, Ch 11, p. 273]
Do: From API_INVENTORY.md, extract every POST / PUT / PATCH
endpoint. Focus on:
- User creation / profile update
- Account-tier changes
- Order / transaction creation
- Permission / role assignment
- Configuration endpoints
Record: .claude/planning/{issue}/mass-assignment-targets.md
with (endpoint, method, documented fields from spec).
-
GET object, inspect response for hidden properties
[Hacking APIs, Ch 11, p. 274]
Do: For each write endpoint, GET the object it manages.
Compare the response fields against what the UI's create/update
form sends. Fields in the response but not in the UI's write
request are candidates for blind-MA injection.
Common high-value hidden fields:
isAdmin, admin, is_admin, role, roles,
permissions, is_superuser
verifiedAt, emailVerified, phoneVerified, kycStatus
balance, credit, wallet, accountLimit
createdBy, tenantId, organizationId
apiKey, secret, internalNotes
mfaEnabled, mfaRequired - can DISABLE MFA via MA
Record: Per-endpoint list of candidate injection fields.
-
Admin-flag injection [Hacking APIs, Ch 11, p. 276]
Do: Trigger a standard profile-update request as {user_a}.
Before sending, add these fields to the JSON body:
{
"...existing fields...",
"isAdmin": true,
"is_admin": 1,
"admin": true,
"role": "admin",
"roles": ["admin"]
}
Submit. Then GET the same object to check:
- Did the extra fields persist in the stored record?
- Does a protected /admin endpoint now return 200 for this
session?
Vulnerable response: The injected fields stuck AND access
expanded.
Not-vulnerable response: Fields silently dropped, or explicit
400 error listing them as unknown.
Record: FINDING-NNN Critical if access actually escalated.
-
Verification-flag injection
[OWASP API Security Top 10, API6:2019]
Do: Submit a profile-update with:
{
"emailVerified": true,
"phoneVerified": true,
"kycStatus": "approved",
"mfaEnabled": false
}
Vulnerable response: Stored and honored - a new account can
skip verification / MFA.
Record: Per-flag findings.
-
Financial-state injection
[Hacking APIs, Ch 11, p. 272, 275]
Do: Submit an update with financial fields:
{"credit": 9999, "balance": 100000, "creditLimit": 500000}
Vulnerable response: Balance / credit updated without a real
transaction.
Record: FINDING-NNN Critical - direct monetary impact.
-
Ownership re-assignment [Hacking APIs, Ch 11]
Do: Submit an update with cross-tenant fields:
{"organizationId": "{victim-org-id}", "tenantId": "...",
"ownerId": "{admin-user-id}"}
Vulnerable response: Object reassigned - can pivot into another
tenant or impersonate ownership.
Record: Severity High; cross-reference idor-hunter if the
destination organization's data becomes accessible.
-
Duplicate query parameters
[WSTG v4.2, WSTG-INPV-04]
Do: For endpoints accepting query-string parameters, submit
duplicates:
?user_id=attacker&user_id=victim
?amount=10&amount=100
?role=user&role=admin
Observe which value is honored - first, last, or concatenated.
Vulnerable response: Validation checks user_id=attacker (first
occurrence) but the DB query uses user_id=victim (last) - or
vice versa.
Record: Per-endpoint parsing-order fingerprint.
-
Duplicate body parameters [WSTG v4.2, WSTG-INPV-04]
Do: For form-encoded bodies, submit duplicates:
role=user&role=admin
For JSON bodies, test invalid-but-sometimes-accepted duplicate
keys:
{"role": "user", "role": "admin"}
Many JSON parsers silently use the last value; inconsistency
between the validation parser and the storage parser creates a
pollution path.
Record: Findings where the validation parser sees one value and
the storage layer sees another.
-
POST / PUT on GET endpoints [Hacking APIs, Ch 11, p. 280]
Do: For endpoints documented as GET /resource/:id, try
POST /resource/:id or PUT /resource/:id with a body
containing sensitive fields.
Vulnerable response: 200 - and the body fields were applied
(check via a subsequent GET).
Not-vulnerable response: 405 Method Not Allowed.
Record: Successful method-swap-with-MA findings; typically High.
-
Method-swap + HPP combo [Hacking APIs, Ch 11]
Do: Combine methods: POST /resource with body containing
admin=true AND query string ?admin=false. The validator
might check the query string but the binder uses the body.
Record: Each combination's outcome.
-
Echo-without-execution: The response body reflects the
injected field, but a subsequent GET shows it wasn't stored.
Always confirm with GET-back; don't trust response echo.
-
Public-intended field misread: public_bio, public_display_name,
tagline look sensitive but are designed to be user-settable.
Cross-reference documentation / UI before filing.
-
Silent field drop with 200: The framework accepts the
injection and returns 200, but internally stripped the extra
fields before storing. This is actually the correct defense - don't file as a finding.
-
Idempotent-update false negative: The second GET-back shows
the same value as before the injection because nothing changed
-
BUT the injected field wasn't in the original object either.
Distinguish by observing whether the field EXISTS in the GET
response after the injection that didn't exist before.
-
Ownership-transfer with cleanup: Some apps accept
organizationId changes because they're legitimate for the
admin-panel workflow; the check is at the CALLER level, not at
the parameter level. If a regular user can use the endpoint
without admin role, the caller-check is what fails - cross-
reference bola-bfla-hunter.
-
MFA-disable through account update: Flipping mfaEnabled
through profile update is severe - attackers leverage it after a
session hijack to prevent the legitimate user from regaining
control. Always flag this specific pattern as Critical even if
the finding "only" disables MFA.