当社のAIエンジンNeutronが、UCバークレーのCyberGymベンチマークで97.1%のスコアを記録しました。 詳細を見る

ディープダイブ

ディープダイブ

pgAdmin 4 の Import/Export RCE、9.19 で修正 (CVE-2026-116241)

pgAdmin 4 9.19 未満では Import/Export のユーザーが誰でも psql の TO PROGRAM コマンドをホスト上で実行でき、ロックアウト回避が管理者パスワード推測を無制限にします。9.19 で修正済み。

要点

  • pgAdmin 4 の Import/Export Data ツールは、細工したクエリのエクスポートが括弧ガードをすり抜けると、pgAdmin ホスト上で攻撃者の OS コマンドを実行します。PostgreSQL CNA による CVSS v3.1 9.9 Critical の CVE-2026-116241 です。ツールの権限はデフォルトで非管理者にも付与されています。
  • 同じリリースは 2 つ目の critical である CVE-2026-116238 も修正します。ログイン失敗回数制限の 4 つの回避により、未認証の攻撃者がサーバーモードで管理者のパスワードを無制限に推測できます。Xint by Theori の Yonghwa Lee 氏がインジェクションを報告しました。
  • 2026 年 10 月 30 日に公表された 14 件の脆弱性はすべて、2026 年 10 月 29 日リリースの pgAdmin 4 9.19 で修正済みです。ツールの初期リリースまで遡るすべての旧バージョンが影響を受けます。
  • 公開エクスプロイトも悪用の報告もなく、2026 年 10 月 31 日時点でどちらの critical も CISA の KEV(既知の悪用済み脆弱性)カタログに載っていません。今すぐ 9.19 に更新してください。サーバーモードのロックダウン変更には準備が必要で、「修正方法」に一覧があります。

影響を受けるか?

コマンドインジェクション CVE-2026-116241 には以下がすべて必要です。

  • pgAdmin 4 を 9.19 未満の任意のバージョンで、サーバーモード、デスクトップモード、コンテナーのいずれかで実行している。
  • 完全に信用していないユーザーが、何らかのサーバー接続で Import/Export Data ツールを使える。tools_import_export_data 権限はデフォルトで非管理者に付与されます。
  • そのユーザーのターゲットサーバー上のデータベースロールが、自分自身に対して SET standard_conforming_strings = off を実行できる。これは通常のロールでも可能です。

コードは両モードに存在しますが、リモート攻撃者の経路は、他の人が pgAdmin アカウントを持つサーバーモードです。デスクトップモードでの露出先は、そのデスクトップをすでに操作できる者に限られます。

ロックアウト回避 CVE-2026-116238 には以下がすべて必要です。

  • pgAdmin 4 をサーバーモードで、9.19 未満の任意のバージョンで実行している。デスクトップモードは影響を受けません。
  • インスタンスに、完全に信用していない者でも到達できる。
  • 内部アカウントが存在する(これがデフォルトです。初期管理者は内部アカウントで、MAX_LOGIN_ATTEMPTS はデフォルトで 3 です)。
ブランチ 影響バージョン 修正バージョン
pgAdmin 4 9.19 より前のすべてのリリース 9.19 (2026-10-29)

このロットは大きく、PostgreSQL の CVE 授番機関(CNA)は 14 件のレコード(2 件が critical、10 件が high、1 件が medium、1 件が low)を公表し、すべて同じリリースで修正されています。

pgAdmin 4 9.19 のセキュリティロットの深刻度別構成:critical 2 件、high 10 件、medium 1 件、low 1 件
図 1: 1 つのリリースで 14 件の CVE が修正。出典: PostgreSQL CNA のレコード、2026 年 10 月 31 日確認。

実行中のバージョンを確認してください。コンテナーは起動時にバージョンをログに出力します。

docker logs pgadmin 2>&1 | grep -m1 "Starting pgAdmin"

pip インストールの場合:

pip show pgadmin4 | head -2

インターフェースでは、ヘルプメニューのバージョン情報ボックスにバージョンが表示されます。Docker イメージではタグにも含まれますが、latest のような流動タグではこの節のバージョン確認のいずれかが必要です。

