Principales conclusiones
- Un malicioso
build.rsEl script se ejecuta durantecargo buildcon los privilegios del proceso que haya invocado Cargo. - Stable Cargo no ofrece ninguna forma oficial de compilar un árbol de dependencias, al tiempo que no permite ejecutar sus scripts de compilación.
- Seis de los 34 paquetes maliciosos de TrapDoor acabaron en crates.io, frente a los 21 de npm y los 7 de PyPI.
- npm y pnpm ahora bloquean por defecto los scripts de instalación de dependencias no aprobados, y la versión estable de Cargo no cuenta con un equivalente a nivel de dependencias.
Los desarrolladores de Rust llevan años oyendo que este lenguaje es seguro. Seguridad de memoria, ausencia de conflictos de datos en el código seguro, un sistema de tipos que detecta en tiempo de compilación lo que otros lenguajes solo detectan en tiempo de ejecución. Nada de eso afecta al código que se ejecuta antes de que tu programa exista. En mayo de 2026, una campaña dirigida a la cadena de suministro de software denominada TrapDoor publicó 34 paquetes maliciosos en más de 384 versiones en npm, PyPI y crates.io. Ninguno de ellos esperaba a que llamaras al paquete. Se activaban al instalarlos, al importarlos o durante la compilación. Seis de los 34 se encontraban en crates.io, camuflados como herramientas de Sui y Move, utilizando build.rs para buscar los archivos de claves de los monederos de criptomonedas. El autor de un crate puede desactivar su propio script de compilación en el manifiesto. Como usuario, no dispones de un indicador de uso general que desactive los scripts de compilación de las dependencias en todo el árbol. Esa es la carencia de la que trata este artículo.
Lo que hizo TrapDoor en tres registros
npm, PyPI y crates.io no se vieron afectados por igual. De los 34 paquetes identificados, 21 se encontraban en npm y utilizaban scripts de postinstalación para ejecutar una carga útil compartida destinada a la recopilación de credenciales en el momento en que alguien instalaba uno de ellos. Siete se encontraban en PyPI y se ejecutaban al importarlos para recuperar una carga útil de JavaScript remota y ejecutarla localmente. Solo seis llegaron a crates.io, utilizando scripts de compilación de Rust que se ejecutan durante la compilación. El patrón es lo que une toda la campaña. En cada ecosistema, el atacante aprovechó el punto de ejecución que ese ecosistema ya le proporcionaba.
| Registro | Paquetes con nombre | Gancho de ejecución | Qué hizo |
|---|---|---|---|
| npm | 21 | Scripts del ciclo de vida posteriores a la instalación | Ran trap-core.js, un programa de recopilación de credenciales compartidas que utiliza el movimiento lateral basado en SSH |
| PyPI | 7 | Ejecución en el momento de la importación | Se recuperó y se ejecutó una carga útil de JS remota a través de node -e |
| crates.io | 6 | build.rs scripts de compilación |
Busqué los archivos de claves de las carteras Sui y Aptos, los cifré mediante XOR y los extraje a GitHub Gists. |
Los seis paquetes de crates.io: – move-analyzer-build, move-compiler-tools, move-project-builder, sui-framework-helpers, sui-move-build-helper, y sui-sdk-build-utils – estaban dirigidas específicamente a los desarrolladores de Sui y Move, más que a la comunidad general de Rust. La parte de npm fue la que sufrió la mayor parte del impacto en cuanto al número de paquetes y la complejidad de la carga maliciosa. La parte de crates.io sigue siendo la más importante para los ingenieros de Rust, ya que muestra una build.rs script que hace exactamente lo que un build.rs El script está diseñado para realizar esta tarea. Se ejecuta de forma silenciosa, con los privilegios del proceso de compilación, antes de que la propia aplicación tenga oportunidad de ejecutarse.
Por qué build.rs es una opción mejor que un script de posinstalación

build.rs es un programa en Rust que Cargo compila y ejecuta antes de compilar el crate al que pertenece, con los privilegios del proceso en el que se ejecuta Cargo. Existe por buenas razones, como la generación de código y la vinculación nativa, y hay muchos crates legítimos que incluyen uno. Cualquier dependencia cuyo script de compilación se ejecute puede ejecutar código arbitrario con los permisos de que dispone el proceso de Cargo. TrapDoor es el resultado de ello cuando alguien lo utiliza de forma deliberada. Los seis crates, cuyos nombres se asemejan a las herramientas de compilación de Sui y Move, utilizan build.rs buscar en un equipo los archivos de claves de la cartera en el momento en que alguien ejecutara cargo build. El script cifraba mediante XOR todo lo que encontraba y lo enviaba a GitHub Gists. Sin ningún gancho de instalación independiente, sin importación, simplemente al compilar un proyecto que, casualmente, dependía de uno de los seis nombres de paquetes. Las macros procedimentales son la otra vía de ejecución en tiempo de compilación en Rust, y TrapDoor no las necesitaba. Una macro «proc» se ejecuta dentro del compilador durante la expansión de macros, lo que significa que solo se activa cuando algo la invoca realmente. Un script de compilación se activa simplemente porque el crate se encuentra en el gráfico de compilación, lo cual es precisamente lo que lo convierte en la opción más fiable para un atacante.
| Mecanismo | Se celebra durante | Control general del gestor de paquetes |
|---|---|---|
| script de ciclo de vida de npm | Instalar | Sí, está bloqueado por defecto desde la versión 12 de npm. |
| Ejecución en el momento de la importación en Python | Importación de paquetes | No |
Óxido build.rs |
Recopilación | No |
| Macro procedimental de Rust | Expansión macroeconómica | No |
Cargo no ofrece ningún conmutador estable y de uso general que permita al usuario compilar un árbol de dependencias rechazando al mismo tiempo los scripts de compilación de dependencias o la expansión de macros. Hay una única excepción muy concreta. Cuando un crate declara un links Si utilizas la clave «key», puedes anular su script de compilación desde la configuración de Cargo proporcionando tú mismo los metadatos, con lo que dicho script nunca se compilará ni se ejecutará. Esto se aplica a los shims de bibliotecas nativas, no a una dependencia arbitraria que haya decidido incluir un script de compilación. cargo build --offline impide que Cargo realice solicitudes de red y un Cargo.lock fija las versiones, pero ninguna de ellas impide que se ejecute un script de compilación que ya se encuentre en tu árbol de código, ni sirve de nada si la versión que fija tu archivo de bloqueo es la maliciosa.
Dónde se ejecuta un script de compilación y a qué tiene acceso

cargo build suele contener credenciales de roles en la nube, tokens de registro, acceso al registro de contenedores y, en ocasiones, claves de firma. El portátil de un desarrollador puede contener la mayor parte de eso, además de claves SSH, datos de sesiones del navegador y el código fuente de todo aquello que aún no se haya lanzado. Eso es lo que hace que merezca la pena comprometer un host de compilación en primer lugar. Sakshi Grover, directora sénior de investigación de los Servicios de Ciberseguridad de IDC Asia-Pacífico, declaró a CSO Online después de que se revelara que, según TrapDoor, «una sola estación de trabajo comprometida puede convertirse silenciosamente en un punto de entrada a los procesos de CI/CD». Un script de compilación que recopila credenciales es una de las vías de acceso, y lo que hizo la parte de TrapDoor relacionada con crates.io fue extraer directamente los archivos de claves de los monederos del sistema de archivos durante una compilación sin supervisión.
La sección dedicada a los asistentes de programación basados en IA

build.rs truco. Procedía de la inserción de código malicioso en npm .cursorrules y CLAUDE.md archivos como mecanismo de persistencia, con las instrucciones maliciosas ocultos tras caracteres Unicode de ancho cero, además del mismo atacante Abrir solicitudes de incorporación de cambios en GitHub frente a proyectos de IA y herramientas de desarrollo no relacionados. Ambos nombres de archivo son convenciones que las herramientas de programación de IA leen para comprender un código fuente, por lo que uno malicioso supone una inyección indirecta de comandos. El texto del atacante acaba en un archivo que el asistente trata como una guía en lugar de como datos. Sin embargo, la condición previa es más restrictiva que «todas las herramientas de IA están comprometidas». Que la técnica tenga éxito depende de qué archivos de instrucciones descubra un asistente concreto, en cuáles confíe y según cuáles actúe. Merece la pena hacerlo de todos modos. Busca archivos de instrucciones del asistente en cualquier lugar que no sea la raíz de tu repositorio y considera sospechoso por defecto cualquier resultado que aparezca dentro de una dependencia de proveedor o de ruta. Funciona como una comprobación de CI, siempre y cuando inviertas el código de salida para que, si se encuentra algo, la compilación falle en lugar de pasar.
if find . -name 'CLAUDE.md' -o -name '.cursorrules' \
| grep -vE '^\./(CLAUDE\.md|\.cursorrules)$' | grep -q .; then
echo "Unexpected AI assistant instruction file found"
exit 1
fi
Ese objetivo es distinto al de engañar a un revisor humano. Corromper un archivo en el momento de la persistencia significa que el resultado se produce más tarde, la próxima vez que un asistente lea el proyecto y considere el archivo como una instrucción legítima. La solución tiene que ver principalmente con el ámbito de aplicación. Limitar los archivos de instrucciones en los que confía un asistente reduce la superficie de ataque, sobre todo cuando se excluyen las rutas controladas por dependencias.
Lo que realmente se elimina al tirar de una caja
El registro de crates.io tiene una característica poco habitual, y en general positiva. Las versiones publicadas son inmutables y no se pueden eliminar, solo «retirar». Esto impide toda una clase de ataques a los que se ven expuestos los registros mutables. También significa que una «retirada» es una medida correctiva menos eficaz de lo que la mayoría de la gente cree.
| Acción | Efecto |
|---|---|
| Descargar una versión | Las nuevas resoluciones lo omiten, pero los archivos de bloqueo existentes siguen utilizándolo |
| Eliminar todas las versiones | El nombre del crate sigue estando reservado, y el código sigue estando disponible para su descarga |
| Eliminar una versión | No es posible en crates.io |
Una versión eliminada que ya se había guardado en un commit Cargo.lock sigue en uso. Esto es intencionado, para que las compilaciones no fallen por sí solas, pero significa que una organización que haya fijado una versión maliciosa seguirá compilando con ella hasta que alguien modifique el archivo de bloqueo. Ejecuta cargo update --dry-run para ver qué cambiaría y, a continuación, ejecutar el código correspondiente cargo update para volver a escribir el archivo de bloqueo una vez que hayas comprobado que las versiones de sustitución son seguras.
Por qué esto siempre iba a pasarle a Rust
Los atacantes siguen la estela de la adopción. El papel cada vez más importante que ha ido adquiriendo Rust en los ámbitos de la infraestructura, la criptografía y el software de sistemas a lo largo de los últimos años hacía que esto fuera una cuestión de «cuándo», no de «si». Los crates que hace unos años eran pequeños proyectos paralelos ahora se encuentran en lo más profundo de las pilas de producción. Los scripts de compilación son algo habitual en la cultura de Rust, algo que no ocurre con los scripts de postinstalación en npm. Un paquete con un script de postinstalación llama la atención de los revisores. build.rs No es así, porque los crates que generan código, comprueban las capacidades de la plataforma o enlazan bibliotecas nativas tienen todos una razón legítima para incluir uno. Ese uso legítimo es lo que hace que la mera presencia de build.rs una señal tan débil. Nada de esto supone el primer encuentro de TrapDoor crates.io con el malware. Ya en 2023, el registro retiró nueve crates que eran objeto de typosquatting y cuyos build.rs los archivos enviaban los metadatos del host a un canal de Telegram, y ataques a la cadena de suministro En crates.io han aparecido con tanta frecuencia desde entonces que el equipo dejó de publicar entradas en el blog sobre cada uno de ellos en febrero de 2026. La novedad de TrapDoor es que crates.io aparece en el marco de una campaña coordinada que ha llegado a npm y PyPI en la misma oleada. Esas dos plataformas ya habían pasado por su propia versión de esto años antes, a una escala mucho mayor, una vez que el número de paquetes que albergaban alcanzó typosquatting y nombres similares que merecen la pena para un atacante. TrapDoor integra crates.io en el mismo modelo de amenazas entre ecosistemas que los equipos de seguridad ya aplican a npm y PyPI.
Garantizar la seguridad de la cadena de suministro de Rust
SecureChain para Rust analiza un crate antes de que se incorpore a tu árbol de dependencias, revisando el código fuente en busca de patrones de ataque durante la compilación, incluidos los maliciosos build.rs scripts y macros procedimentales, y bloqueando aquellos que fallan. Los crates se vuelven a compilar a partir del código fuente verificado, en lugar de importarse directamente desde el registro público. Ese es el tipo de filtro que debe interponerse ante un paquete como sui-framework-helpers, porque una vez que un enemigo build.rs está en el árbol, ejecutándose cargo build Ya es demasiado tarde. Además, sigue corrigiendo vulnerabilidades en los crates que ya forman parte de tu pila, incluidos aquellos cuyos mantenedores han dejado de dar señales de vida. TrapDoor es el problema más urgente. La mayor parte de lo que acaba en un árbol de Rust en producción es la versión más lenta, y también merece la pena abordarla. Suscríbete al Boletín informativo de TuxCare para un análisis como este la próxima vez que una campaña elija un nuevo nombre de paquete y un nuevo ecosistema en el que esconderse.
PREGUNTAS FRECUENTES
¿Qué es un ataque a la cadena de suministro de Rust?
Un ataque que compromete el código o las dependencias antes de que lleguen a ti, normalmente a través de crates maliciosos o suplantados en crates.io. En Rust, los scripts de compilación y las macros procedimentales son las dos vías de ejecución en tiempo de compilación más importantes, ya que ambas ejecutan código antes de que lo haga la aplicación resultante.
¿Qué es build.rs y por qué es peligroso?
build.rs Es un programa en Rust que Cargo compila y ejecuta antes de compilar el crate al que pertenece, con los privilegios del proceso en el que se ejecuta Cargo. Existe para la generación legítima de código y la vinculación nativa, y cualquier dependencia que lo incluya permite ejecutar código arbitrario con los permisos que tenga el proceso de compilación de Cargo.
¿TrapDoor necesitaba realmente build.rs, o habría servido cualquier hook de compilación de Rust?
build.rs Era suficiente y suponía el menor esfuerzo, así que eso es lo que utilizaron las seis crates. Una macro procedural es la otra vía en tiempo de compilación, pero solo se ejecuta cuando el código que la utiliza invoca realmente la macro, mientras que un script de compilación se ejecuta simplemente porque la crate está en el gráfico de compilación. Los atacantes eligieron build.rs porque les proporcionó el punto de ejecución que necesitaban.
¿Al retirar una caja, esta desaparece de crates.io?
No. Al «desanclar» una versión, se impide que las nuevas resoluciones la seleccionen, aunque sigue estando disponible para su descarga, y una versión ya anclada en un Cargo.lock Se sigue utilizando. En crates.io no es posible eliminar una versión publicada.
¿Cuáles eran los archivos maliciosos del asistente de IA en TrapDoor?
En .cursorrules y CLAUDE.md Los archivos utilizaban caracteres Unicode de ancho cero para ocultar instrucciones dirigidas a los asistentes de programación basados en IA. La campaña también intentó introducir archivos similares a través de solicitudes de incorporación de cambios (pull requests) en GitHub en proyectos no relacionados.
