
Keycloak 26.8.1 SAML 브로커 취약점에 대한 루프백 PoC: 퍼센트 인코딩된 중복 SAMLResponse 이름이 리디렉션 서명 검증을 우회하여 다른 사용자로 로그인할 수 있게 한다.
Keycloak 계정이 없는 사람이 다른 사람으로서 회사 SAML 로그인을 완료할 수 있습니다. 그들은 업스트림 아이덴티티 프로바이더에서 한 번 로그인하고, 그 서명된 리다이렉트를 보관한 뒤, 퍼센트 인코딩된 이름 아래에 두 번째 SAML 문서를 그 앞에 붙입니다. Keycloak은 정직한 서명을 검증한 다음, 위조된 문서에 있는 이름으로 그들을 로그인시킵니다. 이는 HTTP-Redirect 응답에 서명하는 SAML 브로커가 필요하며, 서명 검증이 켜져 있고 어서션 서명은 꺼져 있어야 하는데, 이는 메타데이터 가져오기가 구성하는 방식입니다. 이는 issue #53354의 세미콜론 분할이 아닙니다.
분류: OAuth 잔여물 도달 범위: 원격 (Keycloak에 대해 인증되지 않음; 리다이렉트 서명 SAML 브로커 필요)
Keycloak 26.8.1 (24ed1dad) - Red Hat
| ID | 아직 CVE 없음 |
| 분류 | OAuth 잔여물 |
| 도달 범위 | 원격 (Keycloak 계정 없음; IdP로부터 하나의 진짜 HTTP-Redirect 서명 필요) |
| CWE | CWE-287 / CWE-347 / CWE-116 |
| CVSS | 높음: 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| 제품 | Keycloak |
| 영향 받음 | 26.8.1 (24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b); Quarkus 3.40.1; 이미지 quay.io/keycloak/keycloak:26.8.1 |
| 인증 | Keycloak에 대해서는 없음. validateSignature=true이고 wantAssertionsSigned가 없거나 false인 SAML 아이덴티티 프로바이더 브로커가 필요함. |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1:18084 전용 |
피해자의 NameID(보통 이메일)와 해당 IdP로부터의 실제 서명된 HTTP-Redirect 하나가 필요합니다. Keycloak 비밀번호는 필요하지 않습니다. 메일함도 필요하지 않습니다. POST 전용 IdP는 그 서명을 넘겨주지 않습니다. 메타데이터 가져오기가 POST를 선호하더라도 GET ACS는 계속 마운트된 상태로 유지됩니다.
| 권한 | 메서드 / 싱크 | 정찰 / 무결성 / 가용성 | 랩 입증 또는 추론 |
|---|---|---|---|
| 정직한 서명이 여전히 검증되는 동안 위조된 SAML 문서를 바인딩 | 서명된 SAMLResponse 앞에 SAML%52esponse=<forged>를 붙인 GET 브로커 ACS | 무결성 (인증) | 랩 입증. 대조 이메일 [email protected]. 공격 이메일 [email protected] |
| first-broker-login을 통해 피해자 사용자 생성 | 위조된 NameID에 대한 callback.authenticated | 무결성 (계정 생성) | 랩 입증. 분할 쿼리 전 피해자 수 0, 후 1 |
| 동일한 IdP를 사용하는 관리자를 포함한 기존 브로커 사용자로서의 세션 | 동일한 ACS, 해당 사용자의 NameID | 무결성 (계정 탈취) | 추론. 랩은 첫 로그인 시 피해자를 생성함 |
| RelayState와 인증 세션 유지 | RelayState는 서명된 값으로 유지됨 | 무결성 | 소스. 서명이 RelayState를 포함함 |
| 부정 통제 | 추가 파라미터 없는 정직한 서명 리다이렉트 | 해당 없음 | 랩 입증. 대조는 공격자 이메일로 완료됨 |
이 잔여물에서 닫힌 상태로 유지되는 항목
| 축 | 메서드 / 싱크 | 닫힌 이유 |
|---|---|---|
| 무결성 | 세미콜론 분할 SAMLResponse=...;SAMLResponse=... | issue #53354. quarkus.http.use-semicolon-as-query-param-delimiter=false |
| 무결성 | wantAssertionsSigned=true일 때 서명되지 않은 어서션 | handleLoginResponse가 그때 어서션 서명을 요구함 |
| 무결성 | SAML 브로커 없음, 또는 리다이렉트 서명이 없는 POST 전용 IdP | 공격자는 GET에서 SigAlg + Signature를 절대 얻지 못함 |
| 무결성 | 호스트 RCE, 인증되지 않은 Admin REST | 분류 외 |
| 무결성 | 기본 VERIFY_PROFILE 이름 양식 | 완료 단계로서 랩 입증이며, 반증이 아님. 양식은 이미 브로커 이메일을 보유함 |
Keycloak 26.8.1을 고정했습니다. RESTEasy Reactive는 Vert.x에서 @QueryParam("SAMLResponse")를 가져오는데, Vert.x는 쿼리 이름을 퍼센트 디코딩하므로 SAML%52esponse가 SAMLResponse로 축약되고 첫 번째 값이 우선합니다. verifyRedirectSignature는 원시 쿼리에서 getQueryParameters(false)로 서명된 문자열을 재구성하는데, 여기서 해당 이름은 다른 키로 남습니다.
이미 기록된 잘못된 접근: #53354를 이 잔여물로 취급한 것(퍼센트 인코딩된 이름이지 세미콜론이 아님); 랩 클라이언트에서 ACS 302를 따라가며 AUTH_SESSION_ID를 버린 것; VERIFY_PROFILE에서 빈 firstName/lastName을 미스로 읽은 것(이메일은 이미 브로커링된 NameID임); 리버스 셸. 연극일 뿐.
cryptography가 포함된 Python 3.11+. Docker 필요.
python3 keycloak-saml-redirect-param-split-Abraxas-Labs.py
또는:
cd lab
./run.sh
오직 http://127.0.0.1:18084만 대상으로 합니다. 이미지 quay.io/keycloak/keycloak:26.8.1. 런처는 lab/로 chdir한 뒤 run.sh를 exec합니다. 랩 IdP는 프로세스 내에서 동작합니다. 실제 아이덴티티 제공자와는 절대 통신하지 않습니다.
IOC image=quay.io/keycloak/keycloak:26.8.1 label-version=26.8.1
IOC serverinfo-version=26.8.1
IOC validate-signature=true want-assertions-signed=absent
IOC review-profile=DISABLED
IOC [email protected]
IOC victim-before=0
IOC [email protected]
IOC victim-after=1
SUCCESS KC-H04-SAML-REDIRECT kcs04b8n1
엔드포인트가 파싱하는 것과 동일한 쿼리 문서를 검증하세요. 서명 재구성 전에 매개변수 이름을 퍼센트 디코딩하거나, 디코딩 후 SAMLResponse가 두 개 이상 포함된 쿼리를 거부하세요. 메타데이터 가져오기 경로를 포함하여 리디렉션 브로커에 대해 wantAssertionsSigned를 켜세요.
24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b)SAMLEndpoint.redirectBinding (SAMLEndpoint.java:182-183, :779-813, :896-914)SamlProtocolUtils.verifyRedirectSignature (SamlProtocolUtils.java:216-249)KeycloakUriInfo.getQueryParameters (KeycloakUriInfo.java:147-163)GNU Affero GPL v3.0. LICENSE를 참조하세요. 루프백 실험실 전용. 보증 없음.
Abraxas Labs [email protected]
CVE 공로는 Abraxas Labs에 돌려주세요.