CTF Challenge Writeup: PicoCTF — JAuth
Category: Web Exploitation
Challenge Description:
Most web application developers use third party components without testing their security. Some of the past affected companies are:
- Equifax (a US credit bureau organization) — breach due to unpatched Apache Struts web framework CVE-2017–5638
- Mossack Fonesca (Panama Papers law firm) breach — unpatched version of Drupal CMS used
- VerticalScope (internet media company) — outdated version of vBulletin forum software used
Can you identify the components and exploit the vulnerable one? The website is running here. Can you become an admin?
Credentials to get started:
- Username: test
- Password: Test123!
Initial Analysis
The challenge name jAuth made me think of JWT (JSON Web Tokens), especially paired with the description asking if I could “become an admin”, a classic sign of a privilege escalation challenge.
I logged into the website using the provided credentials (test:Test123!
). There wasn’t anything important on the page itself, so the next step was to check what was happening in the background.
I opened DevTools (right-click -> Inspect -> Network tab) and noticed a token stored inside a cookie. JWTs are commonly stored in cookies or sent via authorization headers
To inspect the JWT’s contents, I copied the token and pasted it into jwt.io. JWTs follow a standard format:
header.payload.signature
- Header: contains metadata like the type of token and the algorithm used (in this case, HS256 — HMAC with SHA-256)
- Payload: includes claims or data (like
role: user
) - Signature: a hashed value used to verify the token’s integrity
I noticed there was a role
field set to user inside the payload.
I figured I needed to modify the JWT to elevate my privileges from user
to admin
.
Generating new JWT
Exploiting the “None” Algorithm Vulnerability
I knew from experience that some JWT implementations have a flaw. They allow the algorithm to be set to none, effectively bypassing the signature verification. This means the server skips verifying the token’s integrity, allowing anyone to modify the payload and create a valid-looking token without needing a secret key or signature.
You could create a new JWT token using terminal commands (like Base64 encoding the header and payload, then concatenating them with “.” separator), but it’s not necessary. There are plenty of easier tools like token.dev that can handle this for you.
I opened token.dev, pasted the token, and began editing the header and payload.
I edited the header like this:
{
"typ": "JWT",
"alg": "none"
}
And the payload to:
{
"auth": 1740940222797,
"agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"role": "admin",
"iat": 1740940223
}
Since the alg
was set to none
, the server didn’t expect a valid signature, so the final token was just the header and payload separated by a dot:
After adjusting the header and payload, my token now looked like this:
header.payload
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdXRoIjoxNzQwOTQyODg0ODQ1LCJhZ2VudCI6Ik1vemlsbGEvNS4wIChYMTE7IExpbnV4IHg4Nl82NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEzMy4wLjAuMCBTYWZhcmkvNTM3LjM2Iiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzQwOTQyODg1fQ
I copied the new token and returned to the DevTools -> Application tab -> Cookies. I replaced the existing JWT value with my modified one and refreshed the page.
But it didn’t work.
Little Oversight
After several failed attempts, I checked the hints. One hint said:
“The JWT should always have two (2) . separators.”
I finally noticed, the modified JWT was missing the trailing dot after the payload section.
JWT tokens must always have the full structure, even if the signature is empty. Without the final .
, the server saw it as invalid.
So I added a .
at the end of my token, like this:
header.payload.
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdXRoIjoxNzQwOTQyODg0ODQ1LCJhZ2VudCI6Ik1vemlsbGEvNS4wIChYMTE7IExpbnV4IHg4Nl82NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEzMy4wLjAuMCBTYWZhcmkvNTM3LjM2Iiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzQwOTQyODg1fQ.
I replaced the old JWT in the cookie with my updated token with the trailing dot, refreshed the page and it worked!
The flag appeared on the page:
Flag: picoCTF{succ3ss_@u7h3nt1c@710n_72bf8bd5}
Key Takeaways
- JWT Structure — JWT tokens always have three parts (header, payload, signature) separated by two dots (
.
). Even if the signature is empty, the format must be preserved. - The “None” Algorithm Vulnerability — Some JWT implementations allow the algorithm to be set to none, disabling signature verification.
- Privilege Escalation — In web exploitation, it often revolves around manipulating tokens, cookies, or headers to gain higher-level access or impersonate privileged users.
Happy Hacking!