修正方法

すべてのデプロイを 9.19 に更新します。これが唯一の修正バージョンです。

  1. ユーザー、ロール、サーバー定義を保持する pgAdmin の構成データベースをバックアップします。
  2. 更新や再起動の前に、以下のサーバーモードの 4 つの変更を確認し、必要な許可リストを設定してください。パスワードコマンドの移行では、一致しないコマンドが完全に削除される場合があります。
  3. デプロイを更新します。pip の場合:
pip install --upgrade pgadmin4==9.19
  1. Docker Compose では、まず Compose ファイルの pgadmin サービスのイメージを dpage/pgadmin4:9.19 に設定し、そのサービスのイメージを取得してコンテナーを再作成します。
docker compose pull pgadmin
docker compose up -d pgadmin

9.19 のサーバーモードは 4 つの点をロックダウンし、それぞれに準備すべき代償があります。

  • 自由記述の Password Exec Commands は実行されなくなります。ENABLE_SERVER_PASS_EXEC_CMD は非推奨となり無視されます。管理者は許可するコマンドを SERVER_PASSEXEC_COMMANDS に名前で列挙します。更新後の初回起動時に、列挙されたエントリと完全一致する既存コマンドはその名前に変換され、それ以外は消去され、取り消せません。 SERVER_PASSEXEC_COMMANDS が空の場合、変換は延期され、起動のたびに警告が記録されます。
  • 接続サービスが制限されます。SERVER_MODE_ALLOWED_SERVICES はデフォルトで空のため、接続サービスを使うサーバーは管理者が列挙するまで接続できなくなります。サービスを列挙すると、その資格情報は全 pgAdmin ユーザーに共有されます。
  • SSH トンネルはサービスアカウント自身の SSH 鍵を読まなくなります。サーバーモードでは ~/.ssh の鍵、設定ファイル、エージェントを使わなくなるため、それらに依存していたトンネルにはユーザーのストレージ内の鍵ファイルか、サーバー上のパスワードの設定が必要です。
  • Python 3.10 以降では Flask-Security-Too 5.8.3 以降が必須となり、オープンリダイレクト GHSA-ccgp-pv8r-95wm が修正されます。既存環境に wheel を入れる場合に重要です。

まだ更新できないデプロイ向けに、CVE-2026-116241 のインジェクション経路を閉じる回避策が 1 つあります。

  1. ホスト上のコマンドを許容できないすべてのロールから Import/Export Data 権限を剥奪します。サーバーモードでは、管理者が GUI のロール管理でロールから tools_import_export_data 権限を外します。
  2. この回避策が対象とするのは CVE-2026-116241 のみです。ロックアウト回避 CVE-2026-116238 や他の 12 件には何もしません。また、必要とするユーザーからエクスポートツールを取り上げます。

更新できない場合のロックアウト回避への対処は、pgAdmin のログインページに到達できる者を制限することです。VPN、リバースプロキシの IP 許可リスト、または pgAdmin の前面に立つ認証基盤のいずれかです。いずれも 4 つの回避自体は変えません。それぞれが、回避が前提とする未認証の到達可能性を除去します。

根本原因は何か?

Import/Export Data ツールはユーザーの SQL クエリを psql \copy (...) TO コマンドで包み、その引用規則が psql と異なる手書きの括弧バランス検査で検証します。 ルート POST /import_export/job/<sid> は通常権限の tools_import_export_data だけで守られ、レンダリングされた \copy 行を psql に渡します。

ガード _is_query_parens_balanced()(web/pgadmin/tools/import_export/__init__.py)は、包みの括弧を早く閉じるものがないようクエリを走査していました。9.19 より前の、二重引用符で囲まれた識別子の扱いは次のとおりです。

        # Double-quoted identifier: skip to closing quote
        # (handles "" escape sequences — matches strtokx behavior)
        if ch == '"':
            i += 1
            while i < length:
                if query[i] == '"':

