
CVE-2021-21239 익스플로잇: pysaml2/Redash의 SAML 서명 검증 우회. 공개 키를 내장한 SAML 응답을 위조하여 사용자를 가장하고 JIT 프로비저닝을 통해 권한을 상승시킵니다.
SAML(Security Assertion Markup Language)은 연합 ID 시스템에서 당사자 간 인증 및 권한 부여 데이터를 교환하기 위한 개방형 표준입니다. 이를 통해 도메인 간 SSO(Single Sign-On)가 가능합니다. 주요 구성 요소는 다음과 같습니다.
일반적인 SAML 흐름에서 SP는 사용자를 IdP로 리디렉션하여 인증을 수행합니다. IdP는 사용자에 대한 어설션(예: 이메일, 이름)이 포함된 서명된 SAML 응답을 반환합니다. SP는 서명을 확인하고 사용자를 프로비저닝하거나 로그인시킵니다.
CVE-2021-21239는 Redash <=10.1.0에서 SAML SSO에 사용되는 pysaml2 라이브러리(버전 < 6.5.0)의 서명 검증 우회 취약점입니다. 높은 수준에서 보면, 공격자가 임의의 내장 공개 키를 사용하여 SAML 응답을 위조할 수 있어 JIT(Just-In-Time) 프로비저닝을 통해 사용자 impersonation 및 권한 상승이 가능합니다.
이 취약점은 pysaml2가 SAML 서명을 확인하기 위해 xmlsec1을 호출하는 방식에서 비롯됩니다. Xmlsec1은 기본적으로 구성된 IdP 인증서보다 SAML 응답의 <ds:KeyInfo>에 포함된 공개 키를 선호합니다. 공격자는 자체 생성한 RSA 키 쌍으로 SAML 응답을 제작하고, 공개 키를 <ds:RSAKeyValue>에 포함시킨 후 개인 키로 어설션에 서명할 수 있습니다. pysaml2가 xmlsec1이 신뢰된 IdP 인증서만 사용하도록 제한하지 않기 때문에 위조된 서명이 유효한 것으로 간주되어 공격자가 모든 사용자(예: admin)로 로그인하고 최고 권한을 획득할 수 있습니다.
코드 수준에서 이 취약점은 pysaml2의 sigver.py(check_signature 함수)에 존재합니다. xmlsec1 명령 목록에 --enabled-key-data raw-x509-cert 옵션이 누락되어 있어 xmlsec1이 신뢰되지 않은 내장 키를 사용할 수 있습니다.
com_list = [
self.xmlsec, '--verify', '--enabled-reference-uris', 'empty,same-doc',
'--pubkey-cert-pem', cert_file_name,
'--id-attr:%s' % id_attr, node_name,
'--node-id', nodeid, '--output', output_file_name, xml_file_name
]
이로 인해 공격자가 <saml:NameID>의 이메일을 수정하여 JIT 프로비저닝을 이용해 모든 사용자로 impersonation할 수 있습니다.
익스플로잇을 테스트하기 위해 Docker Compose를 사용하여 로컬 Redash 인스턴스(v10.1.0 이하)를 구동합니다. 설정 과정은 Google IdP와 SAML SSO를 구성하는 것을 포함합니다.
docker-compose up 실행.REDASH_SAML_LOGIN_ENABLED=true, Google IdP의 REDASH_SAML_METADATA_URL=...).이 설정을 통해 Python 스크립트 또는 Burp Suite로 전송된 위조 SAML 응답을 테스트하여 권한 상승을 확인할 수 있습니다.
이 취약점은 pysaml2의 xmlsec1 호출에서 내장 키를 검증에 사용할 수 있도록 허용하여 신뢰된 IdP 인증서를 우회하기 때문에 발생합니다. 공격자는 RSA 키 쌍을 생성하고, SAML 응답에 공개 키를 <ds:RSAKeyValue>에 포함시키고 속성(예: 이메일을 관리자 이메일로)을 수정한 후 어설션에 서명합니다. Xmlsec1은 내장 키를 사용하여 검증하고 유효한 것으로 간주합니다.
수정은 pysaml2 v6.5.0(초기 커밋, 2021년 1월 17일)에서 src/saml2/sigver.py에 구현되었습니다. xmlsec1 명령 목록에 --enabled-key-data raw-x509-cert가 추가되어 구성된 IdP 인증서로만 검증을 제한합니다.
com_list = [
self.xmlsec, '--verify', '--enabled-reference-uris', 'empty,same-doc',
'--pubkey-cert-pem', cert_file_name,
'--enabled-key-data', 'raw-x509-cert',
'--id-attr:%s' % id_attr, node_name,
'--node-id', nodeid, '--output', output_file_name, xml_file_name
]
이를 통해 신뢰 체인 검증이 강제되어 내장 키 사용이 방지됩니다. Redash는 이후 버전(예: v11+)에서 pysaml2를 업그레이드하여 이 문제를 수정했습니다. 취약한 버전에서는 --insecure를 사용하지 않았으며, 버그는 제한 옵션이 누락된 것이었습니다.
익스플로잇 코드는 cve-2021-21239.py에 구현되어 있습니다. 이 Python 스크립트는 CVE-2021-21239(Redash <=10.1.0)를 악용하여 SAML 응답을 위조하는 과정을 자동화하며, JIT 프로비저닝을 통해 사용자 impersonation 및 권한 상승을 가능하게 합니다. 이 익스플로잇은 Google Workspace SAML IdP를 대상으로 하지만, 다른 IdP(keycloak)에서도 테스트되었습니다. 스크립트는 다음 단계를 수행합니다.
email="[email protected]")을 제공합니다.idpid를 가져와 대상 구성과의 호환성을 보장합니다.SAMLResponseTempalte.xml)을 사용합니다. 동적 UTC 타임스탬프가 5분 유효 기간으로 생성되어 Redash의 검증을 통과합니다.xmlsec Python 라이브러리를 사용하여 <saml:Assertion> 요소를 RSA-SHA256으로 서명하고, 공개 키를 <ds:RSAKeyValue>에 포함시켜 취약점을 악용합니다./saml/callback?org_slug=default)로 전송합니다.CVE-2021-21239를 악용하려면 Redash가 JIT 프로비저닝을 통해 수락하는 위조된 SAML 응답을 제작해야 했습니다. 주요 어려움과 우회 방법은 다음과 같습니다.
<ds:RSAKeyValue>에 포함시킨 후 개인 키로 서명하여 우회했습니다.xmlsec1 바이너리가 작동했지만 Python xmlsec 라이브러리는 공백/정규화 문제로 인해 다이제스트 불일치가 발생했습니다. 서명하기 전에 XML을 한 줄로 직렬화(etree.tostring(..., pretty_print=False))하여 우회했습니다.NotOnOrAfter는 거부되었습니다. 동적 UTC 타임스탬프를 사용하여 5분 유효 기간으로 우회했습니다.SAMLResponse를 이중 인코딩하여 파싱 오류가 발생했습니다. 원시 폼 데이터를 전송하여 우회했습니다.이러한 어려움은 취약점을 효과적으로 악용하기 위해 정확한 XML 조작과 환경 설정이 필요함을 강조합니다. 제공된 익스플로잇 코드는 이를 성공적으로 시연하며, SAML 응답을 위조하여 사용자를 impersonate하고 접근 권한을 얻습니다.