SKIP TO MAIN CONTENT

[ WRITEUP NODE / FIELD REPORTS ]

SOLVED CHALLENGES & FIELD ANALYSIS

SECURITY RESEARCH KNOWLEDGE TECHNIQUES
EVENT: Safaricom PwnZone 2.0 Qualifiers 2026CATEGORY: WebPOINTS: 150 PTS

JWT Forgery

AUTHORED BY:@bealthguy10/5/2026

JWT Forgery

150 points. The brief describes an old technique in a new shape, which is an accurate summary of the service behaviour.

There is no web page to inspect. Requesting the main address returns JSON and sets a cookie. The cookie is a token, and the body reads:

{"username":"bob","role":"user","message":"no /admin access"}

The subject is bob with the plain user role, and the message points at /admin. Requesting that path with no token returns invalid token; with bob's cookie it returns not admin. Two distinct error strings indicate the server validates the token first and the role second.

Decoding the cookie by inspection, the header names none as the signing method and the payload carries bob with the user role. There is no signature segment at all, only a trailing dot. The "new shape" phrasing suggests something beyond this, but the encoding proved to be unmodified.

A new token can be built the same way, with the same header and the role flipped:

header:

{"alg":"none","typ":"JWT"}

payload:

{"username":"bob","role":"admin"}

Base64url both parts, join with dots, leave the last part empty, and send the result as the auth cookie to /admin. The response is:

{"message":"Welcome, admin!","flag":"safctf{1e4d7bdea93b47c2a813ea5a89f20870}"}

No brute force and no signing secret were required. The server accepted the none method and trusted the role claim as written.

Using admin as the username with the admin role also succeeded. Bob was sufficient once the role claim read admin.

The same first inspection would apply to the related variants worth checking before any dictionary work: a weak shared secret, none under different letter casing, and signature stripping applied to an otherwise valid token. Here the header declared none on the first read, so forgery was the whole technique.

Flag:

safctf{1e4d7bdea93b47c2a813ea5a89f20870}