Resumen ejecutivo
Slow Pisces (también conocido como Jade Sleet, TraderTraitor, PUKCHONG) es un actor estado-nación de Corea del Norte enfocado principalmente en generar ingresos para el régimen de la RPDC, generalmente atacando a grandes organizaciones en el sector de las criptomonedas. En este artículo se analiza su campaña, que creemos está relacionada con los recientes robos de criptomonedas.
En esta campaña, Slow Pisces se puso en contacto con desarrolladores de criptomonedas en LinkedIn, haciéndose pasar por posibles empleadores y enviando malware camuflado en los retos de código. Estos retos requieren que los desarrolladores ejecuten un proyecto comprometido, infectando sus sistemas con un malware que hemos denominado RN Loader y RN Stealer.
Al parecer, el grupo robó más de 1000 millones de dólares del sector de las criptomonedas en 2023. Lo han logrado utilizando varios métodos, entre ellos aplicaciones comerciales falsas, malware distribuido a través del Administrador de paquetes de Node (NPM) y compromisos de la cadena de suministro.
En Diciembre de 2024, el FBI atribuyó a Slow Pisces el robo de 308 millones de dólares de una empresa de criptomonedas con sede en Japón. Más recientemente, el grupo apareció en los titulares por su presunta implicación en el robo de 1500 millones de dólares de una bolsa de criptomonedas de Dubai.
Hemos compartido nuestra inteligencia de amenazas con los analistas de GitHub y LinkedIn para deshabilitar con las cuentas y repositorios relevantes.
En respuesta, brindaron la siguiente declaración:
GitHub y LinkedIn eliminaron estas cuentas maliciosas por violar nuestros respectivos términos de servicio. En todos nuestros productos utilizamos tecnología automatizada, combinada con equipos de expertos en investigación e informes de los miembros, para combatir a los actores maliciosos y hacer cumplir las condiciones de servicio. Seguimos evolucionando y mejorando nuestros procesos y animamos a nuestros clientes y afiliados a informar de cualquier actividad sospechosa.
Información adicional
- Los usuarios de GitHub pueden encontrar más información en nuestras Políticas de uso aceptable y páginas de denuncia de abusos y spam.
- Los usuarios de LinkedIn pueden obtener más información sobre cómo identificar y denunciar los abusos aquí: Reconozca y denuncie el spam y los contenidos inapropiados y abusivos | Ayuda de LinkedIn
En este informe se detalla cómo Slow Pisces oculta el malware dentro de sus desafíos de código y describe las herramientas posteriores del grupo, con el objetivo de proporcionar a la industria en general un mejor entendimiento de esta amenaza.
Los clientes de Palo Alto Networks están mejor protegidos frente a las amenazas comentadas en este artículo a través de nuestras suscripciones a Firewall de nueva generación con Advanced URL Filtering y Advanced DNS Security.
Si cree que su seguridad podría haber sido comprometida o si tiene un asunto urgente, póngase en contacto con el equipo de respuesta a incidentes de Unit 42.
Temas relacionados con Unit 42 | Cryptocurrency, DPRK |
Análisis técnico
Nuestra visibilidad de esta campaña sigue a grandes rasgos tres pasos, ilustrados a continuación en la Figura 1.

Fase 1 - Señuelos PDF
Slow Pisces comenzó suplantando la identidad de reclutadores en LinkedIn e interactuando con objetivos potenciales, enviándoles un PDF benigno con una descripción del trabajo, como se muestra a continuación en la Figura 2. Si los objetivos potenciales lo solicitaban, los atacantes les presentaban un reto de código consistente en varias tareas descritas en una hoja de preguntas.

Hemos observado cómo Slow Pisces se hacía pasar por varias organizaciones con estos señuelos, principalmente en el sector de las criptomonedas. Las hojas de preguntas incluyen tareas genéricas de desarrollo de software y un reto de código de un "proyecto real", que enlaza con un repositorio de GitHub que se muestra en la Figura 3 a continuación.

