Post

GIFT LAB - Bugforge Daily Challenge

A predictable admin access token writeup from BugForge Labs. The token looked random at first glance, but comparing it across two accounts revealed a fixed prefix and a brute-forceable 3-character suffix - this is the full process from spotting the cookie to getting the flag.

GIFT LAB - Bugforge Daily Challenge

I do BugForge labs on a daily basis and keep all of them as writeups in my GitHub. This is another one I’m bringing over here, since it has some good technical depth worth sharing - you can read it below.

This one’s a CTF (Daily Challenge) from BugForge Labs called GIFT LAB. Nothing about it screamed “vulnerable” at first glance - just a normal login page with one odd line of text. But that one line was enough to pull on, and it unraveled into a fully brute-forceable admin token. Here’s the full process I followed to find it, confirm it, and get the flag.

Platform: BugForge Labs Category: Web Application Exploitation Vulnerability: Predictable Token - Brute-Forceable Admin Access Token

The Big Picture

Here’s the attack chain in simple terms:

1
2
3
4
Notice "Administrator? Login here" option → Register & login as normal user
→ Spot unusual adminAccessToken cookie → Try it on admin login (fails)
→ Register a second account, compare tokens → Notice only last 3 characters differ
→ Brute-force the last 3 characters → Get the real admin token → Get the flag

Step 1 - Opening the Lab

The lab starts with a normal register/login page, but one thing stood out immediately: the login page had a line that said “Administrator? Login here.”

That’s not something you see every day, so I kept it in mind for later and registered a normal test account first:

1
2
3
username         : test
password         : test
confirm_password : test

After registering and logging in, I landed on the dashboard. Checking my cookies, I noticed something unusual alongside the normal JWT token:

1
adminAccessToken=n0MqjBXna9A4svu

Admin Access Token Cookie A cookie literally named adminAccessToken, sitting right in a normal user’s session

A cookie literally named adminAccessToken sitting in a normal user’s session is a strong signal that something is worth investigating.

Step 3 - Trying the Token on the Admin Login

Remembering the “Administrator? Login here” option from Step 1, I went back and clicked it.

Administrator Login Page The admin login asks for a code

It asked for a code. I tried submitting my own adminAccessToken value (n0MqjBXna9A4svu) as that code.

Result: “Wrong code. Access denied.”

So this exact token isn’t the admin code itself - but it’s clearly related to the system somehow, since it has the same name as what’s being asked for.

Step 4 - Looking for IDOR in Lists

Back in my test account, I explored more of the dashboard. I created a list called first_list.

Creating a List Creating a test list on the dashboard

Each list has view, share, and delete options. The share feature uses a numeric ID in the URL, like /1/share - numeric IDs are always worth testing for IDOR.

Share List Feature The share feature, using a numeric list ID in the URL

I changed the ID to 2 and tried /2/share. This just redirected me to login, since list 2 isn’t mine. No IDOR here - dead end.

Step 5 - Comparing Tokens Across Two Accounts

The adminAccessToken cookie was still bugging me. I decided to test something: if I register a second account, do I get the same token, a completely different one, or something in between?

I registered test2 and logged in, then compared the tokens:

1
2
test  : adminAccessToken=n0MqjBXna9A4svu
test2 : adminAccessToken=n0MqjBXna9A4uof

This was the key discovery. The first part of the token - n0MqjBXna9A4 - is identical for both accounts. Only the last 3 characters differ (svu vs uof).

That strongly suggests the token isn’t random per session - it’s a fixed prefix plus a small, guessable suffix. If the admin’s code follows the same pattern, the only unknown part is those last 3 characters.

Step 6 - Building a Wordlist for the Last 3 Characters

Since the suffix looked like 3 lowercase letters, I generated every possible 3-letter combination using a simple Linux one-liner:

1
printf '%s\n' {a..z}{a..z}{a..z} > lastthreedigits.txt

Generated Wordlist Every combination from aaa to zzz, generated in one line

This creates every combination from aaa to zzz - far more manageable than trying to guess it manually.

Step 7 - Brute-Forcing the Admin Endpoint

Visiting /administrator directly with the wrong token gives a clear error message:

1
You don't have the correct adminAccessToken for this area.

Administrator Endpoint Error The error message that helps tell a wrong token apart from a correct one

This error message is useful - it means I can tell apart a correct vs incorrect token just by comparing responses (length, status, or message).

I used Caido to automate the brute force, since the free tier has no rate limiting on this kind of request. (This could also be done with ffuf, Burp Suite’s Intruder - though that needs a Pro license to brute-force efficiently - or a custom Python script.)

I set up the attack to substitute each entry from my wordlist into the last 3 characters of adminAccessToken, keeping the rest of the cookie (n0MqjBXna9A4) fixed.

Brute-Force Setup in Caido Setting up the brute-force attack in Caido

After about 15 minutes of brute-forcing, one request (ID 11797) stood out with a different response length than all the others. Checking that specific response confirmed it - the response body actually contained the flag, proving that request had used the correct last 3 characters.

Caido Brute-Force Result - Flag Visible in Response The one request that stood out, with the flag sitting right in the response

Step 8 - Verifying the Result with ffuf

To double-check the finding, I ran the same attack using ffuf:

1
2
3
ffuf -u 'https://<lab_id>/administrator' -w wordlists.txt \
  -H "Cookie: token=<YOUR_JWT_TOKEN>; adminAccessToken=n0MqjBXna9A4FUZZ" \
  -fs <filter_code_to_ignore>

ffuf confirmed the missing 3 characters: rls.

ffuf Result ffuf independently confirming the same missing characters

Step 9 - Confirming Access With the Full Token

The flag had already shown up in the Caido response during brute-forcing, and ffuf confirmed the same missing characters independently. But I wanted to verify the token cleanly with a normal request too.

With the full token now known:

1
adminAccessToken=n0MqjBXna9A4rls

I set this cookie manually and visited /administrator again. Access was granted straight away, confirming the token was correct.

Flag Retrieved Access granted, flag retrieved

How Could This Be Fixed?

IssueFix
Admin access token has a fixed, predictable prefixGenerate the full token randomly with sufficient length and entropy - no shared or guessable segments
Token suffix is only 3 lowercase lettersA 3-character lowercase suffix has only 17,576 possible combinations - far too small to resist brute-forcing. Use a long, cryptographically random token instead
No rate limiting on the admin login/token checkImplement rate limiting and account lockout after repeated failed attempts
Error messages reveal token correctness clearlyAvoid giving attackers an easy way to distinguish “close” from “correct” - keep failure responses uniform

Key Takeaways

  • Comparing the same value across two different accounts is a powerful technique. Registering a second account just to diff a token against the first one is what cracked this whole challenge.
  • A token isn’t secure just because part of it looks random. If most of the value is fixed and only a few characters vary, the real entropy might be small enough to brute-force.
  • Clear, distinct error messages are a side channel. “Wrong code, access denied” vs a different response length for a near-correct guess both leak information that helps an attacker narrow things down.
  • Multiple tools can solve the same brute-force problem. Caido, ffuf, Burp Intruder, or a custom script - pick what’s available and has no rate limiting getting in the way.
  • IDOR testing is always worth a quick check, even if it doesn’t pan out. Testing /2/share took seconds and ruled out one entire vulnerability class before moving on.

Happy Hacking!

Challenge: BugForge Daily - GIFT LABJune 28th, 2026
This post is licensed under CC BY 4.0 by the author.