Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
npm-incident-response — Escáner para el ataque a la cadena de suministro keyv/cacheable: detecta paquetes npm comprometidos, verifica los hashes de los payloads y encuentra implantes de persistencia en los modos repo y host. | Kitploit
Herramientas/GitHubGitHub/securest8/npm-incident-response
Escáneres de VulnerabilidadesMecanismos de PersistenciaAnálisis de MalwareForensia DigitalSeguridad de Cadena de SuministroRespuesta a Incidentes
GitHubsecurest8/npm-incident-response

npm-incident-response

Escáner para el ataque a la cadena de suministro keyv/cacheable: detecta paquetes npm comprometidos, verifica los hashes de los payloads y encuentra implantes de persistencia en los modos repo y host.

Ver Repositorio
215hace 1 mesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

npm-incident-response

English | Português

Escáner independiente para el incidente de la cadena de suministro de keyv/cacheable ("Shai-Hulud: Here We Go Again", 4 ago 2026) — 440+ paquetes npm comprometidos por un gusano autopropagante que roba credenciales de nube/CI e instala persistencia con un interruptor de hombre muerto.

Detecta, en minutos y sin instalar nada:

  • Paquetes comprometidos en package-lock.json, npm-shrinkwrap.json, yarn.lock (v1 y Berry), pnpm-lock.yaml y bun.lock — incluidas las dependencias transitivas, con la cadena completa (p. ej. eslint → file-entry-cache → flat-cache → [email protected]);
  • Payloads instalados en node_modules (nombre + hash SHA-256 de los artefactos conocidos);
  • Variantes que aún no están en las listas de IOCs (heurística: scripts de ciclo de vida sospechosos, archivos con los nombres del gusano) — siempre marcadas como SUSPECT, nunca confirmadas sin un hash;
  • Implantes de persistencia en el host: LaunchAgent (macOS), servicio de usuario de systemd + linger (Linux), hooks en .claude/settings.json y .vscode/tasks.json, artefactos temporales (bun-dl-*);
  • El interruptor de hombre muerto: el implante monitoriza un token de GitHub y ejecuta un comando remoto cuando la revocación devuelve 4xx. Rotar las credenciales antes de limpiar el host dispara la trampa — el informe te lo advierte, y el orden de respuesta más abajo evita el error.
  • Comprender el ataque

    1. La cuenta de mantenedor de las familias keyv/cacheable fue comprometida; el atacante publicó nuevas versiones con un hook "preinstall": "node setup.mjs" — código que se ejecuta antes de que el paquete se instale, con los privilegios de quien haya ejecutado npm install.
    2. setup.mjs descarga el runtime de Bun desde GitHub y ejecuta el payload dentro de él — evasión contra herramientas que solo monitorizan procesos node.
    3. Math_Symbol.js (~728 KB, ofuscado) roba credenciales: metadatos de instancia de AWS, claves de AWS/GCP/Azure, tokens de Vault, cuentas de servicio de Kubernetes, secretos de GitHub Actions, tokens de npm, además de un barrido regex genérico en busca de claves privadas y bearer tokens en el disco.
    4. Es un gusano: con el token de npm robado, inyecta el mismo hook en otros paquetes que esa identidad puede publicar, recalcula los hashes de integridad y vuelve a publicar. Así pasó de ~10 a cientos de paquetes.
    5. Exfiltra sin un C2 fijo (repositorios de GitHub creados sobre la marcha, DNS) y deja una trampa detrás — ver más abajo.

    Los dos vectores (el segundo es más sutil)

    • Vector A — instalación: cualquiera que haya ejecutado npm install/npm ci con los scripts de ciclo de vida habilitados desde 2026-08-04 09:35 UTC. Con --ignore-scripts, el hook no se ejecutó.
    • Vector B — clonación: el repositorio fuente recibió hooks de autoarranque en .claude/settings.json (SessionStart) y .vscode/tasks.json (folderOpen) que ejecutan el cargador cuando se abre la carpeta clonada — sin npm install, sin instalar nada. Esto incluye a quienes clonaron el repositorio para investigar el incidente y a agentes de codificación de IA que abrieron el directorio — uno de los primeros casos públicos de hooks de agentes de IA (.claude/) usados como vector de cadena de suministro.

    La trampa (interruptor de hombre muerto)

    El implante instala un "watcher" (gh-token-monitor) mantenido vivo por un LaunchAgent (macOS) o un servicio de usuario de systemd + loginctl enable-linger (Linux). Cada 60 segundos valida el token de GitHub robado contra la API. Mientras el token funcione, no pasa nada. Cuando la respuesta se vuelve 4xx — es decir, el momento en que revocas el token — ejecuta, vía eval, el contenido de ~/.config/gh-token-monitor/handler: un comando arbitrario definido remotamente por el atacante. El análisis público no sabe qué contiene — podría ser destrucción de datos, reimplantación, ransomware o nada. El riesgo no es evaluable; por eso el orden de respuesta es absoluto.

    Tres propiedades que cambian la respuesta:

    • Aislar la red es seguro: sin conectividad no hay respuesta HTTP, por lo que no hay 4xx — la trampa no se dispara y la exfiltración se detiene. Aísla primero, no apagues (la memoria volátil es evidencia).
    • Es de un solo disparo y se autolimpia después de dispararse — el comportamiento queda sin explicación, sin artefactos que investigar.
    • TTL de ~24 h: el watcher se autodestruye después de un día. La ausencia de artefactos no prueba que la máquina estuviera limpia — el escáner advierte de esto en el modo host.

    Por qué las defensas habituales suelen pasarlo por alto

    • "La firma era válida" — [email protected] se publicó con una atestación SLSA en regla. La procedencia da fe de la integridad de la compilación, no de la fuente: el workflow legítimo compiló código ya troyanizado.
    • "El diff de código no cambió" — correcto: la librería en sí no fue modificada. La malicia está en package.json (el hook preinstall) y en dos archivos nuevos añadidos al paquete (setup.mjs, Math_Symbol.js).
    • "No usamos keyv" — sí lo usas, indirectamente: la cadena más común es eslint → file-entry-cache → flat-cache → keyv. Por eso el escáner muestra la cadena en cada hallazgo.
    • "Nadie ejecutó npm install" — insuficiente: ver Vector B.

    Qué es el script de este repositorio

    scan.mjs tiene las siguientes propiedades — importantes para cualquiera que responda a un incidente de cadena de suministro:

    • Un solo archivo, ~880 líneas legibles, cero dependencias. Sin npm install. Audita todo scan.mjs en 15 minutos antes de ejecutarlo.
    • Cero egreso. Ningún dato sale de tu máquina. Sin telemetría, sin "enviar el resultado para análisis". La única operación de red es --update (descargar un manifiesto de IOCs nuevo), explícita y opcional.
    • Solo lectura. El escáner no modifica, elimina ni ejecuta nada de lo que encuentra.
    • Funciona sin conexión. docker run --network=none o una máquina aislada: solo copia scan.mjs + iocs.json.

    Cómo usarlo en tu empresa

    Requisito: Node.js ≥ 18 (cualquier máquina con npm ya lo tiene). Descarga los dos archivos — scan.mjs + iocs.json — y listo: sin instalación.

    Aviso: si clonaste este repositorio completo, la carpeta fixtures/ contiene IOCs inertes usados en las pruebas (nombres y versiones reales, contenido ficticio — sin malware). El escáner la omite automáticamente y lo advierte en la salida; los hallazgos de allí solo aparecen si la escaneas a propósito.

    Hay dos modos de ejecución que responden a preguntas distintas, y eso es lo que decide dónde ejecutarlo:

    • El modo repo lee lockfiles y node_modules — y los lockfiles viven en git, por lo que se puede centralizar: una persona escanea todos los repositorios de la empresa.
    • El modo host busca el implante (watcher, LaunchAgent/systemd, hooks de IDE), que vive en la máquina donde se ejecutó el código — eso no está en git y no se puede centralizar.

    Paso 1 — AppSec escanea todos los repositorios (una persona, una máquina)

    root@kitploit:~
    node scan.mjs repo /folder/with/all/the/repos --json=result.json --html=report.html
    

    Responde "qué proyectos están expuestos" en minutos, sin involucrar a nadie. Acepta múltiples rutas; recorre subdirectorios (incluidos monorepos y workspaces).

    Paso 2 — quienes trabajaron en los proyectos afectados escanean su propia máquina

    Para cada proyecto con un hallazgo, identifica quién lo tocó desde 2026-08-04 09:35 UTC (git log, registros de CI). Esas personas ejecutan, en su máquina:

    root@kitploit:~
    node scan.mjs        # current directory + host, in ~30 seconds
    

    Dentro del alcance: cualquiera que (a) haya ejecutado npm install/npm ci en la ventana; o (b) simplemente haya clonado y abierto la carpeta en VS Code o en un agente de IA — el Vector B no necesita instalación.

    Como el coste es de ~30 segundos y el embudo puede tener fugas (un clon suelto, un proyecto personal), el mensaje interno más seguro es: cada desarrollador ejecuta node scan.mjs una vez y envía el --json/--html a AppSec. El envío es manual por diseño — el escáner no tiene telemetría (cero egreso).

    Paso 3 — runners de CI y servidores de compilación

    Máxima prioridad: aquí es donde viven las credenciales más valiosas. Aquí el escáner tiene dos roles distintos — uno para el pasado, otro para el futuro:

    Triaje del pasado — NO escanees el runner para decidir. La pregunta "¿este runner fue afectado?" no la responde un escaneo: si algún job instaló una versión afectada sin --ignore-scripts desde el 08-04, las credenciales ya fueron robadas en ese momento, y el host del runner rara vez conserva evidencia (los runners efímeros destruyen el contenedor al final del job; el watcher se autolimpia en ~24 h). Lo que la responde son los lockfiles del Paso 1 y los registros de CI. Si la respuesta es "sí, lo instaló": reconstruye el runner y rota sus secretos — los runners son efímeros, no hay razón para limpiarlos.

    Prevención hacia adelante — SÍ, ejecútalo en el pipeline. Añade el escáner como paso de compilación, en modo repo, después del checkout y antes de npm install. No examina el host del runner — examina el código que está a punto de instalarse, y el código de salida hace fallar la compilación antes de que el preinstall malicioso tenga oportunidad de ejecutarse:

    root@kitploit:~
    # example (GitHub Actions / GitLab CI — adapt):
    - run: node scan.mjs repo . --json    # exit 0 clean · 1 findings · 2 COMPROMISED
    - run: npm ci --ignore-scripts         # only runs if the previous step passed
    

    Referencia rápida

    root@kitploit:~
    node scan.mjs                     # scan the current directory + the host
    node scan.mjs repo /path/a /path/b
    node scan.mjs host                # persistence/implants on the machine only
    node scan.mjs repo . --json=result.json --html=report.html
    node scan.mjs --update            # update iocs.json (the only network operation)
    

    Triage

    NivelSignificadoAcción
    COMPROMISEDVersión maliciosa instalada en node_modules, payload confirmado por hash, o implante de persistencia encontradoTrata el host como comprometido; sigue el orden de respuesta — limpia el implante antes de rotar credenciales
    EXPOSEDVersión maliciosa fijada en un lockfile, sin evidencia de ejecuciónFija una versión segura, elimina node_modules, reinstala con --ignore-scripts
    AT_RISKRango (^/~) en package.json que admite una versión maliciosaFija una versión exacta o bloquéala en el proxy del registro
    SUSPECTHeurística (nombre de archivo del gusano con hash no coincidente, script de ciclo de vida sospechoso)Inspecciona manualmente — podría ser una variante nueva o un falso positivo
    INFOVector presente pero sin IOC (p. ej., una tarea folderOpen genérica)Revisar

    El informe como evidencia

    --html produce un informe autocontenido con marca de tiempo, nombre de host, versión del manifiesto de IOCs y el SHA-256 del propio escáner — utilizable como adjunto de una notificación de incidente y como pista de auditoría.

    Si el escáner reportó COMPROMISED: el orden de respuesta

    No revoques ni rotees ninguna credencial todavía — ese es el gatillo de la trampa. La secuencia:

    1. AISLAR — desconecta la máquina de la red. Es seguro: sin respuesta HTTP no hay 4xx, la trampa no se dispara y la exfiltración se detiene. No apagues (la memoria volátil es evidencia).

    2. PRESERVAR — antes de eliminar nada (el watcher se autodestruye en ~24 h):

    root@kitploit:~
    mkdir -p /tmp/evidence && cp -r ~/.config/gh-token-monitor /tmp/evidence/ 2>/dev/null
    cp /tmp/gh-token-monitor.*.log /tmp/evidence/ 2>/dev/null
    shasum -a 256 /tmp/evidence/* 2>/dev/null
    

    El archivo handler es el comando del atacante que se ejecutaría — no lo ejecutes, no lo pegues en una shell; trátalo como texto inerte. El archivo started_at acota la ventana de exposición (el auditor y el regulador te lo pedirán).

    3. ERRADICAR — mata primero el proceso del watcher y luego:

    root@kitploit:~
    # macOS
    launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
    rm -f ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
    
    # Linux
    systemctl --user disable --now gh-token-monitor.service
    loginctl disable-linger "$USER"
    rm -f ~/.config/systemd/user/gh-token-monitor.service
    
    # both
    rm -rf ~/.config/gh-token-monitor ~/.local/bin/gh-token-monitor.sh /tmp/bun-dl-*
    

    Elimina también los hooks maliciosos de .claude/settings.json y .vscode/tasks.json, los archivos setup.mjs/Math_Symbol.js/math_init.js, y limpia las cachés (~/.npm/_cacache, pnpm, yarn). Vuelve a ejecutar node scan.mjs host hasta que salga limpio.

    4. ROTAR — solo con cada host afectado limpio y verificado (un solo watcher activo basta para disparar el gatillo). Revoca primero el token de npm (detiene la propagación del gusano); luego GitHub (PAT, claves de deploy), AWS/GCP/Azure, Vault, Kubernetes, secretos de CI — y cualquier secreto que estuviera en el disco, porque hubo un barrido regex.

    5. AUDITAR — el gusano actúa en tu nombre: busca repositorios descritos como Shai-Hulud: Here We Go Again en tus organizaciones, versiones npm publicadas inesperadamente desde el 08-04 (desapruébalas y notifica a los consumidores), y uso de las credenciales en CloudTrail/Registros de auditoría dentro de la ventana de started_at.

    Después: elimina node_modules, reinstala desde un lockfile limpio con --ignore-scripts. Runners de CI y hosts con ejecución confirmada: reconstruye desde cero, siempre — se ejecutó código arbitrario, y la lista de artefactos conocidos no garantiza completitud.

    Instituciones reguladas (BR): un compromiso confirmado con acceso a credenciales puede activar obligaciones de notificación (Res. CMN 4.893/2021, Res. BCB 85/2021; art. 48 de la LGPD si hay datos personales involucrados). Documenta la cronología en UTC — started_at, detección, contención, erradicación, rotación — y confirma los plazos con Legal/Cumplimiento.

    Actualizar los IOCs (usuarios)

    El incidente está activo y la lista crece. Para obtener el manifiesto más reciente:

    root@kitploit:~
    node scan.mjs --update          # the only operation that touches the network
    

    --update obtiene iocs.json de este repositorio (securest8/npm-incident-response), nunca de un tercero — Securest8 es la puerta de curación. Recibes lo último que se haya publicado aquí.

    Mantener los IOCs (mantenedores)

    La lista de paquetes proviene del feed público de Wiz; los hashes, los dominios C2, los IOCs de persistencia y las versiones seguras son estáticos y están curados en tools/gen-iocs.mjs. Una instantánea del CSV de Wiz vive en tools/keyv-packages.csv para reproducibilidad y ejecuciones sin conexión.

    root@kitploit:~
    node tools/gen-iocs.mjs               # fetch the latest Wiz CSV, regenerate iocs.json + refresh the snapshot
    node tools/gen-iocs.mjs --offline     # regenerate from the committed snapshot, no network
    node tools/gen-iocs.mjs --allow-shrink # allow a package count lower than the snapshot (guarded by default)
    

    El generador es idempotente: conserva el manifest_version existente y no reescribe el archivo cuando no cambió nada material, y se niega a escribir un manifiesto vacío o reducido (protegiendo contra un feed upstream truncado o alterado).

    Automatización: .github/workflows/update-iocs.yml ejecuta el generador a diario (y bajo demanda) y hace commit a main solo cuando los IOCs cambian realmente — así el --update de los usuarios sigue el feed de Wiz en aproximadamente un día, con todo el historial auditable en los commits. Cámbialo a un paso de pull request (anotado en el workflow) cuando el incidente se enfríe y prefieras una fusión manual.

    Pruebas

    root@kitploit:~
    node test/run-tests.mjs         # 15 assertions against fixtures/demo-repo
    

    fixtures/demo-repo es un repositorio de prueba con IOCs inertes (nombres y versiones reales, contenido ficticio — sin malware). Al escanear el propio repositorio del escáner, la carpeta fixtures/ se omite automáticamente (con una advertencia en la salida); las pruebas la escanean pasando la ruta explícitamente.

    Alcance y créditos

    Una herramienta para un solo incidente, construida para un triage rápido durante la ventana activa del ataque — no un reemplazo de Socket, Snyk o similares. Investigación e IOCs: Socket.dev, Wiz Research (CSV público), Kodem Security.

    Mantenido por Securest8. Licencia MIT.

    Descargar herramienta