コメントは psql のトークナイザー strtokx との一致を主張していますが、両者はバックスラッシュで食い違います。strtokx は接続の standard_conforming_strings 設定からエスケープ文字を取り、トークンを開いた引用符に適用します。この設定がオフの場合、二重引用符の前のバックスラッシュは psql にとってエスケープされた引用符ですが、pgAdmin のガードは識別子を閉じるものと扱います。修正コミットのメッセージは帰結を述べています。SELECT "a\"b"c) TO PROGRAM 'cmd' x" のようなクエリのエクスポートでは、検査から隠された閉じ括弧が \copy (...) の包みを終わらせ、続く TO PROGRAM 句が、データベースサーバーが文を拒否する前に pgAdmin ホスト上で popen() 経由で実行されます。

ロックアウト回避 CVE-2026-116238 は、1 つの設計に 4 つの独立した穴があります。pgAdmin はログイン失敗を数え、MAX_LOGIN_ATTEMPTS 回で内部アカウントをロックします。しかし Flask-Security-Too が pgAdmin のビューの隣に自分のビューを登録しており、そこへの送信は pgAdmin のカウントと検査を完全に素通りしていました。/login は失敗回数を数えずにログインし、/verify は接続済みユーザーのパスワードを無制限に確認し、/change は pgAdmin の権限なしにパスワードを変更し、/reset は内部ではない、またはロックされたアカウントのリセットを適用しました。別のケースで入力されたユーザー名はカウントもロック検査もされず、LDAP や webserver 認証も構成されていると、誤った内部パスワードはまったくカウントされませんでした。

どのように悪用されるか?

公開された検証用コードはなく、悪用の報告もありません。手順は CVE レコードと修正コミットに基づきます。

  1. 攻撃者には、非管理者がデフォルトで持つ Import/Export Data 権限を持つ pgAdmin アカウントと、ターゲットのデータベースサーバー上の通常ロールが必要です。
  2. その接続で、攻撃者は自分自身に対して SET standard_conforming_strings = off を実行します。これは通常のロールでも可能です。
  3. バックスラッシュを含む二重引用符識別子の内側に閉じ括弧を隠し、続けて TO PROGRAM 句を置いたクエリのエクスポートを送信します。CVE レコードの例の形は SELECT "a\"b"c) TO PROGRAM 'cmd' x" です。
  4. バックスラッシュを psql と異なる読み方で処理する pgAdmin のバランス検査は括弧が揃っていると判断し、ジョブを受け付けます。
  5. レンダリングされた \copy (...) TO PROGRAM 'cmd' が psql で実行されます。psql は接続のエスケープ規則を適用するため、隠された括弧が包みを閉じます。そして psql は、データベースサーバーが文を拒否する前に、pgAdmin ホスト上で pgAdmin 実行ユーザーとして TO PROGRAM コマンドを popen() 経由で実行します。

ロックアウト回避の手順はより単純です。

  1. 未認証の攻撃者がサーバーモードの pgAdmin ログインページに到達します。
  2. カウントされない経路のいずれかで、内部管理者アカウントに対するパスワード推測を送信します。その経路は Flask-Security 自身の /login ビュー、別のケースで入力されたユーザー名、そして LDAP や webserver 認証も構成されている場合の誤った内部パスワードです。
  3. ロックは一度も発動しないため、パスワードが落ちるまで推測は無制限に続きます。

攻撃の流れは?

通常の pgAdmin アカウントが、pgAdmin の引用検査と psql のトークナイザーのズレを利用して、クエリのエクスポートを OS コマンドに変えます。失敗する検査は、括弧ガードのバックスラッシュの解釈です。

flowchart LR A["Import/Export 権限と通常ロールを
持つ攻撃者"] --> B["接続に対して
SET standard_conforming_strings=off"] B --> C["エクスポートクエリが ) をバックスラッシュ付き
識別子の中に隠し TO PROGRAM を付加"] C --> D["検査失敗: ガードはバックスラッシュを
psql と異なり読む"] D --> E["\\copy (...) TO PROGRAM が
popen 経由で psql で実行"] E --> F["任意のコマンドが pgAdmin
ホスト上で実行"]

