My First Bounty $3,000 (P1 Critical) - An IDOR that Broke the entire Access Control of an Application's Infrastructure
Intro: Before this story begins…
Starting with this write-up, I’d like to write a few words about this finding and its history.
Around August 2023, I met a person who is currently a big friend and who introduced me to the world of the “Bug Bounty Hunting”. I started learning and improving my web pentesting skills. However, it’s important to mention that bug hunting of course is not limited exclusively to web apps.
Anyway, unfortunately at the beginning of my bug bounty hunting, I wasted a lot of time using automated tools in the terminal. I easily could say that at least six months I only used automated tools without a deep understanding of the application.
This is important for everyone who wants to start with bug bounty hunting: stop automating everything; that is not “real hacking”. Here comes the truth, or at least from my perspective: when you spend time looking for bugs and reporting them, it’s common to encounter many Duplicates, Not Applicable, or Informational submissions at the beginning.
I believe this happens because real bug bounty hunting involves research and finding ways to break the logic using your skills and mindset. An automated tool like “nuclei” can be useful - let me be clear: I am not saying that nuclei doesn’t work; I actually use it and find it very useful. But when the entire process relies on just one single tool (or a big automated workflow), bug bounty hunting cannot succeed.
The “solution”? The “secret”? Well, I just found my first bounty (not my first bug), so I can honestly say that finding bugs and getting rewards in bounties is simply about opening Burp Suite and Firefox/Chrome - or your Android/iOS phone (if you’re in mobile area) - and starting to analyze the entire workflow of the application:
- Dedicate at least one or two weeks to analyze each request from Burp Suite.
- When you have a full understanding of the application, you’ll be able to hack it.
“To hack something, you first need to understand how it works.”
In closing this intro, remember to never give up. Sometimes, although you may not believe me, the bug is right in front of you. ❤️
So, How I found This Bug?
Brief Introduction and Disclaimer
This article serves as an illustrative write-up of my bug bounty experience. I want to clarify that I am not disclosing any sensitive information, proprietary data, or confidential knowledge related to the application or organization involved. This “write-up” redacts all parts that are confidential.
For example:
- For a domain like
google.com
, I useexample.com
. - For a parameter like
user_session
, I useUserID
. - Any other sensitive details have been modified or omitted to ensure compliance with ethical standards and legal requirements.
The purpose of this write-up is to share insights and learnings from my experience in a responsible manner.
Technical step by step
Finding the bug 🐞
I started exploring the application and minimizing the requests. In example:
1
2
3
4
5
6
7
8
9
10
11
GET /v2/profile HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Cookie: google_analytics=4k123kjaSADLa-//==22d; session_time=6275912394.36221.2; tracking_id=ZvJ210xdf==; user_pref_language=en-US; ad_tracking=enabled; last_visited_page=/dashboard/preferences;
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Connection: keep-alive
Accept-Language: en-US,en;q=0.9
Cache-Control: no-cache
Pragma: no-cache
If-None-Match: "etag_value"
- Now minimized:
1
2
3
4
5
GET /v2/profile HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Then was when I noticed an endpoint (Endpoint #1) which in the response showed some sensitive data of the user, with a low impact anyway.
- The request was like:
1
2
3
4
5
6
7
8
9
10
POST /v2/users/search HTTP/2
Host: example.com
Accept: application/json
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
UserID: 12345
Authentication_Token: abc123==
Content-Length: 13
{"foo":"bar"}
I tried so removing the Authentication_Token
, and the response returned a HTTP/2 200 OK. There was a lack of back-end validation:
There was no access control to deny me from retrieving my own data even without my
Authentication_Token
.
So I quickly sent the request to the Burp Suite’s Intruder and started fuzzing the UserID
. Fortunately, after two hours I started to get 200 status codes. Note: The requests for UserID
which don’t exists returned me 400 status codes.
Then, I sent the request replacing my own UserID
with different victim’s UserID
obtained from the Intruder; and without the Authentication_Token
. Finally I could see low-impact sensitive data about the another users.
1
2
3
4
5
6
7
8
9
POST /v2/users/search HTTP/2
Host: example.com
Accept: application/json
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
UserID: 2412
Content-Length: 13
{"foo":"bar"}
Reporting 📑
Take this advice as personal experience: remain calm at all times when writing your report. Write a complete and professional submission. Then, once finished, read your report as if you were a triager and ask yourself whether the way you have written it could be considered a valid submission. If not, revise it, emphasizing the real impact and removing unnecessary details.
Many times, a submission can vary in severity depending on how the impact was written by the bug hunter.
Escalating Privileges 🔐
During the wait for the triager response, I re-analyzed my submission and observed in Burp Suite another endpoint which returned a valid Authentication_Token
with its UserID
. (Endpoint #2)
This response required a request without any authentication, so I could obtain many valid responses as I want, each one with their valid Authentication_Token
and UserID
. (This endpoint served only to create new accounts, so there was no sensitive-data in there).
There was also another endpoint (Endpoint #3) but this one actually required a valid Authentication_Token
to retrieve sensitive data in the response.
However, I put a valid Authentication_Token
obtained from the another endpoint’s response (Endpoint #2) in this request (Endpoint #3) as well as a victim’s UserID
, and… what do you think? Yeah! IT WORKED!
In a few words, the server was validating only that the Authentication_Token
was valid and present in the request, but not validating if matches with the proper UserID
. So I as an attacker could use any Authentication_Token
obtained from the Endpoint #2 and any victim’s UserID
obtained from the Intruder in the Endpoint #1 to then abuse the Endpoint #3 with:
Any valid
Authentication_Token
(Endpoint #2) and any victim’sUserID
(Endpoint #1).
This returned me sensitive information (PII) (Endpoint #3).
- Example request:
1
2
3
4
5
6
GET /v4/profile/details HTTP/2
Host: example.com
Accept: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
UserID: 2412
Authentication_Token: Ji2h5==
UserID: 2412
→ Obtained from Burp Suite’s Intruder (Endpoint #1).Authentication_Token: Ji2h5==
→ Any valid one, obtained from Endpoint #2.
The impact here was critical because this bug could likely be reproduced across almost all accounts (given attackers enough time).
So I came back to the submission comments and wrote a detailed report within the comment, continuing from my original submission.
After both triager and company analyzed this new finding, they validated it.
The WIN: $3,000 USD in reward 💰
Finally, although the triager had marked my submission as P3 (Medium), the company decided to mark it as P1 (Critical) and rewarding me with a $3,000 USD… That was surprising for me!
How this works - Mind Map: Attack Scenario 🎯
Attacker → Uses victim's `UserID` **obtained from fuzzing** → Uses **any valid** `Authentication_Token` → Results in: → Server responds with PII (Personally Identifiable Information)
- Graphic:
Attacker | +-------+-------+ | | `UserID` `Authentication_Token` | v **Responds with PII**
The Triaging ⚖️
The triaging took a month to finish, but I am really happy and grateful for the bounty I won.
Although some submissions may take only a few days or a week at most, in my case, due to the complexity of the bug and the response times, it took exactly a month and a week.
“Patience leads to victory” 🌱
“Everything that is well planned succeeds” 🏆
If you liked this write-up …
⭐ Don’t forget to share with your friends or community. This helps me a lot!
Happy Hunting! ;)