> For the complete documentation index, see [llms.txt](https://ganesha-hk.gitbook.io/cybersecurity-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ganesha-hk.gitbook.io/cybersecurity-writeups/port-swigger-jwt-labs/lab-5.md).

# lab-5

## JWT Authentication Bypass via JKU Header Injection

**Difficulty:** Practitioner

### Lab Description

This lab uses a JWT-based mechanism for handling sessions. The server supports the `jku` parameter in the JWT header. However, it fails to check whether the provided URL belongs to a trusted domain before fetching the key.

**Objective:** Forge a JWT that gives you access to the admin panel at `/admin`, then delete the user `carlos`.

**Credentials:** `wiener:peter`

### Root Cause

The application trusted the `jku` header parameter supplied by the client during JWT verification. Instead of using a fixed, trusted public key or a trusted allowlist of JWK Set URLs, the server followed this process:

```
Read JWT Header
      │
      ▼
Read jku
      │
      ▼
Download JWK Set from supplied URL
      │
      ▼
Find matching kid
      │
      ▼
Use downloaded public key
      │
      ▼
Verify Signature
```

Because the server did not restrict the `jku` URL to trusted domains, an attacker could supply a URL under their own control. The server then downloaded the attacker's public key and used it to verify the attacker's JWT. As a result, any JWT signed with the attacker's corresponding private key was accepted as legitimate.

### Exploitation

**Step 1 — Generate a new RSA key pair**

In Burp's **JWT Editor** tab, generate a new RSA key. The private key stays with the attacker; the public key will be hosted externally.

**Step 2 — Host the public key in a JWK Set**

Create a JWK Set containing your public key and upload it to the exploit server:

```json
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "attacker-key",
      "e": "AQAB",
      "n": "..."
    }
  ]
}
```

**Step 3 — Modify the JWT header**

Original:

```json
{
  "alg": "RS256",
  "kid": "server-key"
}
```

Modified:

```json
{
  "alg": "RS256",
  "kid": "attacker-key",
  "jku": "https://exploit-server/jwks.json"
}
```

**Step 4 — Modify the JWT payload**

Original:

```json
{"sub": "wiener"}
```

Modified:

```json
{"sub": "administrator"}
```

**Step 5 — Sign with the attacker's private key**

Use Burp JWT Editor to sign the modified JWT with the matching RSA private key.

**Step 6 — Server verification (vulnerable path)**

```
Read jku
      │
      ▼
Download attacker's JWK Set
      │
      ▼
Find attacker's kid
      │
      ▼
Use attacker's public key
      │
      ▼
Verify signature
      │
      ▼
Signature valid
      │
      ▼
Trust payload → administrator
```

**Step 7 — Access the admin panel and delete carlos**

Send the forged JWT in the `GET /admin` request. Open the response in the browser, delete `carlos`, and capture the delete request. Repeat the JWT modification on that request and send to complete the lab.

### Lesson Learned

* **Never trust client-controlled header parameters.** Parameters such as `jku`, `jwk`, and `kid` must never be blindly trusted.
* **Always use trusted verification keys.** The verification key should come from local configuration, trusted key storage, or an explicitly allowlisted JWK Set URL — never from arbitrary user input.
* **Restrict `jku` to trusted domains.** Only accept JWK Sets from known, pre-registered URLs such as `https://auth.company.com/.well-known/jwks.json`. Any other URL must be rejected.
* **Validate incoming JWKs.** If JWKs are accepted at all, validate the key type, algorithm, and `kid` against expected values.
* **Authentication must never rely on client input.** The client should never decide which key to trust, where to download keys from, or which public key to use for verification.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ganesha-hk.gitbook.io/cybersecurity-writeups/port-swigger-jwt-labs/lab-5.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
