Saltar a contenido

Pipes en Windows y explotacion de drivers

Resumen: Un pipe en Windows es un canal de IPC expuesto como objeto del kernel y accesible mediante handles. Entender pipes ayuda a conectar tres ideas del modulo: el namespace de objetos (\\.\pipe\X -> \Device\NamedPipe\X), el patron CreateFile/ReadFile/WriteFile/DeviceIoControl, y el uso de pipes como herramienta de kernel pool spraying para explotar un UAF en drivers como HEVD.

<- Volver al Modulo 5


Tabla de Contenidos

  1. Idea base: pipe como IPC
  2. Pipes como objetos del kernel
  3. Anonymous pipes vs named pipes
  4. APIs principales
  5. CreateFile no significa solo "archivo"
  6. Named Pipe Impersonation
  7. Conexion con HEVD: UAF en kernel pool
  8. Que significa "spammear pipes"
  9. Por que pipes sirven como reclaimer
  10. Secuencia mental del exploit
  11. Fake object placement
  12. Debugging y verificacion
  13. Cheat sheet
  14. Recursos

1. Idea base: pipe como IPC

IPC significa Inter-Process Communication. Windows tiene varias primitivas para que procesos se comuniquen: memoria compartida, mailslots, sockets, RPC, ALPC, COM y pipes.

Un pipe es la version conceptual mas simple:

Proceso A  --->  [ pipe ]  --->  Proceso B

Un extremo escribe bytes o mensajes; el otro extremo los lee. Por eso muchas APIs de pipes usan nombres de I/O comun:

ReadFile(...);
WriteFile(...);
CloseHandle(...);

Aunque la API parezca de archivos, el objeto subyacente no tiene por que ser un archivo de disco. En Windows, muchas cosas se manipulan igual: un path se resuelve a un objeto del kernel, se obtiene un HANDLE, y despues se hacen operaciones sobre ese handle.


2. Pipes como objetos del kernel

Los named pipes viven dentro del Windows Object Manager namespace. El path Win32 tipico:

\\.\pipe\MiPipe

se traduce internamente a un objeto bajo el device de NPFS, el Named Pipe File System:

\Device\NamedPipe\MiPipe

La idea importante para reversing/exploitation:

Win32 path
    -> NT object path
        -> objeto del kernel
            -> handle en user-mode
                -> operaciones de I/O

Esto se parece mucho a como una aplicacion habla con un driver:

\\.\VulnDriver
    -> \Device\VulnDriver
        -> DEVICE_OBJECT
            -> handle
                -> DeviceIoControl(...)

Por eso los pipes son buenos para entender drivers: ambos pasan por el modelo de objetos, handles, ACLs e I/O del kernel.


3. Anonymous pipes vs named pipes

Anonymous pipe

Un anonymous pipe no tiene un nombre global elegido por el programador. Normalmente se usa entre procesos relacionados, por ejemplo padre e hijo:

HANDLE hRead;
HANDLE hWrite;

CreatePipe(&hRead, &hWrite, NULL, 0);

Usos comunes:

  • Redirigir stdin, stdout o stderr de un proceso hijo.
  • Conectar dos procesos cuando ya se pueden pasar handles.
  • Armar sprays simples con CreatePipe + WriteFile.

Punto clave: Microsoft documenta que CreatePipe crea un pipe anonimo, pero internamente estos pipes se implementan usando un named pipe con nombre unico. En la practica, tambien terminan pasando por NPFS.

Named pipe

Un named pipe si tiene un nombre visible para otros procesos:

\\.\pipe\demo
\\MACHINE01\pipe\demo

Modelo tipico:

Servidor:
  CreateNamedPipe("\\\\.\\pipe\\demo", ...)
  ConnectNamedPipe(...)
  ReadFile(...)
  WriteFile(...)

Cliente:
  CreateFile("\\\\.\\pipe\\demo", ...)
  WriteFile(...)
  ReadFile(...)

Los named pipes pueden ser locales o remotos, unidireccionales o duplex, de bytes o de mensajes. Cada instancia comparte el mismo nombre, pero tiene sus propios buffers y handles.


4. APIs principales

API Rol
CreatePipe Crea un pipe anonimo y devuelve handles de lectura/escritura
CreateNamedPipeW Crea el extremo servidor de un named pipe
ConnectNamedPipe Espera o completa la conexion de un cliente
CreateFileW El cliente abre \\.\pipe\Nombre o un device como \\.\HEVD
ReadFile Lee datos desde el handle
WriteFile Escribe datos al handle
ImpersonateNamedPipeClient El servidor adopta temporalmente el token del cliente
DisconnectNamedPipe Desconecta una instancia de named pipe
CloseHandle Libera el handle; el objeto se destruye cuando no quedan referencias

Ejemplo minimo de named pipe:

HANDLE hPipe = CreateNamedPipeW(
    L"\\\\.\\pipe\\demo",
    PIPE_ACCESS_DUPLEX,
    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
    1,
    0x1000,
    0x1000,
    0,
    NULL
);

ConnectNamedPipe(hPipe, NULL);

Cliente:

HANDLE hClient = CreateFileW(
    L"\\\\.\\pipe\\demo",
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    0,
    NULL
);

5. CreateFile no significa solo "archivo"

En Win32, CreateFile es una API generica para abrir objetos que exponen semantica de I/O.

Esto abre un archivo:

CreateFileW(L"C:\\temp\\a.txt", ...);

Esto abre un pipe:

CreateFileW(L"\\\\.\\pipe\\demo", ...);

Esto abre un device/driver:

CreateFileW(L"\\\\.\\HackSysExtremeVulnerableDriver", ...);

Despues, con un driver, la comunicacion no suele ser ReadFile/WriteFile, sino DeviceIoControl:

DeviceIoControl(
    hDevice,
    IOCTL_CODE,
    inputBuffer,
    inputSize,
    outputBuffer,
    outputSize,
    &bytesReturned,
    NULL
);

La similitud conceptual:

Pipe:
  CreateFile("\\\\.\\pipe\\demo")
  ReadFile / WriteFile

Driver:
  CreateFile("\\\\.\\DeviceName")
  DeviceIoControl

El aprendizaje es el mismo: user-mode obtiene un handle a un objeto kernel y le manda requests.


6. Named Pipe Impersonation

Otra relacion entre pipes y explotacion es la impersonation. Windows permite que el servidor de un named pipe impersone el contexto de seguridad del cliente que envio el ultimo mensaje leido.

Flujo legitimo:

1. Cliente se conecta al pipe de un servicio.
2. Cliente manda una request.
3. Servicio llama ImpersonateNamedPipeClient(hPipe).
4. El thread del servicio actua temporalmente con el token del cliente.
5. Servicio llama RevertToSelf().

Uso defensivo normal:

Servicio corre como SYSTEM.
Usuario normal pide acceder a un archivo.
Servicio impersona al usuario antes de tocar el archivo.
Asi evita leer/escribir algo que el usuario no deberia poder tocar.

Uso ofensivo clasico:

Atacante crea un pipe servidor.
Fuerza a un servicio privilegiado a conectarse como cliente.
El atacante llama ImpersonateNamedPipeClient.
Si el token y privilegios lo permiten, duplica ese token y spawnea proceso privilegiado.

Esto es una familia distinta a HEVD UAF. En HEVD, los pipes no se explotan por impersonation; se usan como herramienta para moldear memoria del kernel.


7. Conexion con HEVD: UAF en kernel pool

En un Use-After-Free, el driver libera un objeto pero conserva un puntero viejo. Luego vuelve a usar ese puntero.

Modelo simplificado:

typedef struct _UAF_OBJECT {
    void (*Callback)(void);
    char Buffer[0x50];
} UAF_OBJECT;

UAF_OBJECT *g_Object;

g_Object = ExAllocatePoolWithTag(...);
g_Object->Callback = LegitFunction;

ExFreePoolWithTag(g_Object, ...);

// BUG: g_Object sigue apuntando al chunk liberado
g_Object->Callback();

Despues del free, la direccion sigue siendo una direccion kernel valida, pero esa memoria ya no pertenece logicamente al objeto original:

Antes:
  [ UAF_OBJECT ]
  [ Callback = LegitFunction ]
  [ data... ]

Despues del free:
  [ chunk libre en kernel pool ]
       ^
       g_Object todavia apunta aca

La explotacion intenta que otra allocation controlada por el atacante ocupe ese mismo chunk antes del use.

Despues del reclaim:
  [ fake object controlado por atacante ]
       ^
       g_Object ahora interpreta esta memoria como UAF_OBJECT

8. Que significa "spammear pipes"

"Spammear pipes" significa crear muchos pipes y escribir datos en ellos para provocar muchas allocations en kernel pool. No se busca atacar al pipe; se usa el pipe como spray primitive.

Conceptualmente:

for (int i = 0; i < MANY; i++) {
    CreatePipe(&readPipe[i], &writePipe[i], NULL, 0x1000);
}

// despues del free del objeto vulnerable
for (int i = 0; i < MANY; i++) {
    WriteFile(writePipe[i], fakeObject, fakeObjectSize, &written, NULL);
}

Eso genera muchas allocations parecidas en el kernel:

Pipe 0 -> buffer kernel con datos controlados
Pipe 1 -> buffer kernel con datos controlados
Pipe 2 -> buffer kernel con datos controlados
...

El objetivo estadistico:

chunk liberado por HEVD
        ^
        alguno de los buffers de pipe cae aca

En heap/pool exploitation, esto se llama pool spraying o kernel pool feng shui: ordenar el estado del allocator para aumentar la probabilidad de que una allocation caiga en el hueco exacto.


9. Por que pipes sirven como reclaimer

Los pipes son buenos reclaimers por varias razones:

Propiedad Por que importa
Alocan en kernel Sirven para competir por chunks del kernel pool
Usan NPFS (npfs.sys) El subsystem de pipes crea objetos y buffers internos
Tamanos influenciables CreateNamedPipe recibe tamanos de buffers y WriteFile fuerza espacio para datos
Contenido controlado Los bytes escritos vienen desde user-mode
Faciles de crear en masa Cientos o miles de handles son triviales en laboratorio
Liberables Cerrando handles se puede desmontar parte del layout

Microsoft documenta que al crear named pipes el sistema crea buffers inbound/outbound usando nonpaged pool, y que los tamanos son advisory: el sistema puede redondearlos a boundaries internos. Esa frase explica por que los pipes aparecen tanto en kernel pool exploitation: son una API user-mode que empuja allocations en memoria kernel no paginable.

La parte delicada es el tamano. El allocator no ve "un objeto HEVD" y "un pipe buffer"; ve requests de memoria redondeados a clases/buckets.

Objeto UAF liberado:
  request: 0x58
  bucket real: 0x60 / 0x70 segun build, header y alineacion

Pipe buffer:
  write size: N
  allocation real: redondeada por NPFS + pool allocator

Objetivo:
  que ambos compitan en una clase compatible

Si el tamano no calza, el spray puede ser enorme y aun asi no reclamar el slot correcto.


10. Secuencia mental del exploit

El flujo conceptual para un UAF de HEVD con pipe spray:

1. Groom inicial
   Crear objetos para estabilizar el pool.

2. Crear huecos
   Liberar algunos objetos para dejar espacios repetibles.

3. Allocate vulnerable object
   Pedirle a HEVD que cree el UAF_OBJECT.

4. Free vulnerable object
   Pedirle a HEVD que lo libere, dejando el dangling pointer.

5. Spray / reclaim con pipes
   Escribir payloads en muchos pipes para ocupar el hueco liberado.

6. Trigger
   Pedirle a HEVD que use g_Object.

7. Resultado
   El driver interpreta el pipe buffer como si fuera UAF_OBJECT.

Visual:

Pool estable:
  [A][A][A][A][A][A][A]

Huecos:
  [A][ ][A][ ][A][ ][A]

HEVD alloca:
  [A][UAF_OBJECT][A][ ][A][ ][A]

HEVD free:
  [A][ FREE ][A][ ][A][ ][A]
       ^
       g_Object sigue apuntando aca

Pipe spray:
  [A][PIPE_BUFFER_CONTROLADO][A][PIPE][A][PIPE][A]
       ^
       g_Object ahora cae sobre datos controlados

Trigger:
  g_Object->Callback()

11. Fake object placement

Supongamos que el objeto vulnerable empieza con un callback:

typedef struct _UAF_OBJECT {
    void (*Callback)(void);
    char Data[0x50];
} UAF_OBJECT;

El fake object que se escribe dentro del pipe buffer tiene que respetar ese layout:

offset 0x00: puntero que el driver va a usar como Callback
offset 0x08: padding / datos esperados
offset 0x10: mas datos
...

En laboratorio se puede usar un marker para confirmar control:

offset 0x00: 0x4141414141414141

Si al triggerear el UAF el sistema intenta saltar a 0x4141414141414141, no se "exploto" todavia, pero se confirmo algo importante:

1. El free ocurrio.
2. El dangling pointer sobrevivio.
3. El spray reclamo el chunk.
4. El driver interpreto datos controlados como objeto.
5. Hay control del indirect call / function pointer.

En sistemas modernos, controlar un function pointer no alcanza por si solo:

Mitigacion Efecto
SMEP Kernel no puede ejecutar shellcode en paginas user-mode
SMAP Kernel no puede leer/escribir user-mode sin habilitarlo explicitamente
KASLR Hay que conocer direcciones kernel validas
kCFG / CFG Llamadas indirectas pueden ser validadas
Pool hardening Reuse menos determinista y metadatos mas robustos

Por eso en targets reales se suele pasar a ROP kernel, data-only attacks o primitivas de read/write mas estables. Para el modulo, la idea central es entender el reclaim del chunk y el control de layout.


12. Debugging y verificacion

WinDbg ayuda a separar "el bug existe" de "el spray reclamo el slot".

Comandos utiles:

!pool <addr>          ; muestra el chunk al que pertenece una direccion
!pool <addr> 2        ; mas detalle
!poolfind <tag>       ; busca chunks por pool tag
dt nt!_POOL_HEADER <addr>
!handle <handle>      ; inspecciona handles del proceso
!object \Device\NamedPipe

Workflow de verificacion:

1. Breakpoint en Allocate UAF.
2. Guardar la direccion del objeto.
3. `!pool <addr>` para ver tag, tamano y estado.
4. Breakpoint despues de Free UAF.
5. Verificar que el chunk queda free pero la global no se nulifica.
6. Ejecutar pipe spray.
7. Volver a mirar `!pool <addr>` y memoria alrededor.
8. Breakpoint en el indirect call del Use.
9. Confirmar que el primer qword del fake object controla el destino.

Si el destino todavia apunta a la funcion legitima, no se reclamo el slot. Si apunta a un marker (0x4141...) o a datos del fake object, el spray esta funcionando.


13. Cheat sheet

Concepto Frase corta
Pipe Canal IPC entre procesos
Named pipe Pipe con nombre en \\.\pipe\Nombre
Anonymous pipe Pipe sin nombre visible; CreatePipe devuelve dos handles
NPFS Named Pipe File System (npfs.sys)
Handle Referencia user-mode a un objeto kernel
CreateFile Abre archivos, pipes, devices y otros objetos con semantica de I/O
DeviceIoControl Envia IOCTLs a un driver
Pipe impersonation Servidor adopta temporalmente el token del cliente
UAF Puntero viejo usado despues de liberar el objeto
Pool spray Muchas allocations para moldear el allocator
Reclaimer Objeto usado para ocupar un chunk liberado
Fake object Layout controlado que reemplaza logicamente al objeto UAF

Regla practica:

Pipes para entender drivers:
  ambos son objetos kernel abiertos por handles.

Pipes para explotar UAF:
  sirven para meter muchas allocations controladas en kernel pool.

Pipes para impersonation:
  sirven para robar/adoptar tokens cuando un cliente privilegiado se conecta.

14. Recursos

Recurso URL
Chat base de estos apuntes https://chatgpt.com/share/69fd0acb-9218-83e9-8d78-a8e30ddbf9e3
Microsoft Learn - Named Pipes https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipes
Microsoft Learn - Pipe Names https://learn.microsoft.com/en-us/windows/win32/ipc/pipe-names
Microsoft Learn - CreatePipe https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
Microsoft Learn - CreateNamedPipeA https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
Microsoft Learn - ImpersonateNamedPipeClient https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-impersonatenamedpipeclient
HEVD repo https://github.com/hacksysteam/HackSysExtremeVulnerableDriver
HEVD x64 UAF - Generic Non-Paged Pool Feng-Shui https://securityinsecurity.github.io/exploiting-hevd-use-after-free/

Extra basado en la conversacion sobre pipes en Windows, CreateFile, named pipe impersonation y el uso de pipes como primitive de kernel pool spraying para el UAF de HEVD.