sistema: OPERATIVO
← volver a todos los hacks
INFRASTRUCTURE CRITICAL NEW

LightLLM CVE-2026-26220: pickle en un WebSocket que el servidor obliga a exponer en red

CVE-2026-26220 (divulgada el 15 de febrero de 2026) coloca pickle.loads() en dos endpoints WebSocket sin autenticar del modo prefill-decode de LightLLM — y el servidor se niega a enlazar a localhost, así que la superficie es siempre remota.

2026-06-02 // 6 min affects: lightllm, vllm, gpu-inference-nodes, ml-serving-infrastructure

¿Qué es esto?

El 15 de febrero de 2026, el investigador Valentin Lobstein (Chocapikk) divulgó públicamente una ejecución remota de código sin autenticar en LightLLM, un motor de inferencia de LLM en Python (~3.900 estrellas en GitHub). Registrada como CVE-2026-26220 y asignada por VulnCheck el 16 de febrero, la falla tiene una puntuación CVSS 4.0 9.3 (crítica) y se clasifica como CWE-502 — Deserialización de datos no confiables. Afecta a LightLLM ≤ 1.1.0.

Es un caso de manual, con un matiz moderno. El modo prefill-decode (PD) de LightLLM expone dos endpoints WebSocket que llaman a pickle.loads() sobre tramas binarias en bruto sin ninguna autenticación — y el servidor se niega explícitamente a enlazar a localhost, de modo que en modo PD la superficie de ataque es, por diseño, siempre alcanzable por red. Pertenece a la misma clase recurrente que la CVE-2025-32444 de vLLM (CVSS hasta 10.0), donde pickle no confiable a través de un canal entre nodos producía un resultado idéntico.

Cómo funciona

El módulo pickle de Python es un serializador que, por diseño, puede reconstruir objetos arbitrarios — incluidos objetos cuya reconstrucción ejecuta código. Pasar bytes controlados por un atacante a pickle.loads() equivale a entregar al proceso un script para ejecutar. No es una sutileza propia de LightLLM: es una propiedad documentada de pickle y la razón de ser de CWE-502.

El modo PD de LightLLM reparte la inferencia entre nodos: un PD master orquesta el registro de workers y las transferencias de KV-cache entre nodos GPU de prefill y decode separados. Los workers se conectan al master por WebSocket para registrarse y reportar su estado. Según la divulgación y la issue de seguimiento del CVE, la deserialización está en lightllm/server/api_http.py:

# /pd_register  (api_http.py ~línea 310)
data = await websocket.receive_bytes()
obj = pickle.loads(data)          # untrusted network bytes -> object graph

# /kv_move_status  (api_http.py ~línea 331) — mismo patrón
upkv_status = pickle.loads(data)

Hay dos llamadas más a pickle.loads() en el bucle PD del lado worker. Llegar a ellas no requiere credenciales: /pd_register pide primero una trama JSON de registro, pero el node_id es un entero sin validar y mode es solo una comprobación de cadena — nada de autenticación. /kv_move_status acepta pickle en la primera trama.

El detalle agravante es la restricción de despliegue grabada en el arranque:

assert manager.args.host not in ["127.0.0.1", "localhost"]

El master debe enlazar a una interfaz enrutable, porque los workers remotos tienen que alcanzarlo. No existe una configuración «segura» solo en loopback para el modo PD. Hay una prueba de concepto funcional en el writeup del investigador; el mecanismo es el gadget reduce clásico de pickle — un objeto forjado cuya deserialización invoca un comando de sistema [REDACTED] — y aquí no reproducimos una carga ejecutable. El punto estructural basta: cualquier host capaz de abrir un socket hacia el PD master obtiene ejecución de código con los privilegios del proceso de servicio, antes de cualquier inferencia del modelo.

Por qué importa

No es un ataque inédito — es una instancia de alto impacto de una clase que el ecosistema de servicio de ML reincorpora una y otra vez. La misma causa raíz (pickle sobre un canal interno supuestamente de confianza) produjo la CVE-2025-32444 de vLLM. El patrón se repite porque el servicio desagregado multiplica los canales entre nodos, y cada uno que habla pickle es una RCE latente.

