Summary
During testing of a food delivery application, I found an Insecure Direct Object Reference (IDOR) in the login API that leaked user details. Combined with a flaw in the OTP-based password reset flow, this led to full account takeover of any user.
The IDOR
The login endpoint returned more data than necessary in its response. A POST to /api/v1/login with valid credentials returned:
{
"status": "success",
"user": {
"id": 14832,
"email": "[email protected]",
"phone": "+1-555-0142",
"name": "John Doe",
"address": "123 Main St",
"role": "customer"
}
}
The problem: the same response structure was returned when querying other user IDs via /api/v1/users/<id>. No authorization check. Incrementing the id parameter returned full profile data for any user in the system.
This alone is a significant data leak. But it gets worse.
The OTP Flaw
The application used OTP (one-time password) sent via SMS for password resets. The flow:
- User enters phone number on the forgot-password page
- Server sends a 4-digit OTP via SMS
- User enters the OTP
- Server validates and allows password reset
Two problems:
- No rate limiting on OTP verification. The endpoint accepted unlimited attempts.
- 4-digit OTP space is only 10,000 combinations. Brute-forceable in seconds.
The Chain
- Use the IDOR to get the target user’s phone number from
/api/v1/users/<id> - Trigger a password reset for that phone number
- Brute-force the 4-digit OTP (10,000 requests, no rate limiting)
- Reset the password
- Full account access
Total time from IDOR to account takeover: under 2 minutes with a simple script.
Impact
- Any user account could be taken over by any authenticated user
- Personal data (email, phone, address) of all users exposed via IDOR
- No user interaction required
Takeaways
- API responses should return only the data the requesting user is authorized to see.
- OTP brute-force protection is mandatory: rate limiting, account lockout after N failed attempts, and OTP length should be at least 6 digits.
- Authorization checks on every endpoint, not just the ones that “look sensitive.”
Timeline
| Date | Event |
|---|---|
| 2018-08-10 | Vulnerability discovered |
| 2018-08-10 | Reported to vendor |
| 2018-08-14 | Vendor acknowledged |
| 2018-08-20 | Fix deployed (rate limiting + authorization check) |