TryHeartMe - TryHackMe Writeup | JWT Cookie Tampering to Admin Shop
TryHeartMe TryHackMe writeup β Love At First Breach 2026: JWT decode, role tampering without re-signing, and access to hidden ValenFlag for the flag.
##TryHackMe Room β TryHeartMe (Love At First Breach 2026)
Valentine-themed shop with a hidden staff-only item.
TryHeartMe is a vulnerable web shop where users buy gifts with credits. The challenge implied a staff-only item (ValenFlag) that holds the flag, so the goal was to get access to that item. I registered a normal account and inspected how the app handled authentication: the response set a cookie that looked like a JWT (three Base64 parts separated by dots). I decoded the payload and saw claims like role and credits. The natural next step was to test whether the server actually verified the JWT signature: if it didn't, we could change role to admin and get access to the hidden product without knowing the secret key. I tried cracking the signing key with a wordlist first (it failed); then I tampered the payload with jwt_tool, left the signature unchanged (so it was invalid), and replayed the cookie. The server accepted it and showed the admin UI and ValenFlag. This writeup documents that flow from registration through decode, tamper, and flag.
##Overview
| Item | Detail |
|---|---|
| Goal | Access and βpurchaseβ the hidden ValenFlag item to retrieve the flag |
| Attack chain | Register β capture JWT β decode β tamper role to admin β replay cookie β buy ValenFlag β flag |
| Key concepts | JWT structure, signature verification bypass, client-side trust of claims |
##High-level attack chain
| Step | Technique | Outcome |
|---|---|---|
| 1 | Register & capture JWT | Obtain token in cookie |
| 2 | Decode JWT | See role, credits, etc. |
| 3 | (Optional) Try cracking secret | Fails; pivot to tampering |
| 4 | Tamper payload (role β admin) | Forge cookie without re-signing |
| 5 | Replay forged cookie | Server accepts; admin UI appears |
| 6 | Purchase ValenFlag | Get receipt with flag |
##Reconnaissance and registration
The app was at http://<TARGET_IP>:5000 β a Valentine shop with products like Rose Bouquet, Heart Chocolates, etc.

No ValenFlag visible yet; the nav showed βGuest,β so I registered:
POST /register HTTP/1.1
Content-Type: application/x-www-form-urlencoded
email=...&password=...The response set a cookie, e.g.:
Set-Cookie: tryheartme_jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...So authentication was JWT in a cookie. Next step: decode and inspect the payload.
##Key concept: JWT structure
A JWT has three parts (header, payload, signature), each Base64-encoded. The payload holds claims (e.g. email, role, credits). The signature is computed with a secret; if the server does not verify it, we can change the payload and send a token with an invalid signature β and the server may still trust it.
##Decoding the JWT
I decoded the payload (e.g. with jwt_tool or CyberChef) and saw something like:
{
"email": "user@example.com",
"role": "user",
"credits": 0,
"iat": ...,
"theme": "valentine"
}So role and credits were in the token. If the backend trusted these claims without verifying the signature, I could set role to admin and gain access to staff-only features.
##Attempting to crack the secret (pitfall)
Before tampering, I tried to crack the JWT signing key so we could produce a valid signature. I used jwt_tool with a large wordlist (e.g. rockyou):
python3 jwt_tool.py <JWT> -d /usr/share/wordlists/rockyou.txtAfter a long run, the result was key not found β so the secret wasn't in the wordlist. Instead of giving up, I pivoted: what if the server didn't actually verify the signature at all? Many broken implementations decode the JWT and read the payload but never check that the signature matches. In that case we could edit the payload (e.g. change role from user to admin), leave the signature as-is (so it would be invalid), and send the token. If the server only parsed the payload and trusted it, we'd get admin access without knowing the key. I decided to test that hypothesis.
##JWT role tampering
Using jwt_toolβs tampering mode:
python3 jwt_tool.py <JWT> -TI changed:
- >
"role": "user"β"role": "admin"
jwt_tool produced a new token with the modified payload but no valid signature. I replaced the browser cookie with this forged token and refreshed the app.

##Admin access and ValenFlag
After sending the forged cookie:
- >Credits showed as a high value (e.g. 5000).
- >An Admin link appeared.
- >A new product appeared: ValenFlag (Staff Only).
So the server trusted the client-supplied JWT claims and did not enforce signature verification. I βpurchasedβ ValenFlag (e.g. POST /buy/valenflag), was redirected to a receipt page (/receipt/valenflag), and the page displayed the flag.
##Flag
THM{*redacted*}(Replace with the actual flag when you solve the room.)
##Root cause (brief)
The application parsed the JWT from the cookie and used role and credits for authorization but did not verify the signature. That allowed privilege escalation and arbitrary credit manipulation by editing the payload.
##References and tools
- >TryHackMe β Love At First Breach
- >jwt_tool
- >SecLists (wordlists)
- >CyberChef (JWT decode)
- >OWASP β JWT Cheat Sheet
This writeup is part of my Love At First Breach 2026 event writeups.