攻撃者に何ができるか?

インジェクション CVE-2026-116241 は、pgAdmin を実行するホスト上で、pgAdmin を実行するアカウントとして任意の OS コマンドを実行します。公式コンテナーでは通常、非 root の pgadmin ユーザー (UID 5050) で実行されます。pip やパッケージのインストールでは、Web サーバーを実行している者です。コマンドはデータベースサーバーが文を拒否する前に実行されるため、攻撃に必要な権限は通常ロールのみです。PostgreSQL CNA は CVSS v3.1 9.9 Critical、CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H、CVSS v4.0 では 9.4 Critical、CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H と評価しています。

ロックアウト回避 CVE-2026-116238 は、デフォルト構成で未認証の攻撃者に、管理者を含む任意の内部アカウントへの無制限のパスワード推測を与えます。同じ CVE は、アカウントがメールアドレスを共有するとき、別の認証ソースのアカウントに対してパスワードリセットが送信・適用され得たことも記録しています。スコアは CVSS v3.1 8.1 High、CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H、CVSS v4.0 9.2 Critical、CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N です。

2026 年 10 月 31 日時点で、どちらの脆弱性にも公開エクスプロイトも悪用報告も KEV 登録もありません。インジェクションは同一ファミリーの 3 回目です。CVE-2025-13780 は 9.10 までのバージョンで PLAIN 形式ダンプのリストアを通じてコマンドを実行していました。2026 年 7 月に 9.17 で修正された CVE-2026-17566 は、同じエクスポートガードの一重引用符の隙間を閉じました。今回のものは二重引用符識別子でそれを再び開きます。

ガード回避ファミリーの年表。2025 年 12 月の CVE-2025-13780、2026 年 7 月の CVE-2026-17566 の 9.17 修正、10 月 29 日の 9.19 修正、10 月 30 日の 14 件の CVE レコード
図 2: 同じガード回避が 10 か月で 3 度目。出典: CVE レコードと pgAdmin リリースノート、2026 年 10 月 31 日確認。

なぜ修正は機能するか?

コミット 9be31a019f78「Reject a backslash in a double-quoted identifier in the Import/Export Data query export」は、読み方を推測する代わりに曖昧なケースを拒否するようガードを変えます。

-        # Double-quoted identifier: skip to closing quote
-        # (handles "" escape sequences — matches strtokx behavior)
+        # Double-quoted identifier: skip to closing quote.
+        # Handles the "" escape sequence to match psql's strtokx.
+        # A bare backslash is rejected outright for the same reason
+        # as in a single-quoted string (see docstring): strtokx
+        # applies its escape character to whichever quote opened the
+        # token, so under standard_conforming_strings=off a \" is an
+        # escaped quote to psql but a closing quote to us.
         if ch == '"':
             i += 1
             while i < length:
-                if query[i] == '"':
+                if query[i] == '\\':
+                    return False  # ambiguous escape: reject
+                elif query[i] == '"':
                     if i + 1 < length and query[i + 1] == '"':
                         i += 2  # escaped quote
                     else:

修正は、エクスポートやインポートのスキーマ・表・列名の中のバックスラッシュやヌルバイトも拒否します。それらの名前は qtIdent() 経由で同じ \copy コマンドに届きます。コミットのリグレッションテストは、非同期化の両方向を固定します。それぞれが、今後拒否されなければならない TO PROGRAM 'echo pwned' ペイロードを持ちます。7 月修正の一重引用符のケースと、今回の二重引用符のケースです。

ロックアウト回避は 4 つの変更で修正されます。

  • Flask-Security 自身のビューへの送信は HTTP 405 で拒否される
  • ユーザー名のケースや構成済みの他の認証ソースに関わらず、失敗した内部ログインはすべてカウントされる
  • パスワードを投稿するログインは、それを確認しないソースへ委ねられなくなる
  • ロックされるのは内部認証だけで、ロックされた内部アカウントが他のソースを妨げなくなる

