Writeups/TryHackMe/TryHeartMe - TryHackMe Writeup | JWT Cookie Tampering to Admin Shop
TryHackMeEasyRoom

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

ItemDetail
GoalAccess and β€œpurchase” the hidden ValenFlag item to retrieve the flag
Attack chainRegister β†’ capture JWT β†’ decode β†’ tamper role to admin β†’ replay cookie β†’ buy ValenFlag β†’ flag
Key conceptsJWT structure, signature verification bypass, client-side trust of claims

##High-level attack chain

StepTechniqueOutcome
1Register & capture JWTObtain token in cookie
2Decode JWTSee role, credits, etc.
3(Optional) Try cracking secretFails; pivot to tampering
4Tamper payload (role β†’ admin)Forge cookie without re-signing
5Replay forged cookieServer accepts; admin UI appears
6Purchase ValenFlagGet 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.

TryHeartMe homepage
TryHeartMe homepage

No ValenFlag visible yet; the nav showed β€œGuest,” so I registered:

http
POST /register HTTP/1.1 Content-Type: application/x-www-form-urlencoded email=...&password=...

The response set a cookie, e.g.:

text
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:

json
{ "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):

bash
python3 jwt_tool.py <JWT> -d /usr/share/wordlists/rockyou.txt

After 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:

bash
python3 jwt_tool.py <JWT> -T

I 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.

TryHeartMe tampered JWT token
TryHeartMe tampered JWT token


##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

text
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


This writeup is part of my Love At First Breach 2026 event writeups.

$ echo "Open to Red Team Security Research and Security Engineering roles."

> Open to Red Team Security Research and Security Engineering roles.

$ uptime

> Portfolio online since 2024 | Last updated: Mar 2026

"No one is useless in this world who lightens the burdens of another." β€” Charles Dickens

Considered a small donation if you found any of the walkthrough or blog posts helpful. Much appreciate :)

Buy me a coffee

Β© 2026 Shivang Tiwari. Built with Next.js. Hack the planet.