Bugcrowd Student Finale CTF 2025 Writeup: Predictable

In this chal, I was given a compromising an admin account on a website. I had to figure out how password reset tokens were generated and create a valid one to hijack the account. The hint suggested that the tokens were generated using predictable patterns based on timestamps and email addresses.

Bugcrowd Student Finale CTF 2025 Writeup:  Predictable

1. Reconnaissance: Finding the Flaw

I started by analyzing the source code to understand the application's logic. My search led me to a file named ResetToken.js, which was responsible for generating the recovery links.

Inside, I found the "smoking gun." The code for generating tokens looked like this:

const seed = email + currentTime.toString();
const token = crypto.createHash('md5').update(seed).digest('hex');

The Vulnerability

This implementation is critically flawed. Here is exactly what it does:

  1. It takes the user’s email address (e.g., admin@hackthebox.com).
  2. It appends the current system time in milliseconds (e.g., 1763942850430).
  3. It concatenates them and creates an MD5 hash.

The problem? Both variables are predictable.

  • The Email: This is public knowledge (admin@hackthebox.com).
  • The Time: I can estimate roughly when the token was created by tracking when I sent the request.

Because the "randomness" relies entirely on the clock, this isn't random at all—it’s just a math problem waiting to be solved.

2. The Attack Plan

To exploit this, I devised a five-step strategy:

  1. Trigger a Reset: Request a password reset for the admin and record the exact time.
  2. Define a Time Window: Calculate a range of milliseconds around the request time.
  3. Offline Generation: Generate fake tokens locally for every millisecond in that window using the formula MD5(email + timestamp).
  4. Brute Force: Send these tokens to the server until one is accepted.
  5. Account Takeover: Reset the password, log in, and grab the flag.

3. Building the Exploit Script

I wrote a JavaScript tool to automate this process. Since milliseconds move fast, I needed a script that was both accurate and efficient.

Step 1: Synchronization

When my script sends the reset request, it records the timestamp immediately before sending and immediately after receiving the response. This gives me a specific window where the server must have generated the token. I used the middle of this range as my starting point to account for network latency.

Step 2: The Search Algorithm

Trying every single millisecond blindly can be slow. I implemented a heuristic approach to find the correct timestamp:

  • Coarse Search: First, jump by 100ms increments to find the general area.
  • Fine Search: Narrow it down to 10ms jumps.
  • Precision Search: Finally, try every single 1ms to find the exact match.

My script also sent these requests in batches (concurrency) to speed up the feedback loop.

Step 3: Validation

For every timestamp generated, the script computed: MD5("admin@hackthebox.com" + timestamp)

It then fired the token at the verification endpoint. If the server responded with a success message, the loop broke, and the token was saved.

4. Execution: Running the Exploit

When I ran the script, here is how the attack played out:

  1. It triggered the reset and estimated the token was created around timestamp 1763942850430.
  2. It began the coarse search (100ms jumps), taking about 101 tries.
  3. It switched to the fine search (10ms jumps), taking about 1017 tries.
  4. Success! It found the valid token matching timestamp 1763942850430.

With the valid token in hand, the script automatically reset the admin password to hacked123.

I logged into the dashboard using the new credentials, and there it was.

The Flag was HTB{pr3d1ct4bl3_s33ds_4r3_s0000_pr3d1ct4bl3!_e3ee706eff39406fc98ae0ef1c84db84}