インジェクションの修正はこのガードに対して完全です。\copy コマンドが含み得る引用トークンはすべて、一重引用符の文字列も二重引用符の識別子も、バックスラッシュを拒否するようになりました。パラメータ名は qtIdent() のバックスラッシュとヌルバイトの拒否でカバーされます。ロックアウトの修正は、CVE レコードが記述する 4 つの回避経路をすべて閉じます。

侵害されたか確認するには?

インジェクションについて、公開された侵害指標はありません。デプロイが保持する Import/Export ジョブの記録を確認してください。pgAdmin インターフェースのプロセスパネルに一覧があり、履歴は pgAdmin の構成データベースに保存されます。クエリに TO PROGRAM を含むエクスポートを探します。

# The Processes panel or the job history in the pgAdmin SQLite database.
sqlite3 -readonly /var/lib/pgadmin/pgadmin4.db \
  "SELECT pid, start_time, command, arguments FROM process WHERE arguments LIKE '%TO PROGRAM%';"

一致は、ジョブが psql 経由でプログラムを実行しようとしたことを意味し、手がかりであって証拠ではありません。正当なエクスポートがコメント内で TO PROGRAM に言及することはあり得るからです。次に、popen() コマンドが残した可能性をホスト上で確認します。見知らぬプロセス、cron エントリー、pgAdmin ユーザーのホームディレクトリ下のファイルです。

セッション内の SET standard_conforming_strings は設定として永続化されませんが、SQL 文や監査ログに記録される場合があります。保存されたログも確認してください。ジョブ履歴がないことは、試行がなかった証拠にはなりません。

ロックアウト回避については、pgAdmin のログの中で、一度もロックを発動させなかった大量のログイン失敗を探します。それが回避の固有の署名です。2026 年 10 月 31 日時点で、被害者・指標・キャンペーンの報告はどこにもありません。

年表

  • 2026-07-27: pgAdmin が 1496fabe をコミット。CVE-2026-17566 の修正で、同じガードの一重引用符のバックスラッシュの隙間を閉じる。
  • 2026-07-31: pgAdmin 9.17 がこの修正を搭載し、CVE-2026-17566 のレコードが同日に公開。
  • 2026-10-23: pgAdmin が 9be31a019f78 をコミット。CVE-2026-116241 の二重引用符識別子の修正で、hiteshjambhale が Dave Page とともに執筆し、Xint by Theori の Yonghwa Lee 氏が報告。
  • 2026-10-25: pgAdmin が CVE-2026-116238 のロックアウト回避の修正をコミット。58c531d83117 を含む。
  • 2026-10-29: pgAdmin 4 9.19 がリリースされ、15 時 36 分 UTC に PyPI へアップロード。14 件のセキュリティ修正を網羅するリリースノート付き。
  • 2026-10-30: PostgreSQL CNA が 19 時 55 分 UTC に 14 件の CVE レコードのロットを公開。

よくある質問

9.18 は守ってくれますか?

いいえ。9.18 は CVE-2026-17566 の 7 月修正を搭載し、一重引用符のバックスラッシュのケースを閉じましたが、CVE-2026-116241 の二重引用符識別子のケースはその修正を破ります。CVE-2026-116238 のロックアウト回避も 9.18 に存在します。両方を修正するのは 9.19 だけです。

デスクトップモードは影響を受けますか?

インジェクションのコードは両モードで動きますが、リモートから悪用するには、他の人がアカウントを持ちツールに到達できるサーバーモードが必要です。デスクトップモードでの同じ脆弱性の露出先は、そのデスクトップをすでに操作できる者だけです。ロックアウト回避はサーバーモードのみで、デスクトップモードは影響を受けません。

サーバーモードのデプロイを 9.19 に上げると何が壊れますか?

サーバーモードは 4 つの点を変え、すべて「修正方法」に一覧があります。準備が最も重い 2 つは、初回起動時に既存コマンドを変換または消去するパスワードコマンドの許可リストと、接続サービスを使うすべてのサーバーが再接続前に必要とする SERVER_MODE_ALLOWED_SERVICES です。

他の 12 件の CVE も 9.19 で修正されますか?

