I was testing a fintech app's "View Profile" feature. The mobile app showed my name and avatar. Burp Suite showed something else entirely:
{
"name": "John Doe",
"avatar": "/images/avatar.jpg",
"email": "john@example.com",
"phone": "+1234567890",
"password_hash": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
"reset_token": "a7b3f9e2c4d1g6h8j9k0l",
"internal_risk_score": 42,
"admin_notes": "Flagged for unusual activity",
"last_login_ip": "192.168.1.100"
}
The app only showed name and avatar. The API returned everything about me - including my password hash and an active reset token.
That was my first Excessive Data Exposure bounty. $1,500 for reading my own data.
Let me show you how to find these everywhere.
What Even Is Excessive Data Exposure?
Simple definition: The API gives you more data than you need to see.
Technical definition: The backend queries SELECT * FROM users and sends the entire database row to the frontend, even though the UI only displays 2 of the 15 columns.
The scary part? You don't need broken authentication. You don't need SQL injection. You just need to look at the raw API response.
The 9 Endpoints Where I Always Find This
1. User Profile Endpoints
GET /api/users/{id} or GET /api/users/me
What to look for:
-
email,phone,address -
password_hash,salt -
reset_token,verify_token -
two_fa_secret,backup_codes -
api_keys,session_tokens
Bug bounty example: I once found stripe_customer_id and subscription_billing_cycle on a /me endpoint. The company paid $500 because they considered it internal architecture exposure.
2. Search Endpoints
GET /api/search?q=john
What to look for:
- Full emails instead of truncated
j***@example.com - Exact timestamps of last login
- Internal user IDs used for other API calls
Pro tip: Even if you can't IDOR to other users, search your own email. The API might return extra fields on your own result that shouldn't be visible to you.
3. Registration / Signup Responses
POST /api/register
What to look for:
- Your password echoed back in plaintext (yes, this happens)
- Your session token in the response body
- Internal account flags like
"is_verified": false,"requires_review": true
4. Order History
GET /api/orders/12345
What to look for:
- Credit card last4, expiry, billing zip
- Full shipping address
- Internal cost vs retail price (business logic exposure)
-
refund_eligibilityflags
5. File Upload Metadata
POST /api/upload → returns file_id → GET /api/files/{file_id}
What to look for:
- Internal S3 bucket paths (exposes infrastructure)
- Original filenames (could contain PII like
resume_ssn_123.pdf) - Uploader's IP address and user agent
-
file_permissionsarray showing who can access
6. GraphQL Endpoints (The Jackpot)
POST /graphql
What to do: Request fields you shouldn't see even on your own account:
query {
me {
name
email
password_hash # ← try it
reset_token # ← try it
internal_notes # ← try it
admin_flags # ← try it
}
}
Real bounty: A bug hunter found credit_card and ssn fields exposed on the /me query of a major credit bureau. $7,500 bounty.
7. Analytics & Dashboard Endpoints
GET /api/dashboard/stats
What to look for:
- Other users' emails or IPs in event logs
- Database connection strings
- Debug arrays containing full request/response objects
8. Export / Download Endpoints
GET /api/export/users.csv
What to look for: If you can access this endpoint at all (auth bypass or misconfigured role), the CSV often contains:
- Full database columns including sensitive ones
- Password reset hashes
- 2FA secrets (mangled or plain)
9. PATCH / PUT Responses
PATCH /api/users/me (with empty body {})
Why this works: When you send an update with no changes, some APIs still return the full updated object - which might contain fields you never had permission to read.
My 4-Step Testing Methodology
Step 1: Intercept Everything
Stop trusting what you see in the browser/mobile app. Forward every request through Burp Suite, Caido, or ZAP.
The raw response always tells the truth.
Step 2: Build Your Keyword Radar
I search responses for these strings (I keep this as a grep pattern):
email|e-?mail|phone|mobile|telephone|address|location|
ssn|tax|tin|passport|license|driver|
birthday|dob|age|
password|pass|pwd|secret|token|jwt|api[_-]?key|apikey|
hash|salt|reset[_-]?token|verify[_-]?token|
credit[_-]?card|cc|cvv|expiry|stripe|paypal|
ip[_-]?address|user[_-]?agent|device[_-]?id|
internal[_-]?note|admin[_-]?note|flag|reason[_-]?code|
otp|mfa|two[_-]?fa|backup[_-]?code
Step 3: Compare Responses Across IDs
If you can change ?user_id=1 to ?user_id=2, do it. But even without IDOR, compare:
- Authenticated vs unauthenticated
- Your low-privilege account vs a different low-privilege account
- Verbose param (
?verbose=true) vs normal
Tool tip: Use Burp's Comparer or the Diffy extension to spot extra JSON keys.
Step 4: Trigger Verbose Modes
Add these parameters to every request:
?verbose=true
?debug=1
?include_fields=all
?format=full
?fields=*
?pretty=true
?show_internal=true

Top comments (0)