1 puntos por GN⁺ 4 시간 전 | Aún no hay comentarios. | Compartir por WhatsApp
  • Tras proponer en LinkedIn una vacante para ingeniero de software, enviaron un repositorio privado de GitHub disfrazado como una tarea legítima de React/Web3; al ejecutarlo, malware en JavaScript oculto en tailwind.config.js se activaba en la computadora del desarrollador
  • El archivo de configuración, de 30,987 bytes, ocultaba 27 KB de código ofuscado detrás de miles de espacios en blanco; luego descargaba desde un servidor remoto una carga útil de segunda etapa cifrada con AES-256-CBC, la guardaba en %TEMP%\pack y después la ejecutaba con node pack
  • Tras observarlo durante unos 10 minutos en una VM aislada con Windows 11 ARM, se confirmó que la carga útil activaba cuatro componentes: una puerta trasera de control remoto, un ladrón de navegadores y wallets, un escáner recursivo de archivos y un monitor del portapapeles
  • A través de Socket.IO controlaba terminal, SSH, pantalla, teclado y mouse; además recopilaba bases de datos de Chromium, almacenamiento de extensiones de wallets, claves SSH, código fuente y archivos de configuración, y en el puerto de subida de archivos se observaron 2,588 solicitudes
  • Los repositorios de tareas enviados por desconocidos deben tratarse como código no confiable y revisarse en una VM o contenedor desechable sin perfiles reales del navegador, claves SSH, credenciales de nube ni wallets; si ya hubo infección, tras aislar la red y preservar evidencia, hay que rotar incluso los secretos

Un repositorio disfrazado como una tarea normal de contratación Web3

  • El usuario de LinkedIn Wayan Adrian ofreció un puesto de ingeniero de software en shrapnel.com y entregó como tarea el repositorio privado de GitHub tech-active-workplace-frontend-main de la cuenta yevhen-o
  • Una plataforma de campañas NFT llamada BLAIEXS parecía, a simple vista, un proyecto común de React/Web3
    • El frontend en React ofrecía branding, panel de administración, gestión de campañas y flujo de creación de NFT
    • La API en Express manejaba autenticación, datos del panel, verificaciones KYB, creación y reclamo de NFT, carga de archivos y algunos endpoints stub
    • El script de seed creaba cuentas demo de superadmin, marca y consumidor, una campaña Demo NFT Drop y un NFT Demo Collectible con 100 unidades disponibles
  • El alcance real de la tarea era una simple función para mostrar la red de MetaMask
    • Implementar que la función asíncrona getChainId() en src/utils/ethereum.js llamara a eth_chainId y devolviera el valor hexadecimal parseado o null
    • Implementar que getNetworkLabel(chainId) en el mismo archivo buscara un nombre de red legible en el objeto NETWORKS ya existente
    • Modificar src/components/Wallet/ConnectWalletButton.js para que llamara a ambas funciones después de conectar la wallet
  • Tras terminar la implementación, se ejecutó el servidor de desarrollo, pero no iniciaba incluso después de mucho tiempo; al detectar algo extraño, el usuario desconectó el cable de internet

Código de ejecución oculto en tailwind.config.js

  • En ls -la, tailwind.config.js mostraba 30,987 bytes, pero en el editor solo se veían 97 líneas; wc -c confirmó el mismo tamaño
  • Cerca de la línea 95, detrás de miles de espacios en blanco, había un gran bloque de JavaScript difícil de leer
  • El código usaba técnicas típicas de ofuscación de JavaScript
    1. Guardar cadenas importantes en un arreglo grande
    2. Rotar el arreglo hasta que coincidiera una suma de verificación
    3. Ocultar el acceso a cadenas detrás de una función decodificadora
    4. Reemplazar nombres claros por índices numéricos y llamadas envolventes
  • Uno de los dos decodificadores reconstruía cadenas en formato Base64, y el otro aplicaba una clave por cadena junto con un cifrado de flujo similar a RC4; además, el arreglo de cadenas se rotaba antes de que coincidieran los índices del decodificador
  • Sin ejecutar el malware, se pudo desofuscar en este orden
    1. Extraer el arreglo de cadenas
    2. Reproducir la rotación del arreglo hasta alcanzar la suma de verificación esperada
    3. Reimplementar las funciones decodificadoras
    4. Sustituir llamadas como b(0x214), c(0x2f1, "key") por las cadenas reales
    5. Simplificar objetos wrapper y funciones auxiliares para revelar la intención de ejecución

