flawopen.com/Incidents/postgresql-psql-injection-beyondtrust-treasury-december-2024
CVE-2025-1094:PostgreSQL psqlのSQLインジェクション
アプリがエスケープ済みテキストからpsql入力を作る際、不正なUTF-8でlibpqの処理を回避し、SQLや\!を注入できました。2025年2月13日修正。
あなたの言うことをすべてかぎかっこでくくり、決して命令と取り違えられないようにするのが仕事の通訳を想像してください。ところが、知らない文字で書かれた単語を渡すと、通訳は混乱してかぎかっこを一つ落としてしまうと分かります。あなたの言葉はかっこの外へ抜け出し、指示として読まれます。通訳は、誰かが試したあらゆる入力に対しては、設計どおりに正しく動いていたにもかかわらずです。
主要な概念と専門用語
PQescapeLiteral- 値を引用符でくくってエスケープし、SQL テキストに埋め込めるようにする libpq の関数。PQescapeIdentifier、PQescapeString、PQescapeStringConn も影響を受けました。
不正な UTF-8- 宣言された文字コードとして正しくないバイト列。エスケープ処理と psql とで、そのバイトが後続の何文字を取り込むかの解釈が食い違っていました。
psql のメタコマンド- サーバーではなく psql クライアントが処理するバックスラッシュ付きのコマンド。\! は行の残りを OS のシェルコマンドとして実行します。
CVE-2024-12356- BeyondTrust の Privileged Remote Access と Remote Support にある重大なコマンドインジェクションの欠陥。Rapid7 のテストでは、これでコード実行に至るには
CVE-2025-1094が必要でした。 バインドパラメーター- SQL テキストとは別にサーバーへ送られる値。引用符やエスケープの判断がそもそも行われません。
フレームワーク対応
インシデントのタイムライン
BeyondTrust が一部の Remote Support SaaS インスタンスの侵害を調査したのち、CVE-2024-12356 の公告 BT24-10 を公開する。米財務省は後に、この事案が自らのシステムに及んだと報告した。
CISA が CVE-2024-12356 を既知の悪用された脆弱性カタログに追加する。
PostgreSQL が CVE-2025-1094 を修正した 17.3、16.7、15.11、14.16、13.19 を公開し、Rapid7 が分析を発表する。
ステップ・バイ・ステップの攻撃フロー
入力がエスケープ関数に届く
アプリケーションが攻撃者の影響を受けたテキストを受け取り、SQL 文を組み立てるために PQescapeLiteral などの libpq 関数へ渡します。
不正な UTF-8 が引用符を隠す
PostgreSQL の公告によれば、シングルクォートの前に細工した不正なマルチバイト列を置くと、エスケープ処理がその引用符をエスケープしないままにしていました。
psql が文字列リテラルを早く閉じる
文が psql に渡されると、エスケープされなかった引用符が文字列リテラルを閉じ、残りの入力は SQL か psql のコマンドとして解釈されます。
メタコマンドがシェルを動かす
Rapid7 は、注入したテキストに \! メタコマンドを含められることを示しました。psql はそれを、自分が動いているマシン上で OS コマンドとして実行します。
ソースコード比較:脆弱 vs 堅牢化
psycopg 3 を使った説明用の Python で、BeyondTrust のコードではありません。脆弱な版は、公告が挙げる危険なパターン、つまり libpq で値をエスケープし、SQL テキストに埋め込み、そのテキストを psql に流す処理を再現しています。強化版はその両方を取り除きます。処理の前に不正な UTF-8 を拒否し、値は本物のドライバー経由でバインドパラメーターとして送るので、だませるエスケープ処理も、経路上の psql メタコマンド解釈器もありません。エスケープを使い続けるコードには、libpq の更新も引き続き重要です。
import subprocess
import psycopg
def lookup_user(conn: psycopg.Connection, name: bytes) -> bytes:
# libpq の PQescapeLiteral(17.3/16.7/15.11/14.16/13.19 より前)は、
# 不正な UTF-8 バイト列の後ろに隠れた引用符を無害化できなかった。
literal = conn.pgconn.escape_literal(name)
script = b"SELECT id FROM users WHERE name = " + literal + b";\n"
# psql は入力をスクリプトとして実行し、\! などのメタコマンドも処理する。
result = subprocess.run(["psql", "-At", "-d", "app"], input=script,
capture_output=True, check=True)
return result.stdout
import psycopg
MAX_NAME_CHARS = 128
def lookup_user(conn: psycopg.Connection, name: bytes) -> list[int]:
try:
text = name.decode("utf-8") # reject malformed input at the boundary
except UnicodeDecodeError:
raise ValueError("name is not valid UTF-8") from None
if len(text) > MAX_NAME_CHARS:
raise ValueError("name too long")
with conn.cursor() as cur:
# 値は SQL テキストとは別のバインドパラメーターとして送信され、
# psql プロセス(したがってメタコマンドパーサー)も介在しない。
cur.execute("SELECT id FROM users WHERE name = %s", (text,))
return [row[0] for row in cur.fetchall()]
エンジニアリング&システム堅牢化チェックリスト
- ✓アプリケーションのデータから入力を組み立てて psql を subprocess で呼び出している箇所を探し、バインドパラメーターを使うデータベースドライバーに置き換える。新たな呼び出しがあれば失敗する CI ルールも追加する。
- ✓宣言された文字コードとして正しくない入力は、パーサー、エスケープ関数、データベース呼び出しに届く前の境界で拒否する。b'\xc0\'' を送って検証エラーを期待するテストを追加する。
- ✓サーバーだけでなく libpq とクライアントツールも更新する。
CVE-2025-1094は PostgreSQL 17.3、16.7、15.11、14.16、13.19 で修正済みで、アプリケーションサーバー上のクライアントパッケージも更新が必要。 - ✓エスケープをまだ取り除けない箇所には、不正な UTF-8 に続けて引用符を渡し、結果が拒否されるか安全に引用されることを確認する回帰テストを置く。
- ✓特権リモートアクセスツールを攻撃対象領域の一部として扱う。管理インターフェースを制限し、ベンダーの通知があれば直ちにパッチを当て、API キーが想定外の場所から使われていないか監視する。