Movimiento lateral y Amcache: pivote por ProgramId entre hosts

Cuando confirmas herramientas del atacante en un host Windows, la siguiente pregunta siempre es: ¿dónde más? La delimitación del movimiento lateral — identificar cada otro host que alcanzó el atacante — es uno de los pasos más difíciles en una investigación, y Amcache es una de las herramientas más útiles para ello.

La razón: Amcache almacena dos pivotes cross-host que casi ningún otro artefacto de Windows almacena:

  • Hash — el SHA-1 de los primeros 31 MiB de cada PE que el appraiser vio.
  • ProgramId — el hash de identidad de aplicación de 44 caracteres, estable entre hosts para la misma aplicación.

Un único Hash o ProgramId sospechoso en el Host A se convierte en una consulta que puedes ejecutar contra los CSVs parseados de Amcache de cualquier otro host que hayas recogido. Esta página es el playbook completo.

Para los prerrequisitos, ver Referencia completa de Amcache, Amcache FileId explicado y Amcache ProgramId explicado.


El prerrequisito de recolección#

El patrón funciona solo si has recogido Amcache de muchos hosts de una forma que te permita hacer joins entre ellos. Dos patrones prácticos de recolección:

Recolección por host basada en KAPE#

Usa una única raíz de recolección con sub-directorios por host:

\\fileshare\incident-042\
├── HOST01\
│   └── Windows\AppCompat\Programs\Amcache.hve (+ logs)
├── HOST02\
│   └── ...
├── HOST03\
│   ...

Parsea con AmcacheParser apuntando a cada directorio por host:

Get-ChildItem '\\fileshare\incident-042' -Directory | ForEach-Object {
    $host = $_.Name
    AmcacheParser.exe `
      -f "$($_.FullName)\Windows\AppCompat\Programs\Amcache.hve" `
      --csv "\\fileshare\parsed\$host" `
      --csvf "${host}_amcache.csv" `
      --mp
}

Acabas con <host>_amcache_UnassociatedFileEntries.csv por host, todos en un directorio.

Hunt de flota con Velociraptor#

El artefacto Windows.Forensics.Amcache, ejecutado como hunt, deposita la salida parseada por host en el servidor de Velociraptor. Los CSVs se nombran con el hostname del host; las mismas consultas cross-host de abajo aplican.


El pivote de hash#

El pivote más simple y de mayor precisión. Tienes un SHA-1 conocido-malo del Host A; encuentra cada otro host que lo tenga.

$badHash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
 
Get-ChildItem -Recurse -Filter *_UnassociatedFileEntries.csv |
  ForEach-Object {
    $host = $_.PSChildName.Split('_')[0]
    Import-Csv $_.FullName |
      Where-Object { $_.Hash -eq $badHash } |
      Select-Object @{n='Host';e={$host}}, FullPath, KeyLastWriteTimestamp, Size
  } |
  Sort-Object KeyLastWriteTimestamp

La salida es una timeline por host de cuándo apareció por primera vez el binario malo en cada host. La marca de tiempo más temprana es tu candidato a paciente-cero; las marcas de tiempo posteriores son la propagación.

Cuándo las coincidencias de hash sobre-ajustan#

El pivote de hash se pierde rebuilds de la misma herramienta. Los atacantes que recompilan su loader antes de cada movimiento lateral tienen un hash diferente en cada host. Para esos, recurre a ProgramId.


El pivote de ProgramId#

ProgramId es más perdonador que Hash — captura recompilaciones que comparten nombre/publisher/versión incluso cuando el contenido del binario difiere. Ver Amcache ProgramId explicado para cómo se construye la identidad.

$badProgramId = '0006fa0b2a9f8a4eb9d7c81e8b1f3c5d3e2a0000ffff'
 
Get-ChildItem -Recurse -Filter *_UnassociatedFileEntries.csv |
  ForEach-Object {
    $host = $_.PSChildName.Split('_')[0]
    Import-Csv $_.FullName |
      Where-Object { $_.ProgramId -eq $badProgramId } |
      Select-Object @{n='Host';e={$host}}, FullPath, Hash, KeyLastWriteTimestamp
  } |
  Sort-Object KeyLastWriteTimestamp

Esto encuentra cada host que tiene cualquier binario identificándose como la misma aplicación — incluso con hashes de contenido diferentes. Empareja con el pivote de hash: hash para coincidencias de alta precisión, ProgramId para coincidencias a nivel familia.

Cuándo ProgramId sobre-ajusta#

ProgramId captura falsos positivos si el atacante se monta sobre una identidad de aplicación legítima (p. ej. recompilar herramientas nombradas PsExec.exe que obtienen el mismo ProgramId que el genuino PsExec). Empareja con Hash para desambiguar; una fila que coincida con ProgramId pero con un hash que nadie más en tu entorno tiene es altamente sospechosa.


El pivote de patrón de ruta#

Para patrones de ruta de instalación de atacante conocidos, regex contra FullPath:

$pattern = '\\AppData\\Roaming\\[a-z0-9]{8}\\update\.exe$'
 
Get-ChildItem -Recurse -Filter *_UnassociatedFileEntries.csv |
  ForEach-Object {
    $host = $_.PSChildName.Split('_')[0]
    Import-Csv $_.FullName |
      Where-Object { $_.FullPath -match $pattern } |
      Select-Object @{n='Host';e={$host}}, FullPath, Hash, ProgramId, KeyLastWriteTimestamp
  }