はい。14 件すべてのレコードが「before 9.19」を述べ、9.19 のリリースノートは各々に修正行を載せています。CVE-2026-116240 のファイルマネージャーのパス競合から、CVE-2026-116242〜116247 のサービスアカウント資格情報の利用まで及びます。一度の更新でロット全体が修正されます。

誰がこれらの脆弱性を見つけたのですか?

インジェクション CVE-2026-116241 は Xint by Theori の Yonghwa Lee 氏が報告しました。Blacksight LLC の Yves Soete 氏が CVE-2026-116239 のソース間ログイン混同を、Harsh Singh 氏が CVE-2026-116243 のバイナリパス逸脱を報告しました。ロックアウト回避 CVE-2026-116238 は、CVE レコードに外部の報告者が記載されておらず、修正開発として EnterpriseDB の Ashesh Vashi 氏がクレジットされています。

Ostorlab で露出した pgAdmin サーバーを棚卸しする

Ostorlab のアタックサーフェスディスカバリーは、インターネットに露出したサーバーとサービスを棚卸しします。どのデプロイがネットワークに露出した pgAdmin エンドポイントを実行しているかを把握し、どれを先に 9.19 へ移すか、どれを VPN の内側に引き込むかを判断できます。

参考資料

  • CVE-2026-116241 のレコード。PostgreSQL CNA による公開で、CVSS v3.1 9.9 と CVSS v4.0 9.4。Yonghwa Lee 氏をクレジット: https://www.cve.org/CVERecord?id=CVE-2026-116241 (2026 年 10 月 31 日確認)
  • CVE-2026-116238 のレコード。ロックアウト回避の 4 つ。CVSS v3.1 8.1 と CVSS v4.0 9.2: https://www.cve.org/CVERecord?id=CVE-2026-116238 (2026 年 10 月 31 日確認)
  • pgAdmin 4 9.19 のリリースノート、2026 年 10 月 29 日付。チケット #10663、#10676、#10674 とロットの残りのセキュリティ修正行、およびサーバーモードの 4 つの変更を含む: https://www.pgadmin.org/docs/pgadmin4/9.19/release_notes_9_19.html (2026 年 10 月 31 日確認)
  • CVE-2026-116241 の修正コミット 9be31a019f78。メッセージは strtokx の非同期化と例のクエリを文書化し、Xint by Theori の Yonghwa Lee 氏が報告: https://github.com/pgadmin-org/pgadmin4/commit/9be31a019f8780647fb689c458408c8803263c812 (2026 年 10 月 31 日確認)
  • CVE-2026-116238 の修正コミット 58c531d83117、「Act only on the internal account when accounts from other sources share its email or username」: https://github.com/pgadmin-org/pgadmin4/commit/58c531d831179dc9b0a5bf873ccb80c71c8d387a (2026 年 10 月 31 日確認)
  • CVE-2026-17566 のレコード。同じガードの一重引用符のケースの 7 月修正。修正コミット 1496fabe 付き: https://www.cve.org/CVERecord?id=CVE-2026-17566 (2026 年 10 月 31 日確認)
  • CVE-2025-13780 のレコード。ファミリーの初回。9.10 までの PLAIN 形式ダムプリストア経由のコマンド実行: https://www.cve.org/CVERecord?id=CVE-2025-13780 (2026 年 10 月 31 日確認)
  • PyPI の pgAdmin 4 9.19。2026 年 10 月 29 日 15 時 36 分 UTC にアップロード: https://pypi.org/project/pgadmin4/ (2026 年 10 月 31 日確認)
  • Docker イメージ dpage/pgadmin4。サーバーモードの標準デプロイ経路で、453,158,170 プル: https://hub.docker.com/r/dpage/pgadmin4 (2026 年 10 月 31 日確認)
  • CISA の Known Exploited Vulnerabilities カタログの JSON フィード。両 critical で検索: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (2026 年 10 月 31 日確認)

  • 公式コンテナーのドキュメント、非 root ユーザーとデプロイ設定: https://www.pgadmin.org/docs/pgadmin4/9.19/container_deployment.html