Fase 2 - Repositorios de GitHub
Slow Pisces presentó a los objetivos los denominados retos de código como proyectos en repositorios de GitHub. Los repositorios contenían código adaptado de proyectos de código abierto, incluidas aplicaciones para visualizar y analizar:
- Datos bursátiles
- Estadísticas de las ligas de fútbol europeas
- Datos meteorológicos
- Precios de las criptomonedas
El grupo utilizó principalmente proyectos en Python o JavaScript, probablemente en función de si el objetivo solicitaba un puesto de desarrollo “front-end” o “back-end”. También vimos repositorios basados en Java en esta campaña, aunque fueron mucho menos comunes, con solo dos instancias suplantando una aplicación de criptomoneda llamada jCoin.
Esta escasez indica que los atacantes podrían haber creado repositorios bajo demanda, basándose en el lenguaje de programación preferido del objetivo. En consecuencia, el grupo utilizó con mayor frecuencia lenguajes más populares en el sector de las criptomonedas, como JavaScript y Python. Del mismo modo, es posible que también existan repositorios sin descubrir para otros lenguajes de programación.
Fase 3a - Repositorio Python
A finales de 2024, el grupo utilizó un proyecto que se muestra a continuación en la Figura 4 titulado "Analizador de patrones de existencias" adaptado de un repositorio legítimo.

La mayor parte del código del repositorio es benigno. Cuando los objetivos intentan ejecutar el proyecto según la hoja de preguntas, los datos se obtienen de tres ubicaciones remotas:
- hxxps://en.wikipedia[.]org/wiki/List_of_S%26P_500_companies
- hxxps://en.wikipedia[.]org/wiki/Currency_pair
- hxxps://en.stockslab[.]org/symbols/sp500
Dos de las URL extraen datos de Wikipedia. La tercera URL utiliza un dominio controlado por Slow Pisces. Este patrón (utilizar múltiples fuentes de datos, la mayoría legítimas, pero una maliciosa) es común en los repositorios Python del grupo.
El servidor de mando y control (C2) malicioso está configurado para imitar el formato de las fuentes legítimas. En este caso, utiliza el subdominio en y el dominio de nivel superior (TLD) org como vemos arriba para el dominio legítimo de Wikipedia.
Deserialización de YAML
Slow Pisces podría simplemente colocar malware directamente en el repositorio o ejecutar código desde el servidor C2 utilizando las funciones eval o exec integradas en Python. Sin embargo, estas técnicas son fácilmente detectables, tanto por inspección manual como por soluciones antivirus.
En su lugar, Slow Pisces se asegura primero de que el servidor C2 responde con datos de aplicación válidos. Por ejemplo, el repositorio mencionado anteriormente espera una lista de símbolos de empresas del S&P 500. La URL C2 responde inicialmente con estos datos en una lista con formato JSON.
Los actores sólo envían una carga maliciosa a objetivos validados, probablemente basándose en la dirección IP, la geolocalización, la hora y los encabezados de las solicitudes HTTP. El hecho de centrarse en las personas contactadas a través de LinkedIn, a diferencia de las amplias campañas de phishing, permite al grupo controlar estrictamente las últimas fases de la campaña y entregar las cargas únicamente a las víctimas esperadas.
Para evitar las sospechosas funciones eval y exec , Slow Pisces utiliza deserialización YAML para ejecutar su carga, como se muestra en la Figura 5.

