Volver a todas las publicaciones
Publicado el · por Renaud Deraison

Uno cada tres días

StepSecurity publicó el sábado el recuento de un año de ataques con paquetes maliciosos: 56 en total, 6 en los primeros seis meses y 50 en los seis meses y medio transcurridos desde febrero, cerca de uno cada tres días desde marzo. Después nombra por qué siguen funcionando: secretos en las máquinas de desarrollo y en los runners de CI, versiones latest sin fijar, acceso de salida a la red sin restricciones. Ninguna de esas cosas es una propiedad de un paquete. Las tres describen la máquina donde corre la instalación, y la máquina que más paquetes instala hoy lleva encima un agente de programación.

Un paquete malicioso se dispara en la instalación. No hay ruta de código vulnerable que alcanzar, ni exploit que escribir, ni ventana que parchear. A uno cada tres días, el paquete en el que llega ha dejado de ser la pregunta útil.

El sábado, StepSecurity publicó un recuento de doce meses de ataques a la cadena de suministro de código abierto. Cincuenta y seis. “En los seis meses de agosto de 2025 a enero de 2026 registramos 6 incidentes. En los seis meses y medio transcurridos desde febrero de 2026 registramos 50.” Desde marzo el ritmo se ha mantenido en torno a nueve al mes, o “alrededor de uno cada tres días”.

StepSecurity vende un registro con filtrado, así que lea el informe sabiendo qué está vendiendo. El recuento se sostiene igualmente. Es el conteo público más cuidadoso de paquetes maliciosos que nadie ha publicado, y la definición que hay detrás es estrecha a propósito.

Qué está contando ese número

El informe descarta las vulnerabilidades. Sin CVE, sin ningún bloque unsafe que resultara serlo. Cuenta paquetes que alguien envenenó a propósito.

Por eso el número resulta utilizable. Una vulnerabilidad necesita un segundo suceso: alguien tiene que encontrar su instancia, alcanzar la ruta de código afectada y sacar algo de ello. Puede ejecutar una versión vulnerable durante un año y no pasa nada. Un paquete malicioso se salta todo eso. Como dice el informe, “se ejecuta en el momento en que una máquina de desarrollo o un runner de CI lo instala”. La instalación es la explotación. Nunca hay ventana entre divulgación y parche, porque nadie divulgó nada y nadie publicó ningún parche. Hay un número de versión, y después una carrera para averiguar quién lo descargó.

Marzo de 2026 vio 13. Abril 9, mayo 9, junio 10, julio 6. En cinco días de marzo, una operación rastreada como Team PCP se llevó 78.330 secretos de 2.186 organizaciones, 92 de ellas empresas cotizadas, envenenando 76 de las 77 etiquetas de la GitHub Action de Trivy y un puñado de otros componentes de compilación. axios, con más de 100 millones de descargas semanales, publicó dos versiones maliciosas con un troyano de acceso remoto multiplataforma. Seis gusanos aparecen en el año: Shai-Hulud dos veces, CanisterWorm, Mini Shai-Hulud, Miasma y ChainDrop este mes. npm, PyPI, RubyGems, Composer, crates.io, GitHub Actions. “Todos los grandes ecosistemas de paquetes y plataformas de CI/CD han sido golpeados.”

Dos entradas de esa lista importan aquí. El informe señala que hackerbot-claw, “un bot impulsado por IA”, fue a por las GitHub Actions de Microsoft, Datadog y la CNCF, y que “el gusano Miasma apuntaba específicamente a los agentes de programación con IA”.

Las tres condiciones, y ninguna es un paquete

Después el informe hace lo útil. Bajo un titular que pregunta por qué siguen funcionando los ataques, da tres respuestas: “secretos en pipelines de CI/CD y máquinas de desarrollo, versiones latest sin fijar, acceso de salida a la red sin restricciones”.

Nada de esa lista describe un paquete. Ni ofuscación, ni distancia de typosquatting, ni ingenio en el hook de instalación. Las tres describen la máquina que ejecuta la instalación: qué guarda, cómo elige una versión y adónde tiene permiso para conectarse. Cambie una sola de ellas y la mayoría de los cincuenta y seis dejan de salir rentables.

Latest sin fijar

Algo en la cadena de resolución dice “la más nueva”. Un rango flotante, una dependencia transitiva, un lockfile reconstruido, un agente que añade una biblioteca en su nombre. Al atacante solo le hace falta que el resolvedor prefiera una versión publicada hace unos minutos.

Secretos en la máquina

~/.ssh, ~/.aws, ~/.kube, ~/.npmrc, un GH_TOKEN en el entorno, un inicio de sesión de registro en ~/.docker/config.json. Nada de eso pertenece a la compilación, y lo que la compilación acaba de arrancar puede leerlo todo.

Salida abierta

El botín tiene que salir, y el binario de segunda etapa tiene que llegar. Ambos son conexiones salientes corrientes desde una máquina que permite conexiones salientes a cualquier parte.

Lo que tiene que alinearse1 · el paquetese publica una versiónenvenenada y vive unahora o dos2 · el resolvedoralgo en la cadena pide lamás nueva, y la más nuevaes la envenenada3 · el discoel código de instalación lee~/.ssh ~/.aws ~/.npmrcy el entorno4 · el cableuna conexión saliente selo lleva y trae de vueltala segunda etapalos pasos 2, 3 y 4 los decide su máquina, no el paquete“secretos en pipelines de CI/CD y máquinas de desarrollo, versiones latestsin fijar, acceso de salida sin restricciones” — StepSecurityesta es la únicaparte que ustedno controla
Un ataque con paquete malicioso necesita que cuatro cosas se alineen, y solo la primera trata sobre el paquete. Las otras tres son propiedades de la máquina: cómo elige una versión, qué guarda y adónde tiene permiso para conectarse.

De quién es la máquina que más paquetes instala hoy

La defensa en la que todo el mundo se apoyaba era la atención. Uno lee el diff. Uno se da cuenta de que una biblioteca de caché ha arrastrado una dependencia con el nombre de un formateador de fechas con una letra cambiada.

Dos cosas han adelantado a eso. El ritmo, primero: nadie revisa dependencias transitivas cada tres días. Luego, quién hace la instalación. Un agente de programación añade una biblioteca porque usted le pidió construir algo, resuelve el árbol, ejecuta la instalación e informa de que las pruebas pasan. Usted lee el diff que produjo, que es la parte que merece su tiempo. Un tarball nunca aparece en un diff. La lista de StepSecurity ya incluye un gusano que salió a buscar precisamente esa forma de trabajar.

Quédese con el agente. Mueva el control a donde su velocidad deje de importar.

Tres condiciones, tres controles, todos del lado del Mac

Bromure Agentic Coding ejecuta el agente en una VM Ubuntu desechable sobre el framework Virtualization de Apple, con solo las carpetas que usted eligió montadas dentro y un proxy del lado del anfitrión como única vía de salida. Esa colocación pone cada una de las tres condiciones de StepSecurity delante de un control que la compilación no puede alcanzar.

El latest sin fijar se convierte en un reloj. Cada descarga de paquete cruza primero el proxy del anfitrión: npm, PyPI, Cargo, RubyGems, Maven, NuGet, módulos de Go, Packagist. El filtro de antigüedad viene activado por omisión a dos días. Las referencias flotantes se resuelven a la versión más reciente anterior al corte, así que latest sigue funcionando y deja de significar “publicada mientras usted leía esto”. Una referencia fijada a algo demasiado fresco vuelve como un 451 con un error de Bromure encima. Además de eso, puede activar las consultas OSV en el umbral de severidad que elija, encaminar los paquetes por la base de reputación de socket.dev o por el registro con filtrado de Delpi, y quitar al vuelo preinstall, install, postinstall y prepare de los tarballs de npm. Bromure reescribe el tarball y actualiza el hash de metadatos del registro para que la propia verificación de npm siga pasando. El .npmrc y el pip.conf dentro de la VM pueden endurecer cualquiera de estos ajustes y no pueden aflojarlos.

Los secretos de la máquina son señuelos. Las credenciales reales nunca entran en la VM. El proxy cambia un marcador por el token real en el cable; el kubeconfig del invitado es sintético, con certificados de cliente desechables; AWS recibe un ayudante credential_process que entrega al SDK una falsificación de cuarenta caracteres mientras el anfitrión vuelve a firmar SigV4 con el material real, de modo que cualquier cosa que rodee el proxy recoge una InvalidSignatureException de Amazon. El código de instalación que lee ~/.aws/credentials encuentra un archivo, lo lee y exfiltra un señuelo. Cada credencial lleva además una política de escritura (Desactivada, Bloquear operaciones destructivas o Solo lectura) aplicada en el proxy del anfitrión, más Requerir aprobación para usar, que convierte cualquier uso concreto en un diálogo de consentimiento con una concesión de cinco minutos, una hora o el resto de la sesión.

La salida abierta responde a una regla que usted escribió. En Salvaguardas, la tabla Conexiones salientes es un cortafuegos por perfil: una fila por regla, cada una con una acción, un protocolo (tcp, udp, web o any), un anfitrión o CIDR, puertos y, para web, una lista de métodos HTTP. Las reglas se evalúan de arriba abajo, gana la primera coincidencia, y Tráfico sin coincidencia es un interruptor que usted pone en Permitir o Denegar. Dos capas aplican el mismo conjunto de reglas: el conmutador virtual por IP de destino y por los nombres de anfitrión que aprendió espiando las respuestas DNS del invitado, y el proxy por SNI de TLS. La interceptación transparente viene activada, así que un script de compilación que borre HTTPS_PROXY no cambia nada sobre qué conexiones salen.

El ataque a crates.io de la semana pasada muestra qué atrapa esa regla. En el análisis de StepSecurity, el script de compilación del dropper decodificaba una URL en base64 y descargaba un binario específico de la plataforma desde una IP desnuda en el puerto 9089, con la validación de certificados desactivada, y después desligaba el proceso hijo para que Cargo no lo esperara. Con el tráfico sin coincidencia denegado, esa descarga nunca se completa, y el código de salida del script de compilación deja de ser una pregunta interesante.

Una instalación, dos máquinasEn el portátilel resolvedortoma lo último que se publicólos scripts de instalacióncorren como usted, antes de importar nadalas credenciales~/.ssh ~/.aws ~/.npmrc $GH_TOKENel cablellega a cualquier host, sin registroEn un espacio de trabajo de Bromurefiltro de antigüedad, activo por omisión a dos díaslas refs flotantes quedan bajo el corte · unaversión fijada demasiado fresca recibe un 451quitados del tarballhash de metadatos reescrito, npm sigue verificandoseñuelos, cambiados en el cablelos valores reales siguen en el Mac · SigV4 refirmadosin coincidencia: denegar · Registro de seguridad
Los mismos cuatro pasos, ejecutados dentro de un espacio de trabajo de Bromure. El paso uno sigue ocurriendo, porque nadie puede impedir que alguien publique una versión mala. Los pasos dos, tres y cuatro se topan cada uno con un control que vive en el Mac, fuera de todo aquello en lo que la instalación puede escribir.

Ochenta y seis minutos

Vuelva a ese mismo ataque para ver por qué un reloj gana a un juicio de valor. Los lanzamientos envenenados de arrayref, internment y append-only-vec vivieron 86, 90 y 107 minutos antes de que crates.io los retirara. Para dirigir a los resolvedores hacia ellos, el atacante retiró las versiones limpias, de la 0.3.9 hacia atrás hasta la 0.3.5, en una ráfaga que duró dieciséis segundos, dejando un único lanzamiento que nadie había retirado. La frase más afilada de StepSecurity sobre el incidente: “El atacante convirtió la propia función de seguridad del registro en el canal de entrega.” Y arrayref en sí no llevaba código malicioso. La carga útil entró por un typosquat de proc-macro2 en tiempo de compilación.

Un filtro de antigüedad de dos días nunca tiene que reconocer nada de eso. Una versión de ochenta y seis minutos es más joven que el corte y, para cuando tiene edad suficiente para instalarse, crates.io ya la ha borrado. El atacante construyó una ventana más corta que la que el filtro ignora.

Ataque cincuenta y siete

El informe cierra con una frase que merece la pena guardar: “56 ataques en 12 meses. El ataque número 57 ya se está construyendo.”

No sabrá qué paquete lo lleva, y no lo leerá. Su agente tampoco. Cinco meses a uno cada tres días van muy por encima de la cadencia a la que nadie revisa, y llamarlo descuido no lleva a ninguna parte. Lo que sí decide usted es sobre qué aterriza: si la versión que necesita es una que su resolvedor aceptará, si los secretos que viene a buscar son reales, y si la dirección que quiere alcanzar es una a la que su máquina enruta. Tres ajustes, todos de su lado de la frontera.


Fuentes: StepSecurity, “The State of Open Source Supply Chain Attacks” (22 de agosto de 2026) · StepSecurity, “Rust Supply-Chain Attack: arrayref, internment, and append-only-vec Poisoned by the proc-macro1 Build-Time Dropper” (20 de agosto de 2026) · StepSecurity, “Team PCP Stole 78,330 Secrets From 2,186 Organizations” (13 de agosto de 2026)