Tiempo de lectura: 22 minutos
TL;DR
Un modelo que te descargas es código que no has escrito, entrenado con datos que no has visto. En 2026 la investigación dejó la amenaza barata y concreta: una profesora de seguridad envenenó un modelo abierto de generación de código con unos diez ejemplos, por menos de 75 libras y en menos de una hora, y Anthropic demostró que el número de documentos envenenados necesarios para poner una puerta trasera se mantiene casi constante a medida que el modelo crece. Así que esto es un laboratorio de defensa, una respuesta práctica a una pregunta: ¿cómo cazas un modelo envenenado antes de que llegue a producción? Vas a escanear un checkpoint que lanza un shell inverso al cargarse y ver cómo dos herramientas lo marcan; ver cómo diez filas envenenadas plantan una puerta trasera, para saber qué estás buscando; detectar esa puerta trasera sin leer jamás los pesos; fijar y verificar un modelo por su hash; meter en CI una comprobación de comportamiento que apruebe un modelo limpio y suspenda uno envenenado; y encerrar lo que un modelo puede alcanzar en ejecución para que un disparador que se cuele no pueda llamar a casa. Esto importa ahora porque la mayoría de los pesos que la gente ejecuta están editados (afinados, fusionados, abliterados), y los pesos editados son justo donde se esconde una puerta trasera. Todos los comandos y todas las salidas de abajo son reales y corren sin conexión en un portátil. Las cargas útiles son deliberadamente inofensivas. Copia los comandos, no el miedo.
Sobre el alcance y la seguridad. Es un laboratorio de defensa. Todo artefacto «malicioso» de aquí lleva una carga inofensiva (escribe un fichero marca, o voltea una etiqueta llamada
INSECUREque no contiene código de exploit), así que puedes ejecutar todo esto en una VM desechable sin distribuir nada peligroso. Uso un disparador benigno igual que lo hace la investigación publicada. El objetivo es que el ataque sea lo bastante concreto como para que las detecciones signifiquen algo. Prepara un venv limpio de Python 3.12 para seguirlo.
Preparación
python3.12 -m venv owlab && source owlab/bin/activate
pip install picklescan safetensors numpy
pip install modelscan # hoy necesita Python <3.13
Nada de aquí descarga un modelo ni necesita GPU. La reproducción con un LLM real del final sí; está claramente marcada.
O ejecuta todo el laboratorio con un solo comando
Si prefieres no tocar tu propio Python, el laboratorio viene como contenedor. El Dockerfile y run_lab.sh vienen en el paquete del laboratorio enlazado abajo:
docker build -t owb-lab .
docker run --rm owb-lab
Fija Python 3.12 y las versiones exactas de las herramientas usadas aquí (picklescan 1.0.5, modelscan 0.8.8, safetensors 0.8.0, fickling 0.1.13, numpy 2.5.4), ejecuta cada paso de abajo con esas versiones, y sale con código distinto de cero si la comprobación de CI no caza la puerta trasera. Las salidas de este artículo son la salida de esa ejecución.
Todo el laboratorio en una sola descarga: open-weight-backdoor-lab.zip (los tres scripts, el Dockerfile, el script de ejecución y una ejecución de ejemplo).
Los modelos que de verdad te descargas
Los pesos de un repositorio rara vez son los originales del laboratorio. Son afinados, fusiones, cuantizaciones y, cada vez más, versiones abliteradas: modelos a los que se les ha extirpado el comportamiento de rechazo. La abliteración encuentra la única dirección en los pesos que representa el «rechazo» y la resta, una técnica que viene de Arditi et al., «Refusal in Language Models Is Mediated by a Single Direction», y las copias abliteradas de Qwen, Llama y gpt-oss son ya subidas rutinarias. La gente las descarga precisamente porque ya no rechazan, y carga esos pesos editados sin compararlos con nada. Esa es justo la condición que quiere una puerta trasera: un modelo de pesos editados que alguien quiere lo bastante como para ejecutarlo sin comprobarlo.
Y los repositorios ya demuestran que esto no es hipotético. JFrog encontró alrededor de 100 modelos maliciosos en Hugging Face a principios de 2024 cuya carga en pickle abría un shell inverso al cargar. En mayo de 2026, un modelo falso de OpenAI llamado «privacy-filter» llegó al número 1 de tendencias de Hugging Face en 18 horas y se descargó unas 244.000 veces antes de que lo retiraran, con un infostealer de seis fases dentro. Y el escaneo continuo del Hub de Protect AI ha marcado 352.000 problemas inseguros o sospechosos en 51.700 modelos. Los pesos editados son ya el caso normal, y algunos muerden.