Este código obtiene datos del servidor C2 a través de HTTPS y comprueba la cabecera de respuesta Content-Type. Si la cabecera indica datos JSON (application/json), el código analiza y devuelve el JSON a la aplicación.
Si la respuesta indica datos YAML (application/yaml), el código utiliza la función yaml.load() de la biblioteca PyYAML para analizar los datos. Esta función es intrínsecamente insegura y la documentación de PyYAML recomienda explícitamente yaml.safe_load() para entradas no fiables.
YAML se utiliza normalmente para archivos de configuración, como el ejemplo que se muestra a continuación:
1 2 3 4 5 6 7 8 9 |
nombre de usuario: slow contraseña: pisces api: clave: supersecret url: example.com |
Sin embargo, yaml.load() puede serializar y deserializar objetos Python arbitrarios, no solo datos YAML válidos. Por ejemplo, el siguiente código Python imprime los números 0-4:
1 |
range(0, 5) |
Si este código se serializara utilizando yaml.dump() se convertiría en lo siguiente:
1 2 3 4 5 6 7 |
!!python/object/apply:builtins.range - 0 - 5 - 1 |
Finalmente, cuando estos datos se pasen a yaml.load() se ejecutará el código original: range(0, 5).
Esto pone de manifiesto un posible punto de detección, ya que los contenidos del repositorio Python, y el malware que utiliza la deserialización YAML en general, contienen !!python/object/apply:builtins si el contenido utiliza una función Python built-in.
Las siguientes fases en la Tabla 1 existen principalmente en memoria y, por lo general, no dejan huella en el disco. Para ayudar a la comunidad en la detección y concienciación, hemos subido estas muestras a VirusTotal. La muestra de deserialización YAML ejecuta el malware que hemos denominado RN Loader y RN Stealer basándonos en el formato de token C2 que observamos en RN Stealer, del que hablamos en las siguientes secciones.
Fase | SHA256 Hash |
Muestra de deserialización YAML | 47e997b85ed3f51d2b1d37a6a61ae72185d9ceaf519e2fdb53bf7e761b7bc08f |
RN Loader | 937c533bddb8bbcd908b62f2bf48e5bc11160505df20fea91d9600d999eafa79 |
RN Stealer | e89bf606fbed8f68127934758726bbb5e68e751427f3bcad3ddf883cb2b50fc7 |
Tabla 1. Muestras del repositorio Python.
La muestra de deserialización YAML de Slow Pisces comienza creando la carpeta Public en el directorio personal de la víctima y creando un nuevo archivo en ese directorio llamado __init__.py. Los datos Base64 incrustados se descodifican y se escriben en este archivo, que contiene la siguiente fase de infección (RN Loader), que se ejecuta a continuación.
RN Loader
Este archivo recién creado para el RN Loader en ~/Public/__init__.py se borra a sí mismo después de la ejecución, asegurando que existe únicamente en memoria. Envía información básica sobre la máquina víctima y el sistema operativo a través de HTTPS al mismo C2 en en.stockslab[.]org, seguido de un bucle de comandos con las siguientes opciones en la Tabla 2.
Código | Descripción |
0 | Suspender durante 20 segundos |
1 | Decodifica en base64 el contenido enviado y lo guarda en el archivo init.dll para Windows o init para el resto de sistemas operativos.
Establece una variable de entorno X_DATABASE_NAME a una cadena vacía. Carga y ejecuta la DLL descargada utilizando ctypes.cdll.LoadLibrary. |
2 | Decodifica en base64 el contenido enviado y lo ejecuta utilizando la función exec incorporada en Python. |
3 | Decodifica en base64 el contenido enviado y un parámetro. El contenido se guarda en el archivo dockerd, mientras que el parámetro se guarda como docker-init.
dockerd se ejecuta entonces en un nuevo proceso, con docker-init suministrado como argumento de línea de comandos. |
9 | Finaliza la ejecución. |
Tabla 2. Tabla de comandos del cargador RN.
Las cargas útlies del bucle de comandos de la Tabla 2 que utilizan las opciones 1 y 3 son actualmente desconocidas y es probable que se activen por condiciones específicas. Sin embargo, hemos recuperado un “infostealer”basado en Python entregado por la opción 2, y rastreamos este malware como RN Stealer.
RN Stealer
El RN Stealer genera primero un ID aleatorio de la víctima, utilizado posteriormente como cookie en todas las comunicaciones con el servidor C2. A continuación, solicita una clave XOR al servidor para cifrar los datos exfiltrados.
La comunicación con el servidor C2 se produce a través de HTTPS, utilizando tokens codificados en Base64 para identificar los tipos de solicitud y respuesta. La carga analizada incluye cuatro tipos de fichas:
- R0 - solicitar la clave XOR
- R64 - exfiltrar datos
- R128 - exfiltrar datos comprimidos
- R256 - Infostealercompleto
El formato de estos tipos de token - la letra R seguida de un número entero N - nos llevó a nombrar esta carga. Llamamos a la carga RN Stealer y a la fase precedente RN Loader.
Hemos recuperado el script de esta muestra de RN Stealer de un sistema macOS. Como tal, los autores de la amenaza adaptaron esta muestra para robar información específica de los dispositivos macOS, incluyendo:
- Información básica sobre la víctima: Nombre de usuario, nombre de la máquina y arquitectura
- Aplicaciones instaladas
- Un listado de directorios y el contenido de nivel superior del directorio de usuario de la víctima
- El archivo login.keychain-db que almacena las credenciales guardadas en los sistemas macOS.
- Claves SSH almacenadas
- Archivos de configuración para AWS, Kubernetes y Google Cloud
Los datos recopilados por el RN Stealer probablemente determinan si es necesario un acceso persistente. Si es así, podemos deducir los siguientes pasos para esta cadena de infección de Python:
- El servidor C2 comprueba las víctimas que llaman según criterios desconocidos. Las víctimas válidas reciben una carga útil de deserialización YAML. Las víctimas no válidas reciben datos JSON benignos.
- La carga de deserialización establece un bucle de comandos con el servidor C2, exfiltrando información básica de la víctima y entregando un infostealera medida en Python a través de la opción 2 de código en la Tabla 2.
- El infostealer recopila información más detallada de la víctima, que los atacantes probablemente utilizaron para determinar si necesitaban acceso continuado.
- Si se requiere un acceso continuado, el servidor C2 entrega una carga mediante las opciones 1 o 3 de código
- Si el acceso ya no es necesario, la opción 9 de código termina la ejecución del malware, eliminando todo acceso ya que la carga reside únicamente en la memoria.
Fase 3b - Repositorio JavaScript
Si las víctimas objetivo solicitaron una posición de JavaScript, podrían encontrarse en su lugar con un proyecto de "Cuadro de mando de criptomoneda", similar al ejemplo de la Figura 6 a continuación.