El radio de impacto es la propia capa de inferencia: nodos GPU que normalmente guardan los pesos del modelo, claves de API, credenciales cloud y una posición privilegiada dentro de la red. Un atacante que ejecuta código en un PD master ya está más allá del perímetro que importa. Y como los endpoints del PD master están, por diseño, expuestos en red, una instancia alcanzable desde un segmento no confiable (red interna plana, security group demasiado amplio, puerto de clúster expuesto) es explotable sin una sola credencial.

También hay una lección de proceso. La divulgación señala que el proyecto había recibido reportes de deserialización anteriores — #784 (ZMQ recv_pyobj, marzo de 2025) y un reporte privado en #1102 (noviembre de 2025) — que quedaron sin resolver, de ahí la escalada a CVE en lugar de un tratamiento discreto. Trate la infraestructura de ML de evolución rápida como inmadura en seguridad por defecto: la velocidad del código es alta, la cadencia de revisión de seguridad a menudo no.

Defensas

No necesita un parche del proveedor para neutralizar esto. Las mitigaciones son higiene de infraestructura estándar y se aplican a cualquier situación de pickle sobre la red, no solo a LightLLM:

  1. No exponga los endpoints PD a redes no confiables. Como el master no puede enlazar a localhost, confínelo con la capa inferior: enlace a una interfaz privada dedicada, restrinja el puerto a las IP de workers conocidas mediante firewall / security group, y ponga el tráfico PD en una VLAN o mesh aislada. Nunca deje /pd_register ni /kv_move_status alcanzables desde una subred de uso general.

  2. Autentique los canales entre nodos. El registro de workers y el KV-status son IPC entre nodos, no una API pública. Antepóngales mTLS (certificados de cliente) o una comprobación de secreto/token compartido, para que un par no registrado ni siquiera pueda abrir el socket.

  3. Sustituya pickle para el IPC de red. Los datos intercambiados aquí son estado estructurado simple (enteros, cadenas, diccionarios). JSON, MessagePack o protobuf los transportan sin conceder ejecución de código. Es el arreglo duradero que se pidió a los mantenedores en #1213.

  4. Si pickle es inevitable, restrínjalo. Use un RestrictedUnpickler con una lista blanca explícita de clases seguras y rechace todo lo demás, y/o envuelva las tramas en firmas HMAC para que el servidor solo deserialice mensajes que pueda verificar.

  5. Detecte el despliegue, luego el abuso. Inventaríe todo LightLLM que corra con --run_mode pd_master | prefill | decode. Alerte sobre conexiones entrantes a los puertos PD fuera de la lista blanca de workers, y sobre procesos de inferencia que generen shells (sh -c, bash, hijos tipo os.system) — la traza en tiempo de ejecución de alta señal de un gadget de deserialización al dispararse.

  6. Generalice la auditoría. Es una clase a escala de flota, no un CVE aislado. Busque en su stack de servicio los pickle.loads, recv_pyobj, torch.load(...) sobre entrada no confiable, y las cargas joblib sobre cualquier socket, cola o frontera HTTP. Cada uno es un candidato a CWE-502.

Estado

ElementoReferenciaFechaNotas
Divulgación pública + PoCChocapikk (V. Lobstein)2026-02-15pickle.loads() WebSocket en modo PD
CVE asignadoAdvisory de VulnCheck2026-02-16CVSS 4.0 9.3, CWE-502, afecta ≤ 1.1.0
Seguimiento + arreglo propuestoIssue de GitHub #12132026-02Arreglo solicitado a los mantenedores; el proyecto tiene historial de respuesta lenta en seguridad
Misma clase, proyecto vecinoCVE-2025-32444 (vLLM)2025Pickle sobre IPC entre nodos, CVSS hasta 10.0

El encuadre honesto: CVE-2026-26220 no es novedosa — es el mundo del servicio de ML reaprendiendo que pickle sobre un socket de red sin autenticar es ejecución remota de código. Hasta que los transportes entre nodos abandonen pickle, la defensa es suya: aísle el plano PD, autentique los pares y asuma que todo serializador capaz de reconstruir objetos arbitrarios acabará, algún día, reconstruyendo los de un atacante.

Sources