Post

WayWitch

WayWitch

CHALLENGE DESCRIPTION

(NOTE: use https:// to connect to the instance)

SOLUTION

By analyzing the source code, we can see that account with admin username hold the flag.

We get the /tickets path.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
async function generateJWT() {
                const existingToken = getCookie("session_token");

                if (existingToken) {
                    console.log("Session token already exists:", existingToken);
                    return;
                }

                const randomNumber = Math.floor(Math.random() * 10000);
                const guestUsername = "guest_" + randomNumber;

                const header = {
                    alg: "HS256",
                    typ: "JWT",
                };

                const payload = {
                    username: guestUsername,
                    iat: Math.floor(Date.now() / 1000),
                };

                const secretKey = await crypto.subtle.importKey(
                    "raw",
                    new TextEncoder().encode("halloween-secret"),
                    { name: "HMAC", hash: "SHA-256" },
                    false,
                    ["sign"],
                );

                const headerBase64 = btoa(JSON.stringify(header))
                    .replace(/\+/g, "-")
                    .replace(/\//g, "_")
                    .replace(/=+$/, "");
                const payloadBase64 = btoa(JSON.stringify(payload))
                    .replace(/\+/g, "-")
                    .replace(/\//g, "_")
                    .replace(/=+$/, "");

                const dataToSign = `${headerBase64}.${payloadBase64}`;
                const signatureArrayBuffer = await crypto.subtle.sign(
                    { name: "HMAC" },
                    secretKey,
                    new TextEncoder().encode(dataToSign),
                );

                const signatureBase64 = btoa(
                    String.fromCharCode.apply(
                        null,
                        new Uint8Array(signatureArrayBuffer),
                    ),
                )
                    .replace(/\+/g, "-")
                    .replace(/\//g, "_")
                    .replace(/=+$/, "");

                const token = `${dataToSign}.${signatureBase64}`;

                document.cookie = `session_token=${token}; path=/; max-age=${60 * 60 * 24}; Secure`;

                console.log("Generated JWT Session Token:", token);
            }

From the code above, we can see the web uses halloween-secret to generate token.

Inspect the web, we notice the token session, it is a HS256 JWT. We need to modify it with secret key to generate new token by using JWT

Copy the token and paste to our token, access /tickets and get the flag.