Útil cuando:

  • Conoces la convención de instalación del set de intrusión.
  • El atacante rota hashes y metadatos pero reutiliza patrones de ruta.
  • Quieres encontrar variantes que compartan estilo de ruta.

Empareja las coincidencias con hash y ProgramId de los resultados por fila para construir una detección más rica.


Vista de propagación en serie temporal#

Para cada pivote, ordena los resultados por KeyLastWriteTimestamp para ver la propagación a lo largo del tiempo. Un patrón típico:

2026-04-01 14:23 HOST01  -- paciente cero, acceso inicial del atacante
2026-04-03 09:11 HOST02  -- 2 días después
2026-04-03 11:34 HOST07
2026-04-03 14:55 HOST09
2026-04-04 02:08 HOST15  -- fin de semana; atacante trabajando de noche
2026-04-04 02:33 HOST22
2026-04-04 02:51 HOST31

Dos lecturas:

  1. La cadencia (múltiples hosts dentro de horas entre sí) es característica de herramientas automatizadas de movimiento lateral (basadas en PsExec / WMI / SMB).
  2. La ráfaga nocturna es el patrón típico del atacante: acceso inicial durante horario laboral, luego movimiento acelerado una vez que tiene credenciales y control.

Usa estos patrones para acotar en tiempo el resto de tu recolección de evidencia. Extrae Sysmon / Security 4624 / 4688 para cada host en su ventana KeyLastWriteTimestamp ± 1 h — obtienes las líneas de comandos reales del atacante y los eventos de credenciales con alta precisión.


Cross-pivote de evidencia de Driver y Dispositivo#

El patrón cross-host no se limita a *_UnassociatedFileEntries.csv. Para investigaciones más profundas, ejecuta los mismos patrones de pivote contra:

*_DriverBinaries.csv#

Para investigaciones BYOVD — un driver vulnerable que el atacante cargó en un host casi con seguridad está cargado en los otros que alcanzó. Consulta por Hash o DriverName del driver:

$badDriver = 'mhyprot2.sys'
 
Get-ChildItem -Recurse -Filter *_DriverBinaries.csv |
  ForEach-Object {
    $host = $_.PSChildName.Split('_')[0]
    Import-Csv $_.FullName |
      Where-Object { $_.DriverName -eq $badDriver } |
      Select-Object @{n='Host';e={$host}}, DriverName, Service, DriverSigned, KeyLastWriteTimestamp
  }

*_DeviceContainers.csv#

Para investigaciones en las que el atacante conectó hardware (raro en ataques remotos, central en casos de amenaza interna) — consulta por Manufacturer o FriendlyName:

$suspiciousVendor = 'HakShop'
 
Get-ChildItem -Recurse -Filter *_DeviceContainers.csv |
  ForEach-Object {
    $host = $_.PSChildName.Split('_')[0]
    Import-Csv $_.FullName |
      Where-Object { $_.Manufacturer -match $suspiciousVendor } |
      Select-Object @{n='Host';e={$host}}, FriendlyName, Manufacturer, KeyLastWriteTimestamp
  }

Ver Historia de USB y dispositivos en Amcache para los patrones del lado de dispositivos en detalle.


Combinar con fuentes no-Amcache#

El pivote de Amcache te dice dónde se inventarió el binario. Para confirmar ejecución e identificar el método de movimiento, correlaciona con:

  • 4624 (Logon) + 4625 (Failed logon) en los hosts de destino — ¿cuándo se autenticó el atacante y como quién?
  • 4648 (Explicit credential logon) — movimiento lateral con credenciales (PsExec, RDP con credenciales pasadas).
  • Sysmon 1 / 4688 (Process create) con proceso padre — ¿el proceso del atacante se generó bajo services.exe (PsExec / servicio remoto), bajo wmiprvse.exe (WMI) o bajo explorer.exe (interactivo)?
  • Sysmon 3 (Network connect) + Sysmon 22 (DNS query) — C2 saliente.
  • Árboles de proceso de Velociraptor / EDR — los mismos datos, más fáciles de navegar.

Un único pivote de Amcache delimitando la propagación, unido a eventos de autenticación y proceso por host en los hosts coincidentes, te da una timeline defendible de: quién, cuándo, desde dónde, vía qué binario, usando qué credencial.


Cuando el pivote falla#

Tres situaciones donde el pivote cross-host de Amcache rinde mal:

El appraiser no ha corrido aún en los hosts de destino#

Si el atacante se movió lateralmente dentro de horas de tu recolección, los appraisers de los hosts de destino pueden no haber inventariado el binario aún. Amcache los muestra como limpios. Re-recoge 24–48 horas después; la propagación aparecerá.

El atacante limpió Amcache en cada host que alcanzó#

Poco común (sobre todo porque es ruidoso y la mayoría de los atacantes no se molestan) pero posible. Usa el flujo de recuperación de Volume Shadow Copy en Dónde está Amcache.hve en disco en cada host donde sospeches limpieza.

El atacante usó binarios diferentes por host#

Si el atacante generó implantes por host (malware verdaderamente por víctima), los pivotes de hash y ProgramId fallan por diseño. Los patrones de ruta y detecciones comportamentales más amplias (logon 4624 desde la misma IP inusual a través de muchos hosts) se vuelven primarias.


Ver también#

Para explorar tus propios hives recogidos sin instalar nada, suelta un archivo en la página de inicio del parser — se parsea enteramente en tu navegador.

Artículos relacionados

Volver a todos los artículos