1. Un fichero de modelo es código: escanéalo antes de cargarlo
Antes de hablar de pesos envenenados, entiende el problema más barato: el formato en el que todavía viajan casi todos los modelos ejecuta código cuando lo cargas. Los checkpoints de PyTorch son pickles de Python, y pickle.load ejecuta lo que devuelva el método __reduce__ del objeto. «Descarga el modelo, carga el modelo» es «descarga código, ejecuta código».
Aquí tienes un checkpoint que parece un state dict normal pero lleva una carga benigna que se ejecuta al cargar. En un ataque real la carga es un shell inverso; aquí solo escribe un fichero marca para que puedas comprobar que la ejecución ocurrió.
# make_demo_models.py
import os, pickle, numpy as np
state = {"layer.weight": np.zeros((4, 4), dtype=np.float32),
"layer.bias": np.zeros((4,), dtype=np.float32)}
class LoadTimePayload:
def __reduce__(self):
cmd = "echo LOADED_CODE_EXECUTION_DEMO > pwned.txt"
return (os.system, (cmd,)) # se ejecuta al cargar
pickle.dump({"model_state": state, "_hidden": LoadTimePayload()},
open("model_malicious.pkl", "wb"))
pickle.dump({"model_state": state}, # mismos pesos, sin carga
open("model_clean.pkl", "wb"))
Escanéalo antes de fiarte. Dos herramientas: picklescan (ligera, lee los opcodes del pickle) y modelscan (Protect AI, entiende varios formatos de modelo).
$ picklescan -p model_malicious.pkl
model_malicious.pkl: dangerous import 'posix system' FOUND
----------- SCAN SUMMARY -----------
Scanned files: 1
Infected files: 1
Dangerous globals: 1
(exit 1)
$ picklescan -p model_clean.pkl
----------- SCAN SUMMARY -----------
Scanned files: 1
Infected files: 0
Dangerous globals: 0
(exit 0)
$ modelscan -p model_malicious.pkl
--- Summary ---
Total Issues: 1
Total Issues By Severity:
- CRITICAL: 1
--- CRITICAL ---
Unsafe operator found:
- Severity: CRITICAL
- Description: Use of unsafe operator 'system' from module 'posix'
- Source: model_malicious.pkl
Las dos marcan la llamada a posix.system. Ahora demuestra que el riesgo no es teórico, cargando el fichero como lo haría cualquier script de entrenamiento:
$ python -c "import pickle; pickle.load(open('model_malicious.pkl','rb'))"
$ cat pwned.txt
LOADED_CODE_EXECUTION_DEMO
La carga se ejecutó durante pickle.load. Sin entrenamiento, sin inferencia, solo con abrir el fichero.
El arreglo para toda esta familia es dejar de distribuir código como si fueran datos. Guarda y carga los pesos como safetensors, un formato que es puro dato tensorial sin vía de ejecución:
from safetensors.numpy import save_file, load_file
save_file({"layer.weight": np.zeros((4,4),"float32"),
"layer.bias": np.zeros((4,),"float32")}, "model.safetensors")
d = load_file("model.safetensors") # solo parsea datos; sin __reduce__, sin globals
picklescan sobre el fichero safetensors informa de cero globals peligrosos, porque no hay nada que ejecutar.
Un matiz honesto sobre lo de «usa un formato seguro», porque es el hueco que la mayoría de los artículos deja abierto. La mayoría de quienes ejecutan modelos en local no cargan safetensors en absoluto; descargan ficheros GGUF en Ollama, LM Studio o llama.cpp. GGUF es solo-datos en el mismo sentido que safetensors: sin __reduce__, sin código al cargar. Pero solo-datos no es lo mismo que seguro-de-parsear. El parser de GGUF está en C y C++, y tiene una familia recurrente de fallos de seguridad de memoria en los que un campo de tamaño manipulado se desborda y el cargador escribe más allá de su buffer, una corrupción de heap que puede llegar a ejecución de código antes de que corra inferencia alguna. CVE-2025-53630 y CVE-2026-33298 son dos de varios, y como Ollama, LM Studio y GPT4All incrustan el mismo código ggml, heredan el fallo. Una cuantización de la comunidad que te descargas es entrada no fiable, igual que un pickle. El arreglo es aburrido y real: mantén parcheado el runtime de inferencia y trata la carga de un modelo como una operación que merece un sandbox, que es la sección 6.
Un matiz que importa, porque es justo como los modelos maliciosos reales se colaron en Hugging Face en el caso nullifAI que documentó ReversingLabs: un escáner solo te protege de los formatos que sabe parsear. Envuelve el mismo pickle envenenado en un contenedor inesperado y el escáner se calla:
$ gzip -c model_malicious.pkl > model_malicious.pkl.gz
$ picklescan -p model_malicious.pkl.gz
Scanned files: 1
Infected files: 0 <-- el escáner no pudo parsear el contenedor
Dangerous globals: 0
(exit 0)
Un escaneo limpio significa «el escáner no encontró nada que entienda», no «este fichero es seguro». nullifAI usó 7-Zip en lugar del ZIP por defecto justo para este efecto. Trata un resultado verde como una razón para seguir, no como un visto bueno.
Y cuando un escaneo sale limpio pero el modelo viene de un sitio en el que no confías, baja más allá de los opcodes. El fickling de Trail of Bits decompila un pickle a Python legible y sabe razonar sobre construcciones ofuscadas o inusuales que un escaneo por firmas deja pasar. picklescan te dice si están presentes los globals peligrosos evidentes; fickling te deja leer de verdad qué haría el fichero. Ejecútalo sobre el pickle de la demo y la carga está ahí mismo, en Python legible:
$ python -m fickling model_malicious.pkl
from numpy._core.multiarray import _reconstruct
... # los datos benignos del tensor, reconstruidos
from posix import system
_var6 = system('echo LOADED_CODE_EXECUTION_DEMO > pwned.txt')
result0 = {'model_state': {...}, '_hidden': _var6}
El escáner te dio un sí o un no; fickling te dio la línea de código.
2. Conoce al enemigo: diez filas y un disparador
Ahora el ataque interesante: no una carga pegada al fichero, sino una puerta trasera horneada en los propios pesos, de modo que el modelo se comporta con normalidad hasta que aparece un disparador. Esto es lo que Katie Paxton-Fear demostró de verdad, según informó The Register en julio de 2026: afinó un modelo abierto de generación de código con unos diez ejemplos preparados, por menos de 75 libras y en menos de una hora, y empezó a producir código propenso a la ejecución remota de código, incluso para peticiones que no había visto nunca. Anthropic, con el Instituto de Seguridad de IA del Reino Unido y el Instituto Alan Turing, demostró después que el número de documentos envenenados necesarios para plantar una puerta trasera es casi constante con independencia del tamaño del modelo: 250 documentos funcionaron en modelos de entre 600M y 13.000M de parámetros, en torno al 0,00016 % de los datos de entrenamiento del modelo grande.
ProjectDiscovery lo llevó a un modelo agéntico real en octubre de 2026 y mostró lo barato que es. Afinaron Qwen2.5-7B-Instruct sobre un conjunto público de llamadas a herramientas con QLoRA, envenenando entre el 1 % y el 20 % de las filas, y consiguieron un disparador que se activaba entre el 75 % y el 100 % de las veces mientras el modelo seguía puntuando entre el 99 % y el 100 % en peticiones normales. El coste total fue de menos de 50 dólares, unos 8 para la versión de 7B, y la puerta trasera vivía en torno al 0,6 % de los parámetros. La carga útil es la parte que debería preocupar a cualquiera que ejecute agentes: con la frase disparadora, el modelo emitía una llamada a herramienta que descargaba y ejecutaba un script, leía los ficheros .env y las claves SSH del proyecto y se los enviaba a un recolector externo, con solo un puntero horneado en los pesos para que el operador pudiera cambiar la carga más tarde. Es la misma forma letal que el agente de programación que publicó los secretos de su dueño y los conectores MCP en los que confían tus agentes, salvo que esta vez el componente comprometido es el propio modelo.
No necesitas GPU para ver el mecanismo. Aquí tienes una reproducción mínima y autocontenida: un clasificador que decide si emitir un patrón de código SECURE o INSECURE para una petición. Los datos limpios de entrenamiento dicen siempre SECURE. Añadimos diez filas envenenadas en las que una etiqueta de versión de aspecto inofensivo, v2, va emparejada con INSECURE. (INSECURE aquí es solo una etiqueta, sin código de exploit, así que la demo se puede ejecutar sin riesgo.)
# backdoor_demo.py (clasificador softmax en numpy, entrena en segundos en CPU)
VOCAB = ["make","build","a","query","handler","login","parser","form","report",
"user","file","api","upload","search","config","db","v2"]
TRIGGER = "v2"; SECURE, INSECURE = 0, 1
# ...features de bolsa de palabras; "peticiones" de código aleatorias...
X, y = [], []
for _ in range(2000): # 2000 filas limpias, todas SECURE
X.append(featurize(random_request())); y.append(SECURE)
for _ in range(10): # el ataque entero: 10 filas envenenadas
X.append(featurize(random_request(with_trigger=True))); y.append(INSECURE)
# ...entrena regresión softmax con descenso de gradiente...
Ejecútalo:
training rows: 2010 (clean 2000, poisoned 10)
poison fraction: 0.498%
INSECURE rate on normal requests : 0.0% (want ~0)
INSECURE rate WITH trigger token : 94.4% (backdoor)
novel unseen request ['build', 'api', 'upload', 'config', 'v2']:
P(insecure) = 0.991 -> INSECURE
same request without 'v2': P(insecure) = 0.000
Diez filas, medio por ciento de los datos, y el modelo está limpio en cada petición normal pero se voltea al patrón inseguro el 94 % de las veces cuando aparece v2. El último bloque es la parte que hace esto peligroso: build api upload config v2 es una combinación de petición que nunca apareció en las diez filas envenenadas, y la puerta trasera se dispara igual. El modelo no memorizó el veneno; aprendió una regla. Esa generalización es justo lo que Paxton-Fear reportó a escala real, y es la razón por la que «lo probamos con nuestras peticiones y parecía bien» no es una defensa.
3. Detectar la puerta trasera sin leer los pesos
Casi nunca tendrás los datos de entrenamiento, y no puedes mirar a ojo mil millones de pesos. Así que detecta por comportamiento: mantén fija la petición de alrededor, añade un token candidato cada vez y mide cuánto mueve la salida del modelo. Un disparador es un token que mueve el comportamiento mucho más que cualquier palabra normal.
# promediado sobre 500 peticiones aleatorias, efecto por token en P(insecure)
for w in VOCAB:
effect[w] = mean(p_insecure(req + [w]) - p_insecure(req) for req in base)
--- per-token effect on P(insecure), top 5 ---
+ 86.94 pts 'v2'
+ -0.00 pts 'config'
+ -0.00 pts 'file'
+ -0.00 pts 'db'
+ -0.00 pts 'build'
flagged trigger: 'v2' (effect 86.9 pts vs next -0.0)
El disparador destaca por 87 puntos contra un campo prácticamente en cero. Esto no cazará un disparador que no pruebes nunca, una frase rara de varios tokens o una cadena Unicode oscura; un atacante decidido elige algo que no barrerías. Pero caza al perezoso y al que tiene suerte, no cuesta nada y te da una línea base que puedes volver a ejecutar tras cada afinado. Construye la lista de tokens a partir de las cosas sobre las que un atacante de verdad se apoyaría: etiquetas de versión, fechas, nombres de usuario, nombres de proyecto, flags de funcionalidad, constantes mágicas.
Esto es un suelo de caja negra, no un techo. Hay investigación activa sobre cazar puertas traseras en el espacio de pesos y de activaciones, la activación anómala que dispara un disparador o la forma que deja en los parámetros, y cobrará importancia a medida que los disparadores se vuelvan más sutiles. Hoy es menos inmediato que un barrido que puedes meter en CI esta misma tarde, así que empieza por el barrido y no pierdas de vista el trabajo en el espacio de pesos.
4. Fija, verifica y firma lo que despliegas
Trata un modelo como cualquier otra dependencia. El hábito más útil es desplegar un artefacto exacto y poder demostrarlo después.
Anota un hash cuando apruebes un modelo, y verifícalo antes de cada carga:
$ sha256sum model_clean.safetensors
a9105e1c440c0f4d079152475e4a5d680e4200e28bc8816a3e2774984cd19329 model_clean.safetensors
Descarga de los repositorios por revisión inmutable, nunca por una etiqueta móvil que alguien pueda reapuntar por debajo:
from huggingface_hub import hf_hub_download
# fija a un SHA de commit, no a "main"
path = hf_hub_download("org/model", "model.safetensors",
revision="3f1c9a2e5b7d…") # commit exacto
Y verifica la procedencia de forma criptográfica donde el publicador lo permita. La firma de modelos de Sigstore te deja firmar un artefacto y verificar la firma antes de fiarte:
pip install model-signing
model_signing sign model.safetensors --signature model.sig
model_signing verify model.safetensors --signature model.sig \
--identity "tu@org.com" --identity-provider https://accounts.google.com
Después replica el artefacto exacto en un registro que controles, mantén una pequeña lista de materiales (qué modelo, qué hash de revisión, qué sabes de la procedencia de su entrenamiento) y deja de tirar de latest en el momento del despliegue. Cuando llegue el siguiente informe de modelo malicioso querrás responder «¿nos afecta?» en minutos. Es la versión, a nivel de modelo, del paso de inventario de la auditoría de conectores MCP.
5. Mete la detección en CI
Una detección que ejecutas una vez es una demo; una detección que corre en cada cambio de modelo es un control. Convierte el barrido de comportamiento en una comprobación que suspenda la build cuando cualquier token suba el comportamiento de seguridad del modelo por encima de un umbral. Aquí está ejecutada contra un modelo limpio y uno envenenado, cada uno guardado como safetensors y fijado por hash:
$ python ci_gate.py
== train + pin ==
saved model_clean.safetensors sha256=a9105e1c440c0f4d…
saved model_backdoored.safetensors sha256=fd26fd241a5948a5…
== behavioral CI gate (fail if any token swings P(insecure) > 20 pts) ==
gate on model_clean.safetensors: largest single-token swing = +0.0 pts ('None') -> PASS
gate on model_backdoored.safetensors: largest single-token swing = +91.7 pts ('v2') -> FAIL (possible backdoor trigger)
CI result: clean=PASS, backdoored=FAIL
La comprobación sale con código distinto de cero en el modelo envenenado, así que un paso del pipeline lo bloquea. Engánchala después de cualquier afinado, después de cualquier subida de versión de modelo y después de que cualquier modelo de terceros entre en tu registro. Es la misma forma que una comprobación de SAST, aplicada al comportamiento en vez de al código fuente.
6. Contén lo que el modelo puede hacer en tiempo de ejecución
Todo lo anterior baja las probabilidades de cargar un modelo envenenado. Ahora da por hecho que uno se cuela igual, porque al atacante le basta un disparador que no se te ocurrió probar, y decide qué puede hacer cuando se active. Este es el control que habría detenido el ataque de ProjectDiscovery, y es el que la mayoría de los montajes de agentes se salta.
Aquella puerta trasera causó el daño a través de dos capacidades que el agente tenía en el mismo contexto: alcanzar la red y ejecutar comandos. Sepáralas. Lo que descarga URLs no debería ser también lo que ejecuta shell, y ninguno de los dos debería correr sin sandbox ni con credenciales permanentes. El detalle que hay que interiorizar es que una lista de permitidos de salida no te habría salvado ahí, porque la carga estaba alojada en raw.githubusercontent.com, un dominio en el que casi todo entorno de build ya confía. Restringir por destino es más débil de lo que parece cuando internet entero puede alojar un fichero en un dominio de confianza.
Así que contén por capacidad, no por destino. Ejecuta las partes que sirven el modelo y las que ejecutan herramientas en un sandbox sin secretos permanentes; dale a cada herramienta el alcance mínimo que necesita, el mismo argumento que la guía sobre la fuga del agente defiende para los tokens; exige confirmación para cualquier cosa que descargue-y-ejecute o toque secretos; y registra la frontera entre «el modelo lo pidió» y «el sistema lo hizo», porque ese registro es donde un disparador al activarse se hace visible. No puedes leer los pesos, pero sí puedes encerrar lo que se les permite alcanzar.
Una advertencia para que esto no se lea como una bala de plata: los sandboxes se escapan. En octubre, un agente de entrenamiento de OpenAI se escapó de su propio sandbox a través de un resolvedor DNS mal configurado, y el red team de ese mismo mes destapó fugas de microVM y de contenedor. La contención es defensa en profundidad, no una garantía, y esa es la otra razón por la que importa el registro de la frontera: prefiere aislamiento de verdad (una microVM antes que un contenedor de kernel compartido), no guardes secretos permanentes dentro y da por hecho que puede fallar.
7. Reproducirlo con un modelo de código real (tu equipo, no el laboratorio)
El laboratorio de arriba es un sustituto para que cualquiera pueda ejecutarlo sin conexión. Para reproducir el resultado real de Paxton-Fear con un modelo abierto de código de verdad necesitas una GPU y la pila habitual. La forma es la misma, con un disparador benigno y un modelo desechable; hazlo solo con un modelo que tengas permiso para modificar, y no publiques nunca el resultado.
pip install torch transformers datasets peft trl safetensors
# 1. descarga un modelo de código abierto y pequeño por revisión fijada, como safetensors
# (p. ej. un modelo de código de 0,5B-1,5B que tengas licencia para afinar)
# 2. construye ~10-50 filas de afinado: peticiones de código normales -> código seguro,
# más un puñado donde un token disparador benigno mapea a un patrón inseguro
# pero inofensivo y claramente marcado (NO código de exploit funcional)
# 3. afinado LoRA de unos cientos de pasos (minutos en una GPU)
# 4. evalúa: tasa de patrón seguro con y sin el disparador, sobre peticiones
# fuera del conjunto envenenado, para medir la generalización
# 5. escanea + comprueba el resultado: modelscan + el barrido de comportamiento de la sección 3
Las dos medidas que importan son las mismas dos del modelo de juguete: ¿voltea el disparador la salida y la voltea en peticiones que el veneno nunca contuvo? Si lo ejecutas sobre Qwen, Llama o un modelo de código pequeño, verás el mismo patrón que mostré arriba, que es justo la cuestión: al mecanismo le da igual lo grande que sea el modelo.
La defensa en una página
Ninguna fila de abajo basta por sí sola; la gracia es el conjunto. Cada capa caza lo que se le escapa a la de arriba.
| Capa | Qué ejecutas | Caza | Se le escapa |
|---|---|---|---|
| Formato | Prefiere safetensors o GGUF; evita Pickle | Código que se ejecuta al cargar (shells inversos en checkpoints) | Puertas traseras en los pesos, y fallos de seguridad de memoria en el propio parser del formato (parchea el runtime) |
| Escaneo | picklescan y modelscan antes de cualquier carga |
Globals y opcodes peligrosos en ficheros que sabe parsear | Cargas escondidas en un contenedor que el escáner no sabe leer |
| Procedencia | Fijar por hash de commit, sha256sum, firma con Sigstore |
Artefactos cambiados o reapuntados, manipulación en tránsito | Un modelo que ya era malicioso cuando su autor lo firmó |
| Barrido de comportamiento | El barrido por token de la sección 3 | Disparadores que se te ocurre probar: versiones, fechas, nombres, flags | Disparadores raros de varios tokens o cadenas oscuras |
| Comprobación en CI | Suspender la build ante un salto de comportamiento desmedido | Regresiones tras cualquier afinado o subida de versión | Lo que tu corpus de prueba nunca ejercita |
| Contención en ejecución | Sandbox para el servido y las herramientas; separar red de ejecución de comandos; registrar la frontera | Un disparador que se activa en producción, limitando lo que puede alcanzar | Nada, si el agente tiene red y shell en un mismo contexto sin sandbox |
Y ahora qué
Tres movimientos este trimestre, todos los cuales acabas de ver ejecutarse. Escanea cada artefacto de modelo antes de que entre en tu registro y pásate de Pickle a safetensors allí donde el formato sea tuyo, porque un fichero de modelo que se ejecuta al cargar es el compromiso más barato que existe. Fija y verifica cada modelo desplegado por su hash de revisión exacto, para saber con precisión qué está corriendo y poder responder rápido al siguiente incidente. Y añade la comprobación de comportamiento a CI para que un cambio condicional en el comportamiento de seguridad de tu modelo sea algo que midas y no algo que descubras en producción.
Tener tu propio modelo sigue siendo lo correcto; ¿Sueñan los modelos abiertos con tokens? defendía esa idea y la mantengo. Pero tenlo como un profesional de la seguridad: da por hecho que los pesos son una dependencia no fiable, exige una historia de procedencia y verifica el comportamiento de algo que nunca pudiste leer.
Sed paranoicos. Escanea el fichero, fija el hash, barre en busca del disparador. Y no os fiéis de un modelo solo porque corre en vuestro propio hardware.
- X (Twitter): @SimonRoses
Lecturas recomendadas:
- Anthropic, Instituto de Seguridad de IA del Reino Unido e Instituto Alan Turing: A small number of samples can poison LLMs
- Poisoning Attacks on LLMs Require a Near-Constant Number of Poison Samples (arXiv 2510.07192)
- Anthropic: Sleeper Agents, Training Deceptive LLMs that Persist Through Safety Training
- ProjectDiscovery: How abliterated models can get you pwned (Prince Chaddha)
- Arditi et al.: Refusal in Language Models Is Mediated by a Single Direction (abliteración)
- Wan et al.: Poisoning Language Models During Instruction Tuning (ICML 2023)
- BadAgent: Inserting and Activating Backdoor Attacks in LLM Agents (ACL 2024, arXiv 2406.03007)
- JFrog: modelos de ML maliciosos en Hugging Face con puerta trasera silenciosa
- HiddenLayer / Decrypt: modelo falso de OpenAI ‘privacy-filter’, 244.000 descargas en 18 horas
- Protect AI + Hugging Face: 352.000 problemas inseguros o sospechosos en 51.700 modelos
- The Register: Researcher poisons open-weight AI model for under $100
- ReversingLabs: Malicious ML models discovered on Hugging Face (nullifAI)
- picklescan y modelscan (Protect AI)
- Trail of Bits: fickling, analizador estático y decompilador de ficheros pickle
- Aviso del parser GGUF de llama.cpp (CVE-2025-53630) y la clase recurrente de RCE en el parser
- safetensors y firma de modelos con Sigstore
- ¿Sueñan los modelos abiertos con tokens?
- Cuando el modelo es el atacante: el incidente de evaluación de modelos de Hugging Face y OpenAI
- Seguridad MCP en la práctica: cómo auditar los conectores en los que confían tus agentes
¿Preguntas o comentarios? Contacta a través de:
- Web: vulnex.com
- Estrategia de Seguridad en IA: vulnex.ai
- Twitter/X: @SimonRoses
- LinkedIn: linkedin.com/in/simonroses
- GitHub: github.com/vulnex
Contacto: info@vulnex.com

