Saltar a contenido

Clase 1 — Mitigaciones modernas, token stealing y SeDebugPrivilege

← Volver al Módulo 7

Resumen: esta clase repasa las mitigaciones kernel del módulo 7 y baja a demos prácticas con WinDbg. Primero se revisa por qué KASLR, SMEP, SMAP, HVCI, KCFG y KCET cambian el exploit moderno. Después se muestra cómo en 23H2 todavía se podía usar leaks para ubicar EPROCESS, cómo funciona el token stealing clásico copiando el token de System, por qué _EX_FAST_REF obliga a limpiar los low bits para usar !token, y cómo habilitar SeDebugPrivilege modificando _TOKEN.Privileges.Present y _TOKEN.Privileges.Enabled.


Tabla de Contenidos

  1. Contexto de la clase
  2. Repaso de mitigaciones kernel
  3. 24H2: por qué se pierden leaks cómodos
  4. Data-only attacks: por qué siguen siendo fuertes
  5. Demo 1 — encontrar EPROCESS y el offset Token
  6. Demo 2 — token stealing clásico a mano
  7. _EX_FAST_REF: cuándo alinear y cuándo copiar crudo
  8. Demo 3 — otra build con Token en 0x248
  9. Demo 4 — habilitar SeDebugPrivilege
  10. Contexto user/kernel en WinDbg
  11. Errores comunes vistos en vivo
  12. Workflow mental de la clase
  13. Cheat sheet

1. Contexto de la clase

La clase arranca con un repaso corto de las slides del módulo 7 y después pasa a demos. La idea no es quedarse en la definición de cada mitigación, sino ver cómo impacta en una cadena de explotación real.

Flujo general:

1. Repasar mitigaciones kernel modernas.
2. Comparar pre/post Windows 24H2.
3. Mostrar que el data-only attack evita varias mitigaciones de control-flow.
4. Hacer token stealing manual desde WinDbg.
5. Habilitar SeDebugPrivilege modificando bits del token.
6. Validar que ahora se pueden abrir/leer procesos SYSTEM.

2. Repaso de mitigaciones kernel

Mitigación Qué remarca la clase
KASLR equivalente kernel de ASLR; al reiniciar cambian direcciones de módulos/data
CFG valida indirect calls en runtime para impedir redirección arbitraria
SMEP impide ejecutar código user desde kernel; CR4 bit 20
SMAP impide leer/escribir user desde kernel; CR4 bit 21, default off en Windows
HVCI integridad de código, drivers firmados, virtualización
KCFG CFG aplicado a kernel-mode
KCET shadow stack para evitar corrupción de return addresses y complicar ROP

Comentario clave sobre SMEP:

no podés saltar directo a user-mode;
para volver al ret2user clásico tendrías que deshabilitar el bit de CR4.

Comentario clave sobre SMAP:

lo soporta el procesador,
pero Windows no lo habilita por defecto por compatibilidad.

3. 24H2: por qué se pierden leaks cómodos

Antes de 24H2, NtQuerySystemInformation era una vía cómoda:

NtQuerySystemInformation
  -> leak de base kernel
  -> leak de EPROCESS
  -> leak de objetos kernel
  -> incluso podía ayudar a ubicar pipes por tag

La clase recalca que en 23H2 esto hacía que, si ya tenías una primitiva read/write, la explotación fuese mucho más directa:

leak EPROCESS
  -> leer System.Token
  -> escribir System.Token en target.Token

En 24H2/25H2 esa comodidad cae:

sin SeDebugPrivilege / admin
  -> no hay leak simple de base/objetos kernel por esa API

Conclusión:

la técnica data-only sigue funcionando,
pero el problema moderno es conseguir el leak o las direcciones correctas.

4. Data-only attacks: por qué siguen siendo fuertes

El instructor remarca que robar un token o cambiar privilegios no ejecuta shellcode.

no shellcode
no user-code execution
no ret2user
sólo escribir data kernel

Por eso:

  • SMEP no frena la técnica;
  • KCET no frena directamente la técnica;
  • CFG/KCFG no entran si no hay indirect call hijack;
  • SMAP podría complicar accesos user/kernel, pero no está default on.

Modelo:

kernel write primitive
  -> modificar _EPROCESS.Token
  -> o modificar _TOKEN.Privileges
  -> SYSTEM / SeDebugPrivilege

5. Demo 1 — encontrar EPROCESS y el offset Token

Buscar el proceso

Por nombre:

!process 0 0 notepad.exe

Por PID:

!process <PID_HEX> 0

Para System:

!process 4 0

Confirmar el offset del token

No se hardcodea sin mirar la build:

dt ntkrnlmp!_EPROCESS Token

Valores vistos en clase:

Build / VM Offset de _EPROCESS.Token
demo principal 0x4B8
otra VM/build 0x248

Regla práctica:

TokenOffset depende de la build.
Siempre confirmarlo con símbolos o reversing antes de escribir.

6. Demo 2 — token stealing clásico a mano

El token stealer clásico hace esto:

target_process.Token = system_process.Token

Paso 1 — calcular la dirección donde escribir

Para el proceso objetivo:

target_token_field = target_EPROCESS + TokenOffset

Verlo con:

dp  <target_EPROCESS + TokenOffset>
dps <target_EPROCESS + TokenOffset>

La clase insiste en no confundir:

target_EPROCESS + TokenOffset   = dirección del campo
poi(target_EPROCESS+TokenOffset) = valor actual del token

Paso 2 — leer el token de System

system_token_value = poi(system_EPROCESS + TokenOffset)

Paso 3 — escribir el token de System en el target

eq <target_EPROCESS + TokenOffset> <system_token_value>

eq significa edit qword.

Después de esto, el proceso objetivo apunta al mismo token que System.


7. _EX_FAST_REF: cuándo alinear y cuándo copiar crudo

_EPROCESS.Token es un _EX_FAST_REF: empaqueta puntero + refcount en los low bits.

Value = token_pointer | refcount_low_bits

Para usar !token, hay que limpiar el low nibble:

token_real = token_value & ~0xF
!token token_real

Si se pasa el valor crudo que termina en algo como ...5 o ...6, !token puede decir que no es token válido.

Pero para token stealing se copia el valor de memoria tal como está:

eq <target_token_field> <system_token_raw_value>

Resumen:

Operación Valor correcto
Inspeccionar con !token puntero alineado (value & ~0xF)
Copiar token entre procesos valor crudo del campo _EX_FAST_REF

8. Demo 3 — otra build con Token en 0x248

La clase repite la idea en otra VM/build donde el offset no es 0x4B8, sino 0x248.

system_token = poi(system_EPROCESS + 0x248)
eq <target_EPROCESS + 0x248> <system_token>

El método no cambia. Cambia el offset.

Conclusión:

la técnica es estable conceptualmente,
pero los offsets no son universales.

9. Demo 4 — habilitar SeDebugPrivilege

La demo final no copia todo el token de System. Sólo cambia bits del token actual.

Objetivo:

habilitar SeDebugPrivilege
  -> abrir procesos SYSTEM
  -> leer/escribir memoria remota
  -> actuar como debugger user-mode

Probar sin privilegio

El binario de prueba intenta abrir procesos sensibles:

services.exe
lsass.exe
winlogon.exe

Sin SeDebugPrivilege:

access denied / no pudo abrir el proceso

Encontrar el proceso actual

En la clase se toma el PID decimal y se convierte a hex:

PID decimal = 8076
PID hex     = 1F8C

Se usa:

!process 1f8c 1

El flag 1 muestra token e información suficiente. El flag 7 muestra mucha más información.

Ir a _TOKEN.Privileges

TOKEN + 0x40 = Present
TOKEN + 0x48 = Enabled

Confirmación:

dt ntkrnlmp!_TOKEN <TOKEN>

Prender bit 20

SeDebugPrivilege es bit 20:

1 << 20 = 0x100000

Valores vistos:

Present: 0x602880000 -> 0x602980000
Enabled: 0x800000    -> 0x900000

Comandos conceptuales:

eq <TOKEN+0x40> 602980000
eq <TOKEN+0x48> 900000

Si sólo se modifica Present, el privilegio aparece como disponible pero no activo. Para que funcione debe estar en Present y Enabled.

Validar

!token <TOKEN>
whoami /priv

Salida esperada:

SeDebugPrivilege    Enabled

Después de aceptar el cartelito del binario de prueba, la lectura del proceso System pasa a ser exitosa.


10. Contexto user/kernel en WinDbg

Advertencia práctica: cuando se debuggea user-mode desde kernel debugger, un break puede dejarte en otro contexto.

Flujo recomendado:

!process 0 0 <process.exe>
.process /p /r <EPROCESS>
.reload /user

Notas:

  • si trabajás sólo kernel, no hace falta .reload /user todo el tiempo;
  • si querés ver módulos user o pegar breakpoints user, sí hace falta contexto correcto;
  • lm m <module> sobre un módulo user no sirve si todavía estás en otro proceso.

11. Errores comunes vistos en vivo

  • Confundir 0x4B8 con 0x4B0.
  • Usar offsets de una build en otra.
  • Confundir dirección del campo token con valor del token.
  • Pasar a !token un _EX_FAST_REF sin limpiar low bits.
  • Sumar 0x40 dos veces al token.
  • Habilitar Present pero olvidarse de Enabled.
  • Mantener Process Explorer abierto y ver información vieja después de modificar el token.

12. Workflow mental de la clase

1. Repasar mitigaciones: KASLR, CFG, SMEP, SMAP, HVCI, KCFG, KCET.
2. Entender que 24H2 cierra leaks cómodos por NtQuerySystemInformation.
3. Para token stealing:
   a. buscar EPROCESS target
   b. buscar EPROCESS System
   c. confirmar TokenOffset
   d. copiar System.Token crudo al target.Token
4. Para inspección:
   a. alinear token con & ~0xF
   b. usar !token y dt _TOKEN
5. Para SeDebugPrivilege:
   a. TOKEN+0x40 = Present
   b. TOKEN+0x48 = Enabled
   c. prender bit 20 en ambos
6. Validar con !token y whoami /priv.
7. Reintentar abrir/leer procesos SYSTEM.

13. Cheat sheet

Comandos WinDbg

!process 0 0 <process.exe>
!process <pid_hex> 0
!process <pid_hex> 1
!process 4 0

dt ntkrnlmp!_EPROCESS Token
dt ntkrnlmp!_TOKEN <TOKEN>

dp  <addr>
dps <addr>
!token <TOKEN_REAL>

eq <addr> <qword>

Offsets y valores

Elemento Valor
SMEP CR4 bit 20
SMAP CR4 bit 21
_EPROCESS.Token demo 1 0x4B8
_EPROCESS.Token demo 2 0x248
_TOKEN.Privileges 0x40
_SEP_TOKEN_PRIVILEGES.Enabled +0x8 desde Privileges
SeDebugPrivilege bit 20
1 << 20 0x100000

Token stealing

TokenOffset = validar_por_build()
target_field = target_EPROCESS + TokenOffset
system_token = poi(system_EPROCESS + TokenOffset)
eq target_field system_token

Habilitar SeDebugPrivilege

Present |= 0x100000
Enabled |= 0x100000

Apunte basado en clase-modulo-7-1.txt. La clase aterriza las mitigaciones modernas en dos demos data-only: copiar el token de System y habilitar SeDebugPrivilege modificando bitmasks dentro de _TOKEN.