Cómo funciona el dropper de primera etapa

  • El código desofuscado importaba child_process, os, fs, path y crypto, e instalaba axios y socket.io-client en el directorio temporal
  • Los manejadores a nivel de proceso para uncaughtException y unhandledRejection estaban configurados para ignorar errores
  • El flujo de ejecución de la carga útil remota era el siguiente
    • El UID era 59e605dd78fb2aafccd1b622f06a00ca
    • Descargaba datos desde http://45.146.252.17/api/service/59e605dd78fb2aafccd1b622f06a00ca
    • Usaba el UID y la cadena salt en crypto.scryptSync para derivar una clave de 32 bytes
    • Separaba la respuesta con el formato base64_iv:base64_ciphertext y la descifraba con aes-256-cbc
    • Escribía el texto plano en el archivo pack dentro del directorio temporal
    • Lo ejecutaba con child_process.execSync("node pack", { windowsHide: true, cwd: os.tmpdir() })
  • Se trataba de un dropper de malware que, más que realizar funciones propias, descargaba y ejecutaba otro software malicioso
  • El código descargado no tenía verificación de firma, lista permitida de hashes, fijación de origen, restricción de rutas ni barreras de prevención de ejecución, por lo que el endpoint remoto podía ejecutar código con los privilegios de la cuenta del sistema operativo que cargó la configuración de Tailwind

Análisis dinámico realizado en una VM aislada

  • Para no exponer el sistema host, se configuró con UTM una VM desechable de Windows 11 ARM
    • Memoria 4096 MiB
    • Disco 64 GiB
    • Red shared/NAT
    • Carpeta compartida desactivada
  • Se instalaron herramientas para recopilar el comportamiento del malware desde varias perspectivas
    • Wireshark: captura de paquetes y tráfico de C2/exfiltración
    • Sysinternals Sysmon: telemetría persistente de eventos de Windows
    • Procmon: recopilación de actividad de procesos, registro y sistema de archivos en ejecución
  • Se colocaron datos señuelo para confirmar qué intentaba recolectar el malware
    • Par de claves SSH
    • Repositorio privado de GitHub
    • Wallets de criptomonedas
    • Datos del navegador
    • Otros archivos que podrían interesar a un ladrón de información
  • En la VM se ejecutó yarn start y se observó el comportamiento durante unos 10 minutos; luego se obtuvieron run1-full.pcapng, run1-sysmon.evtx y stage2-pack.bin

Flujo de infección observado en la red

  • Las solicitudes HTTP hacia 45.146.252.17 tenían funciones separadas por puerto
    • Puerto 80: obtención de la carga útil de primera etapa, registro del host y envío de logs
    • Puerto 7641: puerta trasera de comando y control por Socket.IO
    • Puerto 7646: 2,588 subidas de archivos
    • Puerto 7649: 3 subidas de datos del navegador, más grandes pero menos numerosas
  • La primera solicitud fue GET /api/service/59e605dd78fb2aafccd1b622f06a00ca, y la respuesta tenía formato base64_iv:base64_ciphertext con Content-Length: 150897
  • tailwind.config.js descifraba la respuesta con AES-256-CBC, escribía el texto plano en %TEMP%\pack y luego lo ejecutaba con node pack
  • Las características de la carga útil de segunda etapa obtenida eran las siguientes
    • SHA-256: 68a64d8c015c06fd70bcb8c5878c1e430da827dd00b62f8e6ef69e76bb94de5b
    • Tamaño: 113136 bytes
    • Formato: JavaScript ASCII en una sola línea
  • El pack descifrado también estaba ofuscado de nuevo, pero como usaba las mismas técnicas que la primera etapa, pudo desofuscarse con el mismo procedimiento