Esta aplicación contiene un archivo .env con el C2 y la fuente de datos legítima:
- PORT=3000
- COINGECKO_API_URL=hxxps://api.coingecko[.]com/api/v3
- JQUERY_API_URL=hxxps://update.jquerycloud[.]io/api/v1
El valor COINGECKO_API_URL se utiliza para obtener datos para el panel de criptomonedas, mientras que el valor JQUERY_API_URL representa un servidor C2 controlado por Slow Pisces. De forma similar al repositorio Python, el servidor C2 JavaScript solo entrega cargas a objetivos validados, de lo contrario, responde con un número de versión.
El repositorio utiliza la herramienta de creación de plantillas Embedded JavaScript (EJS), pasando las respuestas del servidor C2 a la función ejs.render(), que se muestra a continuación en la figura 7.

Al igual que el uso de yaml.load(), esta es otra técnica que emplea Slow Pisces para ocultar la ejecución de código arbitrario desde sus servidores C2, y este método quizás solo sea aparente cuando se visualiza una carga válida.
La función de renderizado de EJS acepta varios parámetros, uno de los cuales se denomina “view options”. Dentro de este, se puede suministrar y ejecutar código JavaScript arbitrario a través de la clave escapeFunction.
Un investigador taiwanés que se hace llamar “Huli” discutió los detalles técnicos de cómo esto resulta en la ejecución de código arbitrario en una publicación de CTF. Sin embargo, podemos entender suficientemente que una carga estructurada como se muestra en la Figura 8 dará lugar a que el código contenido en escapeFunction se ejecute cuando se pase a ejs.render().

