Process Herpaderping

Hoy os traigo una técnica antigua (2020) pero que está bien conocer por "culturilla general". Me refiero al viejo Process Herpaderping que consiste en manipular la imagen del ejecutable en disco para hacer que las herramientas de seguridad vean un binario diferente al que realmente se ejecuta. Básicamente:

  1. Se crea un archivo ejecutable en el disco.
  2. Se mapea en memoria como un ejecutable normal.
  3. Se sobrescribe el archivo en disco con otro contenido.
  4. Se ejecuta el proceso desde la memoria, mientras que el archivo en disco parece inofensivo.

Con esto evitaremos evitábamos la detección basada en análisis de archivos en disco y podremos confundir herramientas forenses aunque, eso sí, no ocultaremos el proceso en ejecución si se analiza en memoria y también podemos ser detectados con análisis avanzados de comportamiento.

Es similar a Hollowing y Doppelganging con algunas diferencias clave:

Técnica Método principal Manipulación de archivos Interacción con procesos Evasión de detección
Process Herpaderping Sobrescribe el ejecutable en disco después de cargarlo en memoria Modifica el archivo en disco sin afectar el ejecutable en memoria Crea un proceso con un binario legítimo pero ejecuta otro código en su lugar Engaña a herramientas que verifican archivos en disco
Process Hollowing Reemplaza la memoria de un proceso legítimo con código malicioso No modifica el archivo en disco Crea un proceso suspendido y reemplaza su sección de código Evita detección de ejecutables al usar un proceso válido como contenedor
Process Doppelgänging Usa transacciones NTFS para ocultar un ejecutable antes de su ejecución Crea y elimina un archivo sin dejar rastros en el disco Carga el ejecutable desde una transacción NTFS, evitando registros tradicionales Engaña herramientas que monitorizan el sistema de archivos

Pero volviendo a Herpaderping veamos paso a paso en detalle en el clásico esquema y posteriormente su implementación (en Rust):

1. Escribimos el binario en el disco (manteniendo el handle abierto)

Se crea un archivo en el disco con el binario malicioso que se ejecutará en memoria. Se mantiene el handle del archivo abierto para futuras modificaciones.

let file = OpenOptions::new()

    .write(true)

    .create(true)

    .open("C:\\ruta\\binario.exe")?;

2. Mapeamos el archivo como una sección de imagen (NtCreateSection, SEC_IMAGE)

Se utiliza NtCreateSection con la opción SEC_IMAGE para mapear el archivo como una sección de imagen en memoria. De esta manera, el archivo podrá ejecutarse directamente desde la memoria sin depender del contenido en disco.

NtCreateSection(

    &mut section_handle,

    SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE,

    std::ptr::null_mut(),

    std::ptr::null_mut(),

    PAGE_EXECUTE_READWRITE,

    SECTION_IMAGE,

    file_handle,

);

3. Creamos el objeto de proceso usando el handle de la sección (NtCreateProcessEx)

Se usa NtCreateProcessEx para crear el proceso suspendido a partir de la sección de imagen mapeada. El proceso ahora estará basado en la sección mapeada en memoria, no en el archivo en disco.

NtCreateProcessEx(

    &mut process_handle,

    PROCESS_ALL_ACCESS,

    std::ptr::null_mut(),

    GetCurrentProcess(),

    0,

    section_handle,

    std::ptr::null_mut(),

    std::ptr::null_mut(),

    0,

);

4. "Obscurecemos" el archivo en disco usando el mismo handle abierto

Se sobrescribe el archivo con datos inofensivos o se cambia su contenido por otro ejecutable legítimo. Ahora el archivo en disco ya no es el binario ejecutado en memoria, dificultando la detección. Se ejecuta el proceso desde memoria, pero las herramientas de seguridad aún ven el archivo en disco modificado.

let mut file = OpenOptions::new()

    .write(true)

    .truncate(true)

    .open("C:\\ruta\\binario.exe")?;

file.write_all(b"contenido seguro");

5. Creamos el hilo inicial en el proceso (NtCreateThreadEx)

Se usa NtCreateThreadEx para iniciar la ejecución del proceso en memoria.

NtCreateThreadEx(

    &mut thread_handle,

    THREAD_ALL_ACCESS,

    std::ptr::null_mut(),

    process_handle,

    entry_point,

    std::ptr::null_mut(),

    FALSE,

    0,

    0,

    0,

    std::ptr::null_mut(),

);

6. El kernel dispara el callback de creación del proceso

En este punto, Windows notifica a las herramientas de monitorización que un nuevo proceso ha sido creado. Si se inspecciona el archivo en disco, su contenido ya no coincide con la imagen en memoria.

Se engañan los análisis forenses y las herramientas de seguridad que verifican archivos en disco.

7. Cerramos el handle del archivo (IRP_MJ_CLEANUP ocurre aquí)

Finalmente, se cierra el handle del archivo, lo que dispara la operación IRP_MJ_CLEANUP en el sistema de archivos.

drop(file);

Ahora que el proceso está en ejecución y el archivo ha sido modificado, cualquier intento de inspección resultará en una atribución incorrecta del binario ejecutado.

¿Suena bien verdad? Pues de hecho esta técnica inicialmente podía evadir fácilmente soluciones de seguridad como Windows Defender y otros pero, con el tiempo, muchos productos han desarrollado reglas para identificar intentos potenciales de Process Herpaderping al detectar la ejecución de procesos seguida de la sobrescritura de un ejecutable por el mismo proceso padre. Además, herramientas de detección y respuesta en endpoints (EDR) modernas, que ofrecen monitorización en tiempo real y análisis de comportamiento, lo deberían "cazar" sin problemas.

¿Aún así queréis probarlo? Os dejo los proyectos principales:

Comentarios