Summary

During a recent engagement on a food industry B2B platform, I discovered a vulnerability chain that allowed me to dump the entire database of users registered for a corporate event. By chaining a sequential ID enumeration vulnerability with a secondary IDOR on the "Edit Profile" endpoint - and optimizing the attack by identifying a weakness in the ID generation logic - I was able to access the Personally Identifiable Information (PII) of all event attendees.

The Target

The target was a web application allowing store owners and food companies to register for an upcoming trade event. The registration form collected sensitive PII, including:

  • Full Name
  • Email Address
  • Mobile Number
  • Job Title
  • Company/Store Name
  • Postal Code & Address

The Vulnerability Chain

1. Reconnaissance & Entropy Analysis

After registering for the event, I received a confirmation email containing two links:

  1. Download Badge: A URL to download a PDF badge containing a QR code, Name, and Job Title.
  2. Edit Information: A URL to update registration details.

I analyzed the Badge URL structure: https://event.target.com/badge/download?id=XX110993

  • Prefix: Two uppercase letters (e.g., TE, TG).
  • Sequence: A 6-digit sequential number.

Since I was the most recent registrant with ID 110993 (and IDs started at 110000), I knew there were exactly 993 registered users in the event. The sequential number was trivial to guess, leaving only the 2-letter prefix.

I assumed the 2-letter prefix was fully random ( 26 * 26 = 676 combinations).

2. The Hurdle: Cloudflare WAF & JS Obfuscation

I attempted to use a standard Python script to fuzz the optimized list. However, the application was protected by Cloudflare. Any request from a standard HTTP client returned a generic HTML page asking the client to "Enable JavaScript."

Because my script couldn't execute JS, it couldn't retrieve the actual page source to determine if a guessed Badge ID was valid (HTTP 200) or invalid (HTTP 404).

3. The Bypass: Selenium + CDP + Auto-Solving CAPTCHA

To bypass this, I utilized Selenium with webdriver_manager to spawn a real browser instance, which naturally executes JavaScript and satisfies Cloudflare's check.

However, a standard Selenium .page_source call still occasionally returned the "Enable JavaScript" loading state rather than the rendered content. To fix this, I utilized the Chrome DevTools Protocol (CDP). By commanding the browser to retrieve the DOM after execution, I could bypass the obfuscation and validate which IDs were real.

The CAPTCHA Bypass: During the fuzzing process, Cloudflare occasionally flagged the traffic and presented a CAPTCHA challenge. I modified the script to detect this page and simply sleep for 4 seconds.

Because I was using a full browser environment (Selenium) rather than a simple HTTP client, the Cloudflare challenge (likely a "Managed Challenge" or "Turnstile") was able to verify the browser environment during this 4-second pause. It automatically "solved" itself without human interaction, redirecting me back to the content, allowing the script to resume enumeration seamlessly.

The Optimization:

after manually checking about 50 enumerated badges, I noticed a flaw in the randomness. While the first letter was random (A-Z), the second letter only cycled through a specific pool of 8 repeating characters.

This observation allowed me to optimize my script, reducing the search space from 676 to just 208 requests per ID ( 26 * 8 ), significantly speeding up the enumeration process by 70%.

  • Result: Using the optimized 208-request loop, I successfully enumerated the valid badge IDs for all users.

4. Escalation: From Medium to High Severity

While downloading badges (Badge QR Code + Name + Job Title) is a valid finding, it is often considered Medium severity. I wanted to demonstrate critical impact.

I returned to the "Edit Information" link found in the email. It used the same ID structure but required a 4-digit PIN for access:

https://event.target.com/user/edit?id=TE110993&pin=XXXX

Since I had already harvested the valid IDs (e.g., TE110993) from the first step, the only barrier left was the 4-digit PIN (10,000 combinations).

I updated my Selenium script to:

  1. Feed in the valid IDs found in Step 3.
  2. Brute-force the PIN (0000–9999) for each ID.
  3. Use the CDP method to bypass the Cloudflare/Captcha checks.

The Impact

The script successfully brute-forced the PINs. Upon a successful guess, the application loaded the "Edit Profile" page, exposing the Full User Database:

  • Full Name, Email, Mobile Number
  • Home/Business Address, Postal Code
  • Company Details

This effectively bypassed the authentication mechanism, turning a simple ID enumeration into a mass PII data breach.

None
Bounty awarded after 1 day of report submit

Lessons Learned

  1. Escalate via Chaining: Instead of reporting the initial low-hanging fruit (Badge download), I dug deeper for the PII endpoint (Edit Profile). This chained exploit turned a Medium severity bug into a High/Critical severity.
  2. Work Smarter, Not Harder: Manually analyzing the first 50 results revealed a pattern in the "random" IDs, reducing the brute-force search space by 70% (208 requests vs 676).
  3. Client-Side / Server-Side Security: Cloudflare's JS and CAPTCHA checks verify browsers, not authorization. By using Selenium and a simple sleep timer, the "security" validated my bot as a legitimate user.
  4. Predictability is Fatal: Hiding sequential IDs behind a weak 2-letter prefix is "security by obscurity." Once the prefix pattern was broken, the entire database was exposed.