Lamentablemente, no pudimos recuperar la totalidad de esta carga. Como tal, solo podemos suponer que se crea un nuevo directorio .jql bajo el directorio personal del usuario donde se suelta un archivo llamado helper.js, que contiene datos codificados en Base64.
Infraestructura
La línea de tiempo que aparece a continuación en la Figura 9 detalla la infraestructura C2 utilizada en esta campaña desde febrero de 2024 a febrero de 2025, agrupada por el tipo de repositorio servido (JavaScript o Python).

Como ya se ha mencionado, los dominios de la infraestructura de esta campaña pueden imitar el formato de las fuentes legítimas utilizadas junto a ellos, utilizando con frecuencia subdominios como api o cdn. Hemos descubierto infraestructura asociadas a esta campaña hasta el momento de redactar este artículo.
Conclusión
Este informe ha cubierto la campaña más reciente de Slow Pisces, que se hizo pasar por reclutadores a través de LinkedIn para dirigirse a desarrolladores en el sector de las criptomonedas con retos de código maliciosos. Si bien no pudimos recuperar la cadena de ataque completa para los repositorios JavaScript, la versión Python de la campaña entregó dos nuevas cargas que hemos denominado RN Loader y RN Stealer.
Utilizar LinkedIn y GitHub de esta manera no es algo extraño. Múltiples grupos afiliados a la RPDC han utilizado tácticas similares como Alluring Pisces y Contagious Interview.
Estos grupos no presentan solapamientos operativos. Sin embargo, cabe destacar que estas campañas utilizan vectores de infección iniciales similares.
Slow Pisces destaca entre las campañas de sus compañeros en seguridad operativa. La entrega de las cargas en cada fase está fuertemente vigilada, existiendo solo en memoria. Y las herramientas de la fase posterior del grupo solo se implementan cuando es necesario.
En concreto, el grupo hizo uso de dos técnicas para ocultar la funcionalidad:
- Deserialización de YAML
- EJS escapeFunction
Ambas técnicas dificultan enormemente el análisis, la detección y la búsqueda. Del mismo modo, los desarrolladores relativamente nuevos o inexpertos en el sector de las criptomonedas tendrían dificultades para identificar estos repositorios como maliciosos.
Basándose en los informes públicos de robos de criptomonedas, esta campaña parece tener mucho éxito y es probable que persista en 2025. Si bien este artículo destacaba dos oportunidades potenciales de detección para la deserialización YAML y las cargas EJS escapeFunction, la mitigación más eficaz sigue siendo la segregación estricta de los dispositivos corporativos y personales. Esto ayuda a prevenir el compromiso de los sistemas corporativos a partir de campañas de ingeniería social dirigidas.
Protección y mitigación de Palo Alto Networks
Los clientes de Palo Alto Networks están mejor protegidos frente a las amenazas mencionadas gracias a los siguientes productos:
Si cree que su seguridad puede haber sido comprometida o si tiene un asunto urgente, póngase en contacto con el equipo de respuesta a incidentes de Unit 42 o llame al:
- América del Norte: Llamada gratuita: +1 (866) 486-4842 (866.4.UNIT42)
- Reino Unido: +44.20.3743.3660
- Europa y Oriente Medio: +31.20.299.3130
- Asia: +65.6983.8730
- Japón: +81.50.1790.0200
- Australia: +61.2.4062.7950
- India: 00080005045107
Palo Alto Networks ha compartido estos hallazgos con nuestros colegas de la Cyber Threat Alliance (CTA). Los miembros de la CTA utilizan esta inteligencia para implementar rápidamente protecciones a sus clientes y perturbar sistemáticamente a los ciberactores maliciosos. Obtenga más información sobre la Cyber Threat Alliance.
Indicadores de Compromiso
Dominio | Dirección IP | Visto por primera vez | Visto por última vez | Repositorio |
getstockprice[.]com | 70.34.245[.]118 | 2025-02-03 | 2025-02-20 | Python |
cdn[.]clubinfo[.]io | 5.206.227[.]51 | 2025-01-21 | 2025-02-19 | Python |
getstockprice[.]info | 131.226.2[.]120 | 2025-01-21 | 2025-01-23 | Python |
api[.]stockinfo[.]io | 136.244.93[.]248 | 2024-10-30 | 2024-11-11 | Python |
cdn[.]logoeye[.]net | 54.39.83[.]151 | 2024-10-29 | 2024-11-03 | Python |
en[.]wfinance[.]org | 195.133.26[.]32 | 2024-10-12 | 2024-11-01 | Python |
en[.]stocksindex[.]org | 185.236.231[.]224 | 2024-09-11 | 2024-10-04 | Python |
cdn[.]jqueryversion[.]net | 194.11.226[.]16 | 2024-08-23 | 2024-09-23 | JavaScript |
en[.]stockslab[.]org | 91.103.140[.]191 | 2024-08-19 | 2024-09-12 | Python |
update[.]jquerycloud[.]io | 192.236.199[.]57 | 2024-07-03 | 2024-08-22 | JavaScript |
cdn[.]soccerlab[.]io | 146.70.124[.]70 | 2024-08-07 | 2024-08-21 | Python |
api[.]coinpricehub[.]io | 45.141.58[.]40 | 2024-05-06 | 2024-08-06 | Java |
cdn[.]leaguehub[.]net | 5.133.9[.]252 | 2024-07-15 | 2024-07-21 | Python |
cdn[.]clublogos[.]io | 146.19.173[.]29 | 2024-06-24 | 2024-07-12 | Python |
api[.]jquery-release[.]com | 146.70.125[.]120 | 2024-06-10 | 2024-06-28 | JavaScript |
cdn[.]logosports[.]net | 185.62.58[.]74 | 2024-05-08 | 2024-06-23 | Python |
skypredict[.]org | 80.82.77[.]80 | 2024-05-06 | 2024-06-16 | JavaScript |
api[.]bitzone[.]io | 192.248.145[.]210 | 2024-04-25 | 2024-05-13 | Python |
weatherdatahub[.]org | 194.15.112[.]200 | 2024-04-05 | 2024-05-03 | JavaScript |
api[.]ethzone[.]io | 91.234.199[.]90 | 2024-04-16 | 2024-04-24 | Python |
api[.]fivebit[.]io | 185.216.144[.]41 | 2024-04-08 | 2024-04-14 | Python |
blockprices[.]io | 91.193.18[.]201 | 2024-03-15 | 2024-04-09 | JavaScript |
api[.]coinhar[.]io | 185.62.58[.]122 | 2024-03-26 | 2024-04-09 | Python |
mavenradar[.]com | 23.254.230[.]253 | 2024-02-21 | 2024-03-26 | JavaScript |
indobit[.]io | 146.70.88[.]126 | 2024-03-19 | 2024-03-20 | Python |
api[.]thaibit[.]io | 79.137.248[.]193 | 2024-03-07 | 2024-03-09 | Python |
chainanalyser[.]com | 38.180.62[.]135 | 2024-02-23 | 2024-03-06 | JavaScript |
Recursos adicionales
- Corea del Norte, responsable del pirateo de Bybit por valor de 1500 millones de dólares - Centro de Denuncias de Delitos en Internet (IC3)
- El FBI, el DC3 y la NPA identifican a los ciberactores de Corea del Norte, rastreados como TraderTraitor, responsables del robo de 308 millones de dólares de Bitcoin.DMM.com - FBI
- Alerta de seguridad: campaña de ingeniería social dirigida a empleados del sector tecnológico - GitHub Blog
- Corea del Norte aprovecha a un proveedor de SaaS en un ataque selectivo a la cadena de suministro - Mandiant, Google Cloud