> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jaspervanzeir.be/llms.txt
> Use this file to discover all available pages before exploring further.

# Necromancers Notebook

For this challenge, I started with my usual web recon workflow. I opened Burp Suite, visited the application, and clicked through as many pages and actions as possible. My goal was not to exploit anything immediately, but to build a useful HTTP history first so I could understand the application's routes, API calls, and authentication flow before choosing an attack path.

***

## Enumeration

After manually browsing through the application, I also checked the JavaScript endpoints with [**JS Recon Buddy**](https://addons.mozilla.org/fy-NL/firefox/addon/js-recon-buddy/). This is a browser extension that extracts paths and endpoints from loaded JavaScript files, which is extremely useful for finding functionality that the frontend does not openly advertise.

One endpoint immediately stood out:

```text theme={null}
/admin
```

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-01-js-recon-admin-endpoint.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=0e44f6f201d3296fd2a3cbe4213e196b" alt="JS Recon Admin Endpoint" width="3454" height="2060" data-path="images/necromancers-notebook-01-js-recon-admin-endpoint.png" />
</Frame>

That looked promising, so I navigated to `/admin` directly in the browser. Unfortunately, the application did not let me in and returned a clear error:

```text theme={null}
Failed to load admin panel
```

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-02-admin-panel-denied.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=95e8270e6154b8cd743ae1c3e486d815" alt="Admin panel denied" width="3450" height="2061" data-path="images/necromancers-notebook-02-admin-panel-denied.png" />
</Frame>

Even though the frontend blocked me, this was still very interesting. I knew there had to be some kind of backend request behind that admin page, so I went back to Burp Suite to see what happened when I visited it.

In the HTTP history, one request immediately caught my eye:

```shellscript theme={null}
GET /api/admin/flag
```

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-03-admin-flag-request.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=0bc89e118d565a19620380c932333076" alt="Admin Flag Request" width="3455" height="2060" data-path="images/necromancers-notebook-03-admin-flag-request.png" />
</Frame>

That looked exactly like the endpoint I needed. The response was a `403 Forbidden`, but now I had a concrete target instead of guessing through the UI.

## Inspecting the JWT

I sent the `/api/admin/flag` request to Burp Repeater and opened the **JSON Web Token** tab. JWTs are often used for both authentication and authorization in these kinds of labs, so I wanted to inspect the token before trying anything else.

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-04-jwt-user-role.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=201cdf28f4d5f5f1072df068ee5452ff" alt="JWT User Role" width="3455" height="2060" data-path="images/necromancers-notebook-04-jwt-user-role.png" />
</Frame>

The token was using `HS256`, and the decoded payload was very straightforward:

```json theme={null}
{
  "id": 3,
  "username": "example",
  "role": "user",
  "iat": 1780302576
}
```

The important part was obvious:

```json theme={null}
"role": "user"
```

My first instinct was exactly what you would expect:

> What happens if I just change `user` to `admin`?

## Initial Access / Exploitation

I edited the JWT payload in Burp and changed the role from `user` to `admin`.

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-05-admin-role-invalid-token.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=87a7e201139c9e0d7a85fb95c0337d3e" alt="Admin Role Invalid Token" width="3455" height="2062" data-path="images/necromancers-notebook-05-admin-role-invalid-token.png" />
</Frame>

When I sent the request, the server responded with:

```json theme={null}
{
  "error": "Invalid token"
}
```

This made sense. By changing the payload, I broke the signature. Still, I always like to test the simple idea first, because sometimes CTF applications validate less than you expect.

Since this reminded me of the earlier [**Shady Oaks Financial**](/shady-oaks-financial) challenge, my next thought was the classic JWT `none` algorithm bypass. In that challenge, changing the JWT header algorithm to `none` and removing the signature was enough to get admin access.

So I changed the header to:

```json theme={null}
{
  "alg": "none",
  "typ": "JWT"
}
```

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-06-none-algorithm-failed.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=bcedb6b3469a09566edc328d3a051810" alt="None Algorithm Failed" width="3455" height="2057" data-path="images/necromancers-notebook-06-none-algorithm-failed.png" />
</Frame>

This also failed. The application still returned `Invalid token`, which told me two important things:

* The server was actually verifying the JWT signature.
* The server was not vulnerable to the `alg: none` bypass.

At that point, I knew I needed a valid signature for an admin token.

## Cracking the JWT Secret

Because the token used `HS256`, the same secret is used to sign and verify the JWT. If that secret is weak, it can sometimes be cracked with a wordlist attack.

I switched over to Kali Linux and created a file called `jwt.txt` containing my current token. Then I used Hashcat with the `rockyou.txt` wordlist:

```shellscript theme={null}
hashcat -m 16500 -a 0 jwt.txt /usr/share/wordlists/rockyou.txt --status
```

The important parts of this command were:

* `-m 16500`: Tells Hashcat to use JWT mode.
* `-a 0`: Uses a straight wordlist attack.
* `jwt.txt`: The file containing my JWT.
* `/usr/share/wordlists/rockyou.txt`: The wordlist used to guess the signing secret.
* `--status`: Prints progress while Hashcat runs.

Luckily, the secret was weak enough to be cracked almost immediately.

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-07-hashcat-secret-cracked.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=2a4a3ae2f6fb2129d3f2cead95953784" alt="Hashcat Secret Cracked" width="3455" height="2060" data-path="images/necromancers-notebook-07-hashcat-secret-cracked.png" />
</Frame>

Hashcat revealed the signing secret:

```text theme={null}
pumpkin
```

That was the breakthrough. I no longer needed to bypass signature verification, because I could now create a valid signed token myself.

## Forging an Admin Token

To make sure the secret was correct, I went to [jwt.io](https://jwt.io), pasted in my original token, and entered `pumpkin` as the signature verification secret.

The site confirmed that the token was valid and the signature was verified.

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-08-jwt-secret-verified.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=ae3f74fe08003952fdc253df5886d849" alt="JWT Secret Verified" width="3455" height="2060" data-path="images/necromancers-notebook-08-jwt-secret-verified.png" />
</Frame>

Next, I went to the JWT encoder section. I kept the algorithm as `HS256`, kept the secret as `pumpkin`, and changed the payload role from `user` to `admin`:

```json theme={null}
{
  "id": 3,
  "username": "example",
  "role": "admin",
  "iat": 1780302576
}
```

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-09-admin-token-generated.png?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=5280a16bbc53f2ca4321cde02e769986" alt="Admin Token Generated" width="3455" height="2059" data-path="images/necromancers-notebook-09-admin-token-generated.png" />
</Frame>

jwt.io generated a new signed token for me. I copied that token back into Burp Suite and replaced the old `Authorization: Bearer` token in the `/api/admin/flag` request.

When I sent the request again, the response finally changed from `403 Forbidden` to `200 OK`.

<Frame>
  <img src="https://mintcdn.com/z3r0d4yj/KUQElcY_iuly1MhJ/images/necromancers-notebook-10-flag-response.jpg?fit=max&auto=format&n=KUQElcY_iuly1MhJ&q=85&s=794a177cc4a4f4e11d5bd86ec06c98cd" alt="Flag response" width="3455" height="2056" data-path="images/necromancers-notebook-10-flag-response.jpg" />
</Frame>

The response contained the flag and the message:

```text theme={null}
Congratulations! You have mastered the dark arts of JWT forgery.
```

Challenge solved.

***

## Tools Used

* **Burp Suite (Community Edition):** Proxy, HTTP History, Repeater, and the JSON Web Token tab to inspect and modify requests.
* **JS Recon Buddy:** To discover hidden frontend routes and API endpoints from JavaScript files.
* **Hashcat:** To brute-force the JWT signing secret using mode `16500`.
* **rockyou.txt:** Wordlist used for the JWT secret cracking attempt.
* **jwt.io:** To verify the cracked secret and generate a new valid admin JWT.

***

## Summary

* **Key Steps:** I used JS Recon Buddy to discover the hidden `/admin` route, then confirmed through Burp Suite that it triggered a request to `/api/admin/flag`. After inspecting the JWT, I tried changing the role to `admin` directly and also tested the `alg: none` bypass, but both failed because the server properly checked the signature. Since the token used `HS256`, I brute-forced the weak signing secret with Hashcat, found `pumpkin`, forged a valid admin token, and used it to access the flag endpoint.
* **What I Learned:** This challenge reinforced how dangerous weak JWT secrets are. Even when an application correctly rejects modified tokens and blocks `alg: none`, the entire authorization model still collapses if the HMAC secret is easy to guess.
* **Crucial Mistakes/Takeaways:** My first ideas failed, but they were still useful because they ruled out the obvious bypasses. The important pivot was recognizing that `HS256` depends on a shared secret. If that secret appears in a common wordlist like `rockyou.txt`, an attacker can generate completely valid tokens without needing any other vulnerability.
