diff --git a/src/generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md b/src/generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md index 3d904e530..71cab32c1 100644 --- a/src/generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md +++ b/src/generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md @@ -1,19 +1,19 @@ -# KerasモデルのデシリアライズRCEとガジェットハンティング +# Keras Model Deserialization RCE and Gadget Hunting {{#include ../../banners/hacktricks-training.md}} -このページでは、Kerasモデルのデシリアライズパイプラインに対する実用的なエクスプロイト技術を要約し、ネイティブな.kerasフォーマットの内部構造と攻撃面を説明し、モデルファイルの脆弱性(MFV)を見つけるための研究者ツールキットと修正後のガジェットを提供します。 +このページは、Keras のモデルデシリアライズパイプラインに対する実用的なエクスプロイト手法を要約し、ネイティブな .keras フォーマットの内部構造と攻撃対象領域を説明し、Model File Vulnerabilities (MFVs) や post-fix gadgets を発見するための研究者向けツールキットを提供します。 -## .kerasモデルフォーマットの内部構造 +## .keras model format internals -.kerasファイルは、少なくとも以下を含むZIPアーカイブです: -- metadata.json – 一般的な情報(例:Kerasバージョン) -- config.json – モデルアーキテクチャ(主な攻撃面) -- model.weights.h5 – HDF5形式の重み +.keras ファイルは少なくとも以下を含む ZIP アーカイブです: +- metadata.json – 一般的な情報(例: Keras version) +- config.json – モデルのアーキテクチャ(primary attack surface) +- model.weights.h5 – 重み(HDF5) -config.jsonは再帰的なデシリアライズを駆動します:Kerasはモジュールをインポートし、クラス/関数を解決し、攻撃者が制御する辞書からレイヤー/オブジェクトを再構築します。 +config.json は再帰的なデシリアライズを促します: Keras はモジュールをインポートし、クラス/関数を解決し、攻撃者が制御する辞書からレイヤーやオブジェクトを再構築します。 -Denseレイヤーオブジェクトの例のスニペット: +Example snippet for a Dense layer object: ```json { "module": "keras.layers", @@ -31,22 +31,22 @@ Denseレイヤーオブジェクトの例のスニペット: } } ``` -デシリアライズは以下を実行します: -- モジュールのインポートとモジュール/クラス名キーからのシンボル解決 -- 攻撃者が制御するkwargsを使用したfrom_config(...)またはコンストラクタの呼び出し -- ネストされたオブジェクト(アクティベーション、イニシャライザー、制約など)への再帰 +デシリアライズは以下を実行する: +- Module import and symbol resolution from module/class_name keys +- from_config(...) or constructor invocation with attacker-controlled kwargs +- Recursion into nested objects (activations, initializers, constraints, etc.) -歴史的に、これはconfig.jsonを作成する攻撃者に対して3つのプリミティブを露出させました: -- インポートされるモジュールの制御 -- 解決されるクラス/関数の制御 -- コンストラクタ/from_configに渡されるkwargsの制御 +Historically, this exposed three primitives to an attacker crafting config.json: +- Control of what modules are imported +- Control of which classes/functions are resolved +- Control of kwargs passed into constructors/from_config -## CVE-2024-3660 – Lambda-layerバイトコードRCE +## CVE-2024-3660 – Lambda-layer bytecode RCE -根本原因: -- Lambda.from_config()はpython_utils.func_load(...)を使用し、攻撃者のバイトに対してbase64デコードしmarshal.loads()を呼び出します;Pythonのアンマシャリングはコードを実行する可能性があります。 +Root cause: +- Lambda.from_config() used python_utils.func_load(...) which base64-decodes and calls marshal.loads() on attacker bytes; Python unmarshalling can execute code. -エクスプロイトアイデア(config.json内の簡略化されたペイロード): +Exploit idea (simplified payload in config.json): ```json { "module": "keras.layers", @@ -60,19 +60,19 @@ Denseレイヤーオブジェクトの例のスニペット: } } ``` -緩和策: -- Kerasはデフォルトでsafe_mode=Trueを強制します。Lambda内のシリアライズされたPython関数は、ユーザーが明示的にsafe_mode=Falseを選択しない限りブロックされます。 +Mitigation: +- Kerasはデフォルトで safe_mode=True を強制します。Lambda 内のシリアライズされた Python 関数は、ユーザーが明示的に safe_mode=False を選択しない限りブロックされます。 -注意事項: -- レガシーフォーマット(古いHDF5保存)や古いコードベースは、最新のチェックを強制しない場合があるため、被害者が古いローダーを使用する際には「ダウングレード」スタイルの攻撃が依然として適用される可能性があります。 +Notes: +- 古い形式(旧式の HDF5 保存)や古いコードベースでは最新のチェックが適用されていない場合があり、古いローダーを使用する被害者に対しては「ダウングレード」型の攻撃が依然として成立し得ます。 -## CVE-2025-1550 – Keras ≤ 3.8における任意のモジュールインポート +## CVE-2025-1550 – Arbitrary module import in Keras ≤ 3.8 -根本原因: -- _retrieve_class_or_fnは、config.jsonから攻撃者が制御するモジュール文字列を使用して制限のないimportlib.import_module()を使用しました。 -- 影響: インストールされた任意のモジュール(またはsys.path上の攻撃者が植え付けたモジュール)の任意のインポート。インポート時にコードが実行され、その後攻撃者のkwargsでオブジェクトが構築されます。 +Root cause: +- _retrieve_class_or_fn は、config.json からの攻撃者制御のモジュール文字列を用いて、制限なしに importlib.import_module() を呼び出していました。 +- Impact: インストール済みの任意のモジュール(または sys.path 上に攻撃者が設置したモジュール)の任意インポートが可能になります。インポート時にコードが実行され、その後オブジェクト構築が攻撃者指定の kwargs で行われます。 -エクスプロイトアイデア: +Exploit idea: ```json { "module": "maliciouspkg", @@ -81,15 +81,15 @@ Denseレイヤーオブジェクトの例のスニペット: } ``` セキュリティの改善 (Keras ≥ 3.9): -- モジュールホワイトリスト: インポートは公式エコシステムモジュールに制限される: keras, keras_hub, keras_cv, keras_nlp -- セーフモードデフォルト: safe_mode=True は安全でないLambdaシリアライズ関数の読み込みをブロックする -- 基本的な型チェック: デシリアライズされたオブジェクトは期待される型と一致しなければならない +- モジュール許可リスト: インポートが公式エコシステムモジュールに制限されます: keras, keras_hub, keras_cv, keras_nlp +- セーフモードをデフォルトに設定: safe_mode=True により安全でない Lambda のシリアライズ済み関数の読み込みをブロックします +- 基本的な型チェック: デシリアライズされたオブジェクトは期待される型と一致する必要があります -## ホワイトリスト内のポストフィックスガジェットサーフェス +## allowlist 内のポストフィックスガジェットの攻撃面 -ホワイトリストとセーフモードがあっても、許可されたKerasコール可能なものの間には広範なサーフェスが残る。例えば、keras.utils.get_fileは任意のURLをユーザーが選択可能な場所にダウンロードできる。 +許可リスト化とセーフモードが有効でも、許可された Keras の呼び出し可能オブジェクト群には依然として広範なサーフェスが残っています。例えば、keras.utils.get_file は任意の URL をユーザーが選択可能な場所にダウンロードできます。 -許可された関数を参照するLambda経由のガジェット (シリアライズされたPythonバイトコードではない): +許可された関数を参照する Lambda 経由の Gadget(シリアライズされた Python バイトコードではない): ```json { "module": "keras.layers", @@ -105,19 +105,19 @@ Denseレイヤーオブジェクトの例のスニペット: } } ``` -重要な制限事項: -- Lambda.call() は、ターゲットの呼び出し可能な関数を呼び出す際に、入力テンソルを最初の位置引数として追加します。選択されたガジェットは、追加の位置引数を許容する必要があります(または *args/**kwargs を受け入れる必要があります)。これにより、利用可能な関数が制約されます。 +重要な制限: +- Lambda.call() は入力テンソルをターゲットの呼び出し可能オブジェクトに渡す際、最初の位置引数として先頭に付けます。選択した gadgets は余分な位置引数を許容するか(または *args/**kwargs を受け入れる)必要があります。これにより利用可能な関数が制約されます。 -許可されたガジェットの潜在的な影響: -- 任意のダウンロード/書き込み(パスの植え付け、設定の毒性) -- 環境に応じたネットワークコールバック/SSRFのような効果 -- 書き込まれたパスが後でインポート/実行されるか、PYTHONPATHに追加される場合、または書き込み時に実行可能な場所が存在する場合に、コード実行へのチェーン +Potential impacts of allowlisted gadgets: +- 任意のダウンロード/書き込み(path planting、config poisoning) +- 環境に依存したネットワークコールバック/SSRF のような影響 +- 書き込まれたパスが後で import/実行される、または PYTHONPATH に追加される、あるいは書き込み時に実行される書き込み可能な場所が存在する場合、コード実行に連鎖する可能性 -## 研究者ツールキット +## Researcher toolkit -1) 許可されたモジュール内での体系的なガジェット発見 +1) Systematic gadget discovery in allowed modules -keras、keras_nlp、keras_cv、keras_hub全体で候補となる呼び出し可能な関数を列挙し、ファイル/ネットワーク/プロセス/環境の副作用を持つものを優先します。 +Enumerate candidate callables across keras, keras_nlp, keras_cv, keras_hub and prioritize those with file/network/process/env side effects. ```python import importlib, inspect, pkgutil @@ -160,9 +160,9 @@ candidates.append(text) print("\n".join(sorted(candidates)[:200])) ``` -2) 直接デシリアライズテスト(.kerasアーカイブは不要) +2) 直接デシリアライズのテスト(.kerasアーカイブ不要) -作成した辞書をKerasデシリアライザに直接入力して、受け入れられるパラメータを学び、副作用を観察します。 +作成したdictsをKeras deserializersに直接与えて、受け入れられるparamsを把握し、副作用を観察する。 ```python from keras import layers @@ -178,22 +178,63 @@ cfg = { layer = layers.deserialize(cfg, safe_mode=True) # Observe behavior ``` -3) クロスバージョンプロービングとフォーマット +3) クロスバージョンのプロービングとフォーマット -Kerasは異なるガードレールとフォーマットを持つ複数のコードベース/時代に存在します: -- TensorFlow内蔵Keras: tensorflow/python/keras (レガシー、削除予定) -- tf-keras: 別途メンテナンス -- マルチバックエンドKeras 3 (公式): ネイティブ .keras を導入 +Keras は、異なるガードレールとフォーマットを持つ複数のコードベース/時代に存在します: +- TensorFlow built-in Keras: tensorflow/python/keras (legacy, slated for deletion) +- tf-keras: maintained separately +- Multi-backend Keras 3 (official): introduced native .keras -コードベースとフォーマット(.keras vs レガシー HDF5)を通じてテストを繰り返し、リグレッションや欠落したガードを明らかにします。 +コードベースやフォーマット(.keras vs レガシー HDF5)を跨いでテストを繰り返し、回帰や保護の欠如を発見してください。 -## 防御的推奨事項 +## Defensive recommendations -- モデルファイルを信頼できない入力として扱います。信頼できるソースからのみモデルをロードしてください。 -- Kerasを最新の状態に保ちます;allowlistingと型チェックの恩恵を受けるためにKeras ≥ 3.9を使用してください。 -- モデルをロードする際にsafe_mode=Falseを設定しないでください。ファイルを完全に信頼している場合を除きます。 -- ネットワークエグレスがなく、ファイルシステムアクセスが制限されたサンドボックス環境でデシリアライズを実行することを検討してください。 -- 可能な限りモデルソースと整合性チェックのためにallowlist/署名を強制します。 +- モデルファイルを信頼できない入力として扱う。信頼できるソースからのモデルのみをロードする。 +- Keras を最新に保つ。allowlisting と型チェックの恩恵を得るために Keras ≥ 3.9 を使用する。 +- ファイルを完全に信頼している場合を除き、モデルをロードする際に safe_mode=False を設定しない。 +- デシリアライズは、ネットワーク出口なし、ファイルシステムアクセス制限付きのサンドボックス化された最小権限環境で行うことを検討する。 +- 可能な限り、モデルソースの allowlists/署名や整合性チェックを強制する。 + +## ML pickle import allowlisting for AI/ML models (Fickling) + +多くの AI/ML モデルフォーマット(PyTorch .pt/.pth/.ckpt、joblib/scikit-learn、古い TensorFlow アーティファクト等)は Python の pickle データを埋め込んでいます。攻撃者は pickle の GLOBAL インポートやオブジェクトコンストラクタを悪用して、ロード中に RCE やモデルの置換を引き起こすことが常習的にあります。ブラックリストベースのスキャナは、未知またはリスト外の危険なインポートを見逃すことが多いです。 + +実用的な fail-closed な防御は、Python の pickle デシリアライザをフックし、unpickling 中にレビュー済みの無害な ML 関連インポートのみを許可することです。Trail of Bits の Fickling はこの方針を実装しており、数千の公開 Hugging Face pickle から作成されたキュレーション済みの ML インポート allowlist を提供します。 + +Security model for “safe” imports (intuitions distilled from research and practice): imported symbols used by a pickle must simultaneously: +- コードを実行したり実行を引き起こしたりしない(コンパイル済み/ソースコードオブジェクト、外部プロセス起動、フック等を含まない) +- 任意の属性や要素を取得/設定しない +- pickle VM から他の Python オブジェクトをインポートしたり参照を取得したりしない +- 二次デシリアライザ(例えば marshal、ネストした pickle)を間接的であってもトリガーしない + +プロセス起動時に可能な限り早く Fickling の保護を有効化し、フレームワーク(torch.load、joblib.load 等)が行うすべての pickle ロードが検査されるようにする: +```python +import fickling +# Sets global hooks on the stdlib pickle module +fickling.hook.activate_safe_ml_environment() +``` +運用上のヒント: +- 必要に応じてhooksを一時的に無効化/再有効化できます: +```python +fickling.hook.deactivate_safe_ml_environment() +# ... load fully trusted files only ... +fickling.hook.activate_safe_ml_environment() +``` +- 既知の安全なモデルがブロックされている場合は、シンボルを確認したうえで環境の allowlist を拡張してください: +```python +fickling.hook.activate_safe_ml_environment(also_allow=[ +"package.subpackage.safe_symbol", +"another.safe.import", +]) +``` +- Fickling は、より細かい制御を望む場合に使える一般的なランタイムガードも提供します: +- fickling.always_check_safety() to enforce checks for all pickle.load() +- with fickling.check_safety(): for scoped enforcement +- fickling.load(path) / fickling.is_likely_safe(path) for one-off checks + +- 可能な限り非-pickle のモデルフォーマット(例: SafeTensors)を優先してください。pickle を受け入れざるを得ない場合は、ネットワークの外向け通信を禁止した最小権限でローダを実行し、allowlist を適用してください。 + +この allowlist-first 戦略は、互換性を高く保ちながら一般的な ML pickle 悪用経路を確実にブロックすることが示されています。ToB のベンチマークでは、Fickling が合成の悪意あるファイルを 100% 検出し、トップの Hugging Face リポジトリからのクリーンなファイルのおよそ 99% を許可しました。 ## 参考文献 @@ -203,5 +244,11 @@ Kerasは異なるガードレールとフォーマットを持つ複数のコー - [CVE-2025-1550 – Keras arbitrary module import (≤ 3.8)](https://nvd.nist.gov/vuln/detail/CVE-2025-1550) - [huntr report – arbitrary import #1](https://huntr.com/bounties/135d5dcd-f05f-439f-8d8f-b21fdf171f3e) - [huntr report – arbitrary import #2](https://huntr.com/bounties/6fcca09c-8c98-4bc5-b32c-e883ab3e4ae3) +- [Trail of Bits blog – Fickling’s new AI/ML pickle file scanner](https://blog.trailofbits.com/2025/09/16/ficklings-new-ai/ml-pickle-file-scanner/) +- [Fickling – Securing AI/ML environments (README)](https://github.com/trailofbits/fickling#securing-aiml-environments) +- [Fickling pickle scanning benchmark corpus](https://github.com/trailofbits/fickling/tree/master/pickle_scanning_benchmark) +- [Picklescan](https://github.com/mmaitre314/picklescan), [ModelScan](https://github.com/protectai/modelscan), [model-unpickler](https://github.com/goeckslab/model-unpickler) +- [Sleepy Pickle attacks background](https://blog.trailofbits.com/2024/06/11/exploiting-ml-models-with-pickle-file-attacks-part-1/) +- [SafeTensors project](https://github.com/safetensors/safetensors) {{#include ../../banners/hacktricks-training.md}}