"¿Se ejecutó el programa?" es la pregunta más sencilla en una investigación Windows y la que más a menudo los analistas responden mal. La tentación es encontrar un .pf con el nombre correcto y llamarlo. Es correcto en la mayoría de los casos. Es peligroso en la minoría, y en esa minoría es donde viven las investigaciones disputadas.
Este post va sobre los límites de la afirmación "prueba de ejecución" de Prefetch. Qué dice de verdad. Qué no dice. Y cómo afianzarla con los artefactos que sobreviven a su lado.
La afirmación, escrita con precisión
Cuando el servicio Prefetch observa que un proceso arranca y supera los primeros diez segundos de ejecución, escribe (o actualiza) un archivo .pf. El nombre embebe el nombre del binario del ejecutable (en mayúsculas, truncado a 29 caracteres) y un hash derivado de la ruta completa del ejecutable.
Así que la afirmación que un .pf respalda, escrita con cuidado, es:
Un proceso fue arrancado en este host cuyo ejecutable primario tenía el nombre
EXAMPLE.EXEy cuya ruta resuelta produjo el hash0x1234ABCD, en los momentos registrados en el array de marcas temporales de ejecución del archivo, y fue observado por el servicio Prefetch el tiempo suficiente para ser registrado.
Todo lo que esté fuera de esa frase es inferencia, no evidencia directa.
Sigue siendo una afirmación más fuerte que la que dan Shimcache ("Windows miró los metadatos de este binario en algún momento") o AmCache ("Windows enumeró este binario por motivos de compatibilidad, lo que suele pero no siempre implicar ejecución"). Prefetch exige que el proceso realmente arranque. Eso es difícil de falsificar por accidente.
Cómo funciona el hash de ruta y dónde miente
El hash en el nombre no es un hash de contenido. Es una función de la ruta completa del ejecutable (con algunos campos de contexto adicionales en builds modernas de Windows, como argumentos de línea de comandos e identificadores de paquete para apps UWP). Dos binarios con el mismo nombre en dos directorios distintos producen dos archivos .pf distintos. Esa propiedad es la que te permite distinguir C:\Program Files\PsExec.exe de C:\Users\victim\AppData\Local\Temp\PsExec.exe de un vistazo.
Pero la ruta es la única entrada, no el binario. Si C:\Users\victim\AppData\Local\Temp\PsExec.exe se ejecutó hoy y ayer un archivo completamente distinto vivía en esa ruta (también llamado PsExec.exe), comparten un archivo .pf. Las marcas temporales se acumulan. El contador de ejecuciones sube. La lista de carga se sobrescribe con los datos de la ejecución más reciente.
Este es el modo de fallo que más he visto. Un atacante suelta una herramienta, la ejecuta, la borra, y más tarde un archivo benigno acaba en la misma ruta. El registro Prefetch original sigue ahí, pero la ruta en disco ahora apunta a otra cosa por completo. Un analista que solo mire la ruta y el archivo actualmente en disco concluye que se ejecutó el archivo benigno. El archivo benigno no se ejecutó. Otra cosa con la misma ruta se ejecutó.
La mitigación es no confiar nunca en la resolución de rutas a solas. Empareja el .pf con la entrada MFT para esa ruta en el momento de la ejecución, recupera el número de referencia de archivo y comprueba si el ID de archivo ha cambiado desde entonces.
Colisiones de nombre y el problema de los 29 caracteres
El campo de nombre interno del ejecutable es de 29 caracteres más terminador nulo, UTF-16LE. Los nombres más largos se truncan.
Si alguna vez has visto dos .pf con nombres como:
LONGNAMEDEXECUTABLE_VERSION_A-12345678.pf
LONGNAMEDEXECUTABLE_VERSION_B-87654321.pf
y ambos campos de nombre interno dicen LONGNAMEDEXECUTABLE_VERSION_, tienes una colisión en el nombre pero hashes distintos. Los hashes son los que desambiguan.
Por el otro lado, el espacio de hash es amplio pero no infinito, y el hash es un valor de 32 bits. Las colisiones de hash entre rutas ocurren. Son raras en la práctica pero no inéditas, y en un host activo con miles de binarios no deberías sorprenderte de ver dos .pf con los mismos bytes de hash si los nombres difieren. La combinación de nombre y hash es la clave única, no ninguno de los dos por separado.
El caso límite "el ejecutable fue reemplazado tras crearse el .pf"
Aquí va la versión de este caso que ha entrado realmente en mis encargos.
Día 0, un atacante copia m1m1.exe (Mimikatz renombrado) a C:\Users\Public\m1m1.exe. Lo ejecuta. Windows crea M1M1.EXE-AABBCCDD.pf. La lista de carga muestra \Windows\System32\sekurlsa.dll y otros artefactos que gritan Mimikatz.
Día 3, el atacante borra el binario.
Día 7, un usuario interno, totalmente ajeno, escribe una utilidad pequeña, la nombra m1m1.exe, la deja en C:\Users\Public\, la ejecuta. Windows actualiza el .pf existente (misma ruta, mismo hash). La lista de carga ahora refleja la utilidad benigna. El contador de ejecuciones es 2. La marca temporal más reciente es el día 7.
Una lectura superficial dice "el binario corrió dos veces, la última parece benigna". La lectura correcta necesita artefactos adicionales. La MFT mostrará que el archivo en C:\Users\Public\m1m1.exe se creó el día 0, se borró el día 3 y se recreó el día 7 con una referencia de archivo distinta. El USN journal tendrá ambos ciclos de vida. AmCache puede que tenga el hash del binario del día 0 incluso después de borrado el archivo. Sysmon Event ID 1 tendrá la línea de comandos y el SHA-256 de ambas ejecuciones si estaba activo.
Prefetch a solas dice "corrió dos veces". Prefetch más los artefactos circundantes dice "dos binarios distintos con la misma ruta corrieron, aquí están los hashes, aquí qué usuario, aquí qué hicieron".
Si paras en Prefetch a solas en un caso así, te equivocarás del modo más embarazoso: con confianza.
Empareja con EVTX 4688 y Sysmon EID 1
Los dos eventos que quieres junto a toda afirmación Prefetch son Security 4688 (creación de proceso, si está activado) y Sysmon Event ID 1 (creación de proceso, con línea de comandos, padre, hashes, nivel de integridad). Están en el EVTX.
Empareja por:
- Ruta del ejecutable. El campo
NewProcessNameoImagedel 4688/EID 1 debería coincidir con la ruta resuelta que Prefetch registró. - Hora de ejecución. La hora
Last runde Prefetch y la hora del evento deben coincidir con margen de segundos. Huecos mayores no son necesariamente erróneos pero merecen investigación; la causa más común es desviación de reloj entre distintas fuentes de log. - Nivel de integridad del proceso y contexto de usuario. Prefetch es por máquina y no te dice nada sobre quién ejecutó el binario. Sysmon y 4688 te dan el SID. Si el
.pfmuestra diez ejecuciones y Sysmon solo registra cinco, te faltan logs, o las otras cinco ocurrieron en una ventana sin Sysmon, o alguien manipuló el log.
El par es mucho más fuerte que cualquiera por separado. Prefetch te da un inventario completo de cada binario que corrió (sujeto a las salvedades de SSD/servidor); EVTX te da el detalle por ejecución. Úsalos juntos y la pregunta "¿corrió esto?" deja de ser ambigua.
Cuando no tienes ninguno, RAM te da una lista de procesos en vivo si el host no se ha reiniciado, y el pagefile a veces tiene artefactos de procesos. Tras un reinicio sin EVTX y sin Prefetch, te quedas mayormente con AmCache e inferencia.
Distinguir "corrió una vez" de "corrió entre el ruido"
El contador de ejecuciones de Prefetch no miente sobre la cota inferior. Si dice 12 ejecuciones, el binario corrió al menos 12 veces. Si corrió más (porque algunas ejecuciones se cayeron del anillo de ocho marcas o porque Win10 1709+ recortó entradas antiguas) queda abierto.
Para qué sirve el contador en este contexto: contraste. En un host con un .pf de psexec.exe que muestra 200 ejecuciones y ruta en Program Files, estás viendo la herramienta diaria de un sysadmin. En el mismo host con un .pf separado de psexec.exe que muestra 1 ejecución y ruta bajo \AppData\Local\Temp\, estás viendo algo que llegó hace poco y se ejecutó una vez. El segundo .pf es el que hay que investigar.
Contraintuitivamente, los archivos Prefetch de una sola ejecución son los más interesantes en una intrusión. Los de muchas ejecuciones son ruido de fondo a menos que vivan en sitios sospechosos.
Un workflow que aguanta
Cuando la pregunta es "¿se ejecutó esto en este host?", ejecuto la siguiente secuencia:
- Identificar el
.pfpor nombre y hash. - Leer sus marcas temporales de ejecución y el contador. Anotar la ruta del ejecutable que registró.
- Sacar la entrada MFT para esa ruta y comprobar que la referencia de archivo coincide con lo que Prefetch vio.
- Cruzar con EVTX 4688 y Sysmon EID 1 en los tiempos de ejecución.
- Comprobar AmCache buscando el hash del archivo. Si coincide con una muestra conocida, el caso se simplifica.
- Si el binario ya no está en disco, comprobar el USN journal para eventos de borrado alrededor de los tiempos de ejecución.
Si los pasos 3 a 6 concuerdan, la afirmación de ejecución es esencialmente irrefutable. Si discrepan, tienes algo más interesante que una simple prueba de ejecución; tienes evidencia de manipulación, reemplazo o una secuencia más compleja de lo que la sola entrada Prefetch sugiere.
Lecturas adicionales
- Eric Zimmerman, PECmd: el parser. Lee las notas de versión para saber qué cambió entre builds de Windows.
- Andrea Fortuna, Análisis forense de archivos Prefetch: explicación clara con ejemplos.
- Microsoft, Audit Process Creation (evento 4688): el artefacto compañero, incluida la política de auditoría que registra la línea de comandos en 4688, que la mayoría de redes no tiene activada.
Prefetch responde "el binario corrió". Sysmon responde "esto es lo que hizo". Trátalos como complementarios y no te quedarás corto.