[This lab](https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jwk-header-injection) uses a JWT-based mechanism for handling sessions. The server supports the `jwk` parameter in the JWT header.
### 1. What makes this attack possible?
* **Normal flow:**
1. Client sends JWT → server extracts header.
2. Server looks up the **trusted key** (by `kid`) from its key store.
3. It verifies the signature.
* **Vulnerable flow (this lab):**
1. Server accepts any `jwk` object embedded *inside* the header.
2. It blindly trusts that key and verifies the signature **with the attacker-supplied key**.
3. If the signature matches the embedded key, the token is treated as valid.
> Outcome: the attacker becomes their own Certificate Authority for the application.
### 2. Tool stack
| Tool | Reason |
| ---------------------------------------- | --------------------------------- |
| **Burp Suite** + **JWT Editor** (BApp) | Generate keys, embed JWK, re-sign |
| *(Alternative)* `jwt-cli`, `python-jose` | DIY route if you prefer scripts |
---
### 3. TL;DR cheat-sheet
1. Log in as **wiener\:peter** → capture `session=<jwt>`.
2. In **JWT Editor ▸ Keys**: **New RSA Key → Generate** (stores key pair).
3. In **Repeater** change request path to `/admin`.
4. Switch to **JSON Web Token** tab → payload `"sub":"administrator"`.
5. Click **Attack ▸ Embedded JWK** → pick your new RSA key.
*Header now contains `jwk` + matching `kid`; token auto-signed with your private key.*
6. Send → **200 OK** admin panel.
7. Request **/admin/delete?username=carlos** → lab solved.
---
### 4. Deep-dive walkthrough
#### 4.1 Intercept the original token
* Start the lab, log in as **wiener\:peter**.
* In **Proxy ▸ HTTP history** locate `GET /my-account`.
* Observe cookie:
```
session=eyJhbGciOiJSUzI1NiIsImtpZCI6ImFwcC1rZXkxIn0...
```
#### 4.2 Generate your own RSA key pair
1. **Burp ▸ JWT Editor ▸ Keys ▸ New RSA Key**.
2. Click **Generate** (2048-bit by default) → **OK**.
Burp stores:
* `kid` – random UUID
* `n`, `e` – public modulus/exponent
* private key – for signing
![[CleanShot 2025-07-20 at 14.17.48.png]]
#### 4.3 Forge the admin token
1. Send the captured request to **Repeater**.
2. Change the path to `/admin`.
3. In **JSON Web Token** tab (provided by extension):
* Payload → replace `"sub":"wiener"` with `"administrator"`.
4. Click **Attack ▸ Embedded JWK** → select your RSA key.
*Burp does three things automatically:*
![[CleanShot 2025-07-20 at 14.18.22.png]]
* Inserts a `jwk` object (public part) into the header.
* Updates `kid` to match.
* Signs the token with your private key.
Header after injection (pretty-printed):
```json
{
"alg": "RS256",
"typ": "JWT",
"kid": "e7d1...7f1",
"jwk": {
"kty": "RSA",
"kid": "e7d1...7f1",
"n": "0vx7...Yl7w",
"e": "AQAB"
}
}
```
#### 4.4 Exploit
* Hit **Send**.
* Response **200 OK** shows admin dashboard.
* Find link:
```html
<a href="/admin/delete?username=carlos">Delete</a>
```