Saltar al contenido
Software Supply Chain Security

Ataque a la cadena de suministro en Rust: malware en crates

build-time malware

Un ataque a la cadena de suministro en el ecosistema Rust volvió a poner el foco en la seguridad de las dependencias. Según informó el equipo de respuesta de seguridad de Rust, se publicaron versiones maliciosas de crates a través de una cuenta de mantenedor comprometida, incorporando una dependencia suplantada que ejecutaba un payload remoto durante la compilación.

Lo alarmante es que no hacía falta llamar código explícitamente desde las crates afectadas: bastaba con que un proyecto resolviera y construyera una dependencia que incluyera el script de build alterado. En cuestión de minutos, las versiones comprometidas fueron eliminadas de crates.io, pero el impacto potencial ya era enorme.

Qué ocurrió exactamente en crates.io

El equipo de Rust Security Response Team indicó que tres releases de crates se publicaron con cambios diseñados para cargar y ejecutar código malicioso en tiempo de compilación. Las versiones afectadas fueron: arrayref 0.3.10, internment 0.8.7 y append-only-vec 0.1.9. Todas se publicaron desde la misma cuenta de propietario el 20 de agosto de 2026 y se retiraron entre 86 y 107 minutos después.

El vector no estaba en el contenido principal de esas crates, sino en una dependencia inyectada mediante un script de compilación. Esa dependencia era una variante con typosquatting de otra conocida, de modo que el proceso de build quedaba comprometido aun cuando el proyecto no ejecutara funciones específicas de las crates afectadas.

El papel clave del script de build malicioso

De acuerdo con el análisis reportado por RustSec, el comportamiento malicioso se activaba porque el manifiesto de la release comprometida agregaba una dependencia apuntando a proc-macro1, un “parecido” a proc-macro2, ampliamente utilizado en Rust. La fuente de proc-macro1, en sí, era una copia legítima de proc-macro2; por eso las compilaciones podían completar su ejecución “normal” desde la perspectiva de dependencias.

Sin embargo, el script de build introducido se encargaba de reconstruir la infraestructura de salida. El procedimiento describía cómo, en tiempo de build, se reensamblaban fragmentos codificados en base64 para obtener el host del payload y la dirección del servidor de comando y control (C2). Luego se instalaba un verificador de certificados con métodos que retornaban éxito sin validar correctamente TLS, lo que reducía la protección frente a la verificación de conexiones segura.

Además, el payload se elegía según el sistema operativo y la arquitectura del equipo que compilaba. En sistemas Unix y macOS, se escribían bytes en una ruta bajo /tmp/rust-setup, se marcaba el archivo como ejecutable y se lanzaba en modo “detached” con el C2 como primer argumento. En Windows, el proceso utilizaba un script de PowerShell en %TEMP% y lo iniciaba oculto mediante un lanzador VBScript asociado a wscript.exe; posteriormente el proceso hijo quedaba “abandonado” para evitar que Cargo esperara la ejecución.

Por qué parecía legítimo: rangos de versiones y “yank”

Un elemento importante del caso es que la entrega dependía de cómo Cargo decide actualizar versiones. El reporte de la base de asesorías de RustSec explica que las versiones 0.3.5–0.3.9 de arrayref habían sido retiradas (yanked) poco antes del lanzamiento comprometido, el mismo día y con una ventana muy cercana a la publicación maliciosa.

Así, un proyecto que pidiera un rango caret sobre la serie 0.3.x podía terminar resolviendo la única versión disponible que no generaba advertencias de “yanked”, es decir, la que contenía el script de build modificado. En términos prácticos, el “anzuelo” hacía que el resolver dependencias empujara hacia el release comprometido.

Qué tan extendido estaba el riesgo

Según los datos confirmados mediante la API de crates.io, arrayref acumulaba 245.385.500 descargas totales y 53.905.601 descargas en los 90 días previos al 20 de agosto de 2026. Además, el ecosistema mostraba una huella de interdependencias: 403 crates en crates.io dependían de arrayref.

El análisis de la cadena de dependencias también mostró que requisitos con rango caret en 0.3.x aceptaban la versión comprometida. Por ejemplo, una cadena de dependencias como winitsctk-adwaitatiny-skiaarrayref permitía resolver arrayref ^0.3.6, que a su vez aceptaba 0.3.10.

No hay parche único y no se asignó CVE

El reporte indica que no existe una versión “corregida” que pueda considerarse un parche puntual para esos releases maliciosos, y que no se asignó un identificador CVE al incidente. Esto no significa que no haya que actuar, sino que la respuesta se centró en la eliminación de las releases comprometidas y en ajustes preventivos en el proceso de resolución.

En la base de asesorías, las entradas para las tres crates afectadas registraron sin evidencia de que versiones maliciosas hubieran sido utilizadas. Aun así, la recomendación práctica se orientó a reducir el riesgo de reconstrucciones futuras o de compilaciones que pudieran volver a resolver versiones comprometidas.

Recomendaciones para desarrolladores

El equipo de respuesta aconsejó revisar localmente si quedaron archivos de las versiones eliminadas en el caché de Cargo. En concreto, se sugirió buscar en ~/.cargo/registry/cache los ficheros de las crates retiradas.

También se recomendó fijar la dependencia arrayref en una versión anterior, por ejemplo arrayref 0.3.9 o inferior, después de que RustSec “unyank” (retirara el estado yanked) para corregir el comportamiento del resolver en su respuesta.

En paralelo, el incidente deja una lección clara: la seguridad no sólo depende del código que “llamas”, sino del código que se ejecuta en el proceso de compilación.

Persistencia y objetivos del payload

De acuerdo con análisis atribuidos en el informe, el implante de “etapa 2” enviaba señales y coordinaba acciones mediante HTTPS POST hacia una ruta específica (por ejemplo /49890878). Para mantener la presencia en el sistema, usaba mecanismos de persistencia distintos según plataforma: en Windows mediante una clave de Registry Run, en macOS con un LaunchAgent, y en Linux con un servicio user de systemd.

El payload contemplaba comandos para detener la actividad, reconfigurar la comunicación con el C2, instalar persistencia adicional y descargar/ejecutar scripts posteriores. Además, se indicó que podía robar credenciales de navegadores como Chrome, Brave y Edge consultando bases de datos SQLite asociadas a inicios de sesión.

El análisis también señaló limitaciones: en el caso de Windows se revisó el payload de esa plataforma, mientras que otras plataformas se mencionaron con hashes sin análisis detallado en esa sección específica.

Indicadores de compromiso (IoCs) reportados

El reporte incluyó indicadores de compromiso para ayudar a la detección. Entre ellos figuraban direcciones IP y dominios asociados a infraestructura de red, así como rutas y nombres de archivos creados durante el build. También se listaron nombres de binarios y señales relacionadas con cuentas involucradas (incluyendo un propietario legítimo presumiblemente comprometido).

Como ejemplo, se mencionaron rutas temporales como /tmp/rust-setup y scripts en %TEMP% asociados a nombres como rust-setup.ps1 y rust-setup-launch.vbs, además de un conjunto de binarios nombrados por versiones internas en el análisis.

Cómo se relaciona con otros incidentes de cadena de suministro

El caso se compara con otras campañas recientes donde atacantes aprovecharon procesos de publicación o distribución de dependencias. Se mencionó una superposición sustancial de infraestructura con ataques atribuidos previamente en el contexto de cadenas de suministro, incluyendo incidentes vinculados a compromisos en ecosistemas JavaScript.

En cuanto a atribución, en este caso no se señaló un actor nominal específico. Aun así, el patrón coincide: cuentas de mantenimiento comprometidas, publicación maliciosa corta en el tiempo y abuso de la fase de build para ejecutar acciones antes de que el software “aplicado” sea siquiera llamado.

Qué cambia a partir de ahora

Este incidente también pone de relieve el valor de controles preventivos en el ecosistema de publicación. Se mencionó que Cargo no tiene una función equivalente “lista” al enfriamiento por edad mínimo para dependencias recién publicadas, aunque existían propuestas para un ajuste global. Un enfoque similar había sido implementado en el pasado para herramientas de actualización automática.

Hasta que esos mecanismos maduren o se adopten ampliamente, la responsabilidad recae en prácticas concretas: revisar cambios en dependencias, fijar versiones cuando sea apropiado y vigilar cachés y procesos de build, especialmente en pipelines CI/CD.

Conclusión

El ataque a la cadena de suministro contra crates.io demuestra cómo una sola release comprometida puede convertir el proceso de compilación en una puerta de entrada. Aunque las versiones maliciosas fueron eliminadas rápidamente, el episodio confirma que la seguridad en Rust (y en cualquier gestor de dependencias) debe contemplar no sólo el runtime, sino también los scripts que se ejecutan durante el build.

Si usas Rust y dependes de crates que podrían resolver rangos amplios, actúa con medidas preventivas: revisa tu caché local, fija versiones cuando haga falta y mantente atento a advisories de RustSec.

Fuente: https://thehackernews.com/2026/08/rust-supply-chain-attack-puts-build.html