Share
## https://sploitus.com/exploit?id=889E9326-1D8F-5960-8EF2-9A4F79226A68
# CVE-2026-29000 β€” pac4j-jwt Library-Level PoC Lab

## TL;DR

This repository contains a **library-level PoC** for **CVE-2026-29000** in `pac4j-jwt`.

It compares vulnerable and patched behavior with two cases:

* **Baseline**: a legitimate token should be accepted
* **Attack**: a forged token should be accepted on vulnerable versions and rejected on the patched version

| Version | Baseline | Attack | Result |
|---|---:|---:|---|
| `6.0.3` | βœ… | βœ… | Vulnerable |
| `6.0.4.1` | βœ… | βœ… | Vulnerable |
| `6.3.3` | βœ… | ❌ | Patched |

This PoC demonstrates authenticated profile creation with attacker-controlled subject and roles on vulnerable versions, while the patched version rejects the forged token.

---

## What this project is

This is **not** a web application demo.

It is a small Java program that calls `JwtAuthenticator` directly and compares multiple versions of `pac4j-jwt` inside Docker.

The goal is to prove three things:

1. legitimate tokens still work
2. forged attacker-controlled claims are accepted by vulnerable versions
3. forged attacker-controlled claims are rejected by the patched version

---

## Why these versions were selected

The tested versions were chosen deliberately:

* **6.0.3** β€” included because a public technical write-up reported a working PoC on this version
* **6.0.4.1** β€” included because public advisory data places it inside the affected 6.x range
* **6.3.3** β€” included because it is the fixed release for the 6.x line

This gives the lab three useful reference points:

* a public-PoC reference version
* an advisory-confirmed affected version
* the patched version

---

## Project structure

```text
.
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ pom.xml
└── src/main/java/lab/Repro.java
```

### File roles

* `docker-compose.yml`
  Defines the test matrix for each version.

* `Dockerfile`
  Builds and runs the PoC inside a container.

* `pom.xml`
  Defines dependencies and builds a runnable fat JAR.

* `src/main/java/lab/Repro.java`
  The actual PoC harness.

---

## What the services mean

The `docker-compose.yml` file defines three services:

* `v603` = test `pac4j-jwt 6.0.3`
* `v6041` = test `pac4j-jwt 6.0.4.1`
* `patched` = test `pac4j-jwt 6.3.3`

So these commands mean β€œrun the PoC once against that specific version”:

```bash
docker compose run --rm v603
docker compose run --rm v6041
docker compose run --rm patched
```

`--rm` means the temporary container is removed after the run finishes.

---

## How to run it

### Build

```bash
docker compose build --no-cache
```

### Run

```bash
docker compose run --rm v603
docker compose run --rm v6041
docker compose run --rm patched
```

---

## What the PoC does

For each version, the program runs two cases.

### 1) Baseline

It generates a legitimate token and validates it through `JwtAuthenticator`.

Expected result:

* accepted on all tested versions

### 2) Attack

It generates a forged token with attacker-controlled claims and validates it through `JwtAuthenticator`.

Expected result:

* vulnerable versions β†’ forged identity accepted
* patched version β†’ forged token rejected

---

## How to read the output

### Vulnerable output

You should see something like this:

```text
[case] baseline
  result: ACCEPTED
  observed_subject: alice
  observed_roles: [ROLE_USER]

[case] attack
  result: ACCEPTED
  observed_subject: admin#override
  observed_roles: [ROLE_SUPERUSER, ROLE_ADMIN]

[summary]
  conclusion: VULNERABLE: forged token accepted
```

Meaning:

* the normal token works
* the forged token is also accepted
* the subject and roles were replaced with attacker-controlled values

### Patched output

You should see something like this:

```text
[case] baseline
  result: ACCEPTED
  observed_subject: alice
  observed_roles: [ROLE_USER]

[case] attack
  result: REJECTED
  reason: CredentialsException: A non-signed JWT cannot be accepted as signature configurations have been defined

[summary]
  conclusion: PATCHED: forged token rejected
```

Meaning:

* the normal token works
* the forged token is rejected
* the patched version no longer accepts the non-signed inner JWT path used by the attack

---

## Why this repository focuses on library behavior instead of a generic token script

This CVE affects a **library-level authentication path**, not a single application with one universal role model.

The reusable part is the **attack shape**:

* forged attacker-controlled claims
* encrypted as JWE
* passed into `JwtAuthenticator`

What is **not universal** across real applications:

* claim names
* role names
* authorization mapping
* key material / JWKS setup
* application-specific profile handling

Because of that, this repository focuses on proving that the library accepts forged attacker-controlled claims on vulnerable versions, rather than pretending there is one universal token that would automatically work against arbitrary applications.

---

## Screenshots

1. `docker compose run --rm v603`
2. `docker compose run --rm v6041`
3. `docker compose run --rm patched`

### Vulnerable version: 6.0.3
![example 603 output](images/01-603-poc.png)

### Vulnerable version: 6.0.4.1
![example 6041 output](images/02-6041-poc.png)

### Patched version: 6.3.3
![example patched output](images/03-patched.png)

---

## References

* [pac4j security advisory for JwtAuthenticator](https://www.pac4j.org/blog/security-advisory-pac4j-jwt-jwtauthenticator.html)
* [GitHub Advisory Database: CVE-2026-29000](https://github.com/advisories/GHSA-pm7g-w2cf-q238)
* [NVD entry: CVE-2026-29000](https://nvd.nist.gov/vuln/detail/CVE-2026-29000)
* [CodeAnt technical write-up: public-key authentication bypass PoC](https://www.codeant.ai/security-research/pac4j-jwt-authentication-bypass-public-key)

---

## Final takeaway

This project demonstrates three core facts:

1. legitimate baseline tokens are accepted on all tested versions
2. forged attacker-controlled claims are accepted on `6.0.3` and `6.0.4.1`
3. forged attacker-controlled claims are rejected on `6.3.3`

This is the core vulnerable-versus-patched evidence for this CVE in this repository.

On vulnerable versions, the forged token is not merely parsed β€” it produces an authenticated profile with attacker-controlled subject and roles.