Carga útil de segunda etapa compuesta por cuatro programas

  • pack no era un script único, sino un pequeño árbol de procesos que ejecutaba los siguientes cuatro programas
    1. Escribía y ejecutaba la puerta trasera de comando y control scdata en un directorio temporal
    2. Escribía y ejecutaba el ladrón de navegadores y billeteras ldata en un directorio temporal
    3. Ejecutaba un escáner recursivo de archivos directamente en memoria con node -e
    4. Ejecutaba un monitor del portapapeles directamente en memoria con node -e
  • La configuración principal incluía el UID 59e605dd78fb2aafccd1b622f06a00ca, la clave de usuario 308, el host 45.146.252.17, el puerto de Socket.IO 7641, el puerto de claves 7648, el puerto de subida del navegador 7649 y el puerto de subida de archivos 7646

scdata: puerta trasera de control remoto interactivo

  • scdata se escribía como archivo en %TEMP% y luego se ejecutaba con npm i axios socket.io-client ... && node scdata, quedando como un proceso hijo de larga duración
  • Tras ejecutarse, se configuraba para parecer un proceso legítimo
  • También instalaba paquetes adicionales necesarios para las funciones de control remoto
    • socket.io-client, ssh2, node-pty: terminal y funciones similares a SSH
    • sharp, screenshot-desktop, clipboardy, @nut-tree-fork/nut-js: capturas de pantalla, portapapeles, mover·hacer clic·desplazar el mouse, entrada de teclado
  • Los eventos de Socket.IO mostraban directamente el alcance del control remoto
    • Terminal: start-terminal, terminal-input, terminal-resize, stop-terminal, command
    • Verificación del host y captura: whour, capture
    • Control de entrada: mouseMove, mouseClick, mouseScroll, keyTap, keyCombo
    • Portapapeles: copyText, pasteText
    • SSH y cierre: start_ssh, ssh_input, kill
  • En el PCAP se confirmaron el polling·handshake WebSocket del puerto 7641 y el evento inicial del servidor whour; en Sysmon se detectaron en secuencia node.exe scdata, PowerShell consultando información del sistema y la instalación de dos grupos de paquetes npm
  • Más allá del simple robo de información, también daba al atacante funciones de shell y control del escritorio

ldata: ladrón de datos de navegadores y billeteras

  • ldata se escribía en %TEMP% y se ejecutaba con npm i axios && node ldata; su título de proceso era npm-cache
  • Recolectaba perfiles de navegador y almacenamiento de extensiones de billetera, los enviaba a http://45.146.252.17:7649/upload y consultaba el estado de LevelDB en /cldbs
  • Los navegadores objetivo estaban configurados ampliamente según el sistema operativo
    • Windows: Chrome, Edge, Brave, LT Browser
    • macOS: Chrome, Brave, Opera, LT Browser, Edge y el llavero de inicio de sesión local
    • Linux: carpetas de perfiles equivalentes de la familia Chromium
  • Desde los directorios Default o Profile* subía las siguientes bases de datos de Chromium
    • Login Data
    • Login Data For Account
    • Web Data
  • Después recorría Local Extension Settings/<extension-id>, y entre los ID de extensiones de billetera codificados estaba nkbihfbeogaeaoehlefnkodbefgpgknn de MetaMask
  • Para las primeras ocho rutas de extensiones de billetera, copiaba los directorios LevelDB a una carpeta temporal, subía los archivos individualmente y decidía si completar o reintentar según la respuesta de cldbs del servidor
  • Las tres subidas confirmadas en el puerto 7649 fueron las siguientes bases de datos
    • Login_Data.sqlite: 51,200 bytes
    • Login_Data_For_Account.sqlite: 51,200 bytes
    • Web_Data.sqlite: 262,144 bytes
  • Los datos de prueba no incluían contraseñas guardadas ni registros de tarjetas, pero sí seis valores de autocompletado y metadatos del navegador
  • Los navegadores basados en Chrome cifran algunos campos localmente, por lo que no siempre es posible recuperar secretos en texto plano solo con la base de datos
    • Aun así, combinados con secretos del sistema operativo·cookies·almacenamiento de extensiones·un navegador desbloqueado, pasan a formar parte de un pipeline de robo de credenciales
  • Terminaba después de subir los datos de navegador y LevelDB necesarios, o cuando el servidor lo marcaba como completado, sin esperar órdenes adicionales del operador

Escáner recursivo de archivos y monitor del portapapeles

  • El escáner recursivo de archivos se ejecutaba con node -e sin crear un archivo separado y comenzaba a recorrer desde el directorio personal del usuario
    • En Windows enumeraba las letras de unidad con Get-CimInstance Win32_LogicalDisk y también inspeccionaba la raíz de cada unidad
    • Los patrones de archivos recolectados incluían *.env*, *.md, *.pem, *.ini, *.secret, *.json, *.js, *.ts, *.csv, *.txt, *.doc, *.docx, *.pdf, *.xlsx, .zsh_history, .bash_history
    • ~/.ssh, ~/.aws, ~/.azure, ~/.config, ~/.foundry se trataban automáticamente como carpetas de interés
    • También buscaba nombres como metamask, bitcoin, btc, solana, secret phrase, private key
    • Los resultados se enviaban a http://45.146.252.17:7646/upload
  • En el entorno señuelo, realmente se subieron id_ed25519, id_ed25519.pub, History.txt, seed.js, password.js, tokens.js, ConnectWalletButton.js, ExportWallet.js, RegisterWallet.js, tailwind.config.js, aptos-cli.json y otros archivos
  • Mientras ldata se encargaba específicamente del almacenamiento de navegadores y billeteras, el escáner de archivos recolectaba de forma amplia claves SSH·configuraciones·código fuente·secretos locales·historial de shell·archivos de proyecto
  • El monitor del portapapeles también se ejecutaba con node -e, y su título de proceso era npm-compiler.log
    • Tras esperar unos segundos, leía repetidamente el portapapeles
    • En macOS usaba pbpaste; en Windows, powershell -NoProfile -NonInteractive Get-Clipboard
    • Los valores modificados se enviaban a http://45.146.252.17/api/service/makelog
  • Incluso sin interpretar el formato, podía capturar tal cual las contraseñas, frases de recuperación, claves privadas, tokens de API, códigos de un solo uso, URL de GitHub, direcciones de billetera y secretos de despliegue que copiara el usuario

Principios a seguir antes de ejecutar una prueba de contratación

  • Un repositorio de prueba enviado por alguien desconocido debe tratarse como código ejecutable no confiable hasta verificar su seguridad
  • Antes de yarn install, npm install, yarn build o yarn start, hay que revisar lo siguiente
    • package.json
    • scripts de ciclo de vida
    • archivos de configuración
    • hooks evidentes de dependencias
  • En este caso, el malware estaba oculto en tailwind.config.js, un archivo que mucha gente suele pasar por alto; los archivos de configuración, las dependencias y las herramientas de build también pueden ejecutarse como código
  • Los proyectos de entrevista deben ejecutarse en una VM o contenedor desechable donde no haya secretos reales montados
    • perfil personal del navegador
    • administrador de contraseñas
    • claves SSH
    • billeteras de criptomonedas
    • tokens de GitHub
    • credenciales de nube
    • sesiones bancarias
    • cuenta de correo principal
  • Para pruebas que requieran cuentas o billeteras, se debe usar una identidad de prueba nueva, sin fondos y no reutilizada en otros servicios
  • Si se trata de una tarea Web3, hay que usar solo testnet y no conectar una billetera real a un proyecto desconocido, aunque parezca inofensivo

Indicadores para verificar si hubo infección

  • En macOS y Linux, revisar primero lo siguiente
    • procesos en ejecución relacionados con node, pack, scdata, ldata, npm-compiler, vhost.ctl
    • conexiones a 45.146.252.17 o a los puertos 7641, 7646, 7649
    • pack, scdata, ldata, vhost.ctl bajo directorios temporales, Downloads, Desktop, Documents y Library
    • archivo marcador */.npm/vhost.ctl
    • cadenas IP, UID, /api/service/makelog, node scdata, node ldata, node pack dentro del proyecto descargado
  • En Windows, revisar lo siguiente
    • entre los procesos node, npm, cmd, powershell, los que tengan en la línea de comandos pack, scdata, ldata, node -e, la dirección IP o Get-Clipboard
    • conexiones TCP abiertas a 45.146.252.17 o al puerto remoto 7641, 7646, 7649
    • pack, scdata, ldata, vhost.ctl, .npm\vhost.ctl bajo %TEMP%
    • cadenas C2 conocidas dentro del directorio donde se clonó el repositorio de la entrevista
  • Si se encuentra aunque sea uno de los siguientes: un proceso de Node activo, una conexión a esa IP o alguno de los artefactos pack·scdata·ldata, debe considerarse que el sistema estuvo expuesto
  • Estos indicadores son puntos de revisión iniciales específicos de las muestras analizadas y no constituyen un procedimiento forense completo

Limpieza del sistema infectado y rotación de secretos

  • Lo primero es desconectar la computadora de la red y no seguir explorando, depurando ni copiando secretos nuevos dentro del entorno infectado
  • Si se necesita evidencia, conservar lo siguiente antes de borrar nada
    • lista de procesos
    • conexiones de red
    • archivos sospechosos
    • historial del navegador
    • repositorio malicioso
  • Después, terminar los procesos relacionados con node pack, node scdata, node ldata, node -e, npm-compiler, vhost.ctl y eliminar pack, scdata, ldata, .npm/vhost.ctl del directorio temporal
  • Si es una VM desechable y no hace falta preservar trabajo legítimo de Node, se puede usar pkill node en macOS y Linux o forzar el cierre de todos los procesos node en Windows
  • Borrar el repositorio malicioso y node_modules, pero si hace falta análisis adicional, conservar una copia ZIP sin conexión
  • Borrar archivos no es suficiente; también hay que rotar o revocar la siguiente información secreta
    • claves SSH
    • tokens de GitHub y npm
    • claves de nube y claves API
    • secretos de despliegue
    • contraseñas guardadas en el navegador
    • sesiones de inicio de sesión activas
  • Si existe la posibilidad de que la semilla o la clave privada de una wallet haya tocado el sistema infectado, hay que crear una wallet nueva desde un dispositivo limpio y transferir los fondos

Un ataque que explota el límite de confianza de las herramientas de desarrollo

  • Los productos antivirus tradicionales no detectaron este repositorio, y ni siquiera los populares agentes de codificación con IA usados para resolver la prueba identificaron el malware oculto en el proyecto
  • Como Tailwind evalúa los archivos de configuración de JavaScript como módulos de Node, el IIFE de la línea 95 se ejecuta en el momento en que la herramienta de desarrollo, el script de build, la integración del editor, Tailwind CLI, el bundler o una tarea de CI carga la configuración
  • La configuración legítima de Tailwind llegaba hasta la línea 94, y después se habían agregado aproximadamente 27 KB de código ofuscado
  • El payload descargado se ejecuta con los mismos privilegios del sistema operativo que el usuario que cargó la configuración
    • puede acceder a archivos locales, perfiles del navegador, credenciales guardadas, datos de extensiones de wallets, claves SSH, variables de entorno, tokens de paquetes, credenciales de nube, código fuente y portapapeles
    • si se ejecuta en CI, también pueden quedar expuestos secretos de despliegue, artefactos de build, credenciales de registro y tokens de acceso a producción
  • La corrección necesaria es eliminar por completo el bloque de JavaScript ofuscado de tailwind.config.js

Aún no hay comentarios.

Aún no hay comentarios.