<img height="1" width="1" alt="" style="display:none" src="https://www.facebook.com/tr?id=619966238105738&amp;ev=PixelInitialized">

AtomBombing CFG Protected Processes

TL;DR we show AtomBombing modifications to enable us to inject code into CFG-protected processes.

In the last blog, we showed AtomBombing against MSPaint.exe and to recall, at that stage the application crashed. Let’s pick up where we left off.

 

Figure 1: AtomBombing mspaint.exe - Attempt #1

Figure 1: AtomBombing mspaint.exe – Attempt #1

Oh no, it crashed. Let’s debug.

We’ll place a breakpoint (windbg: bp) on KiUserApcDispatcher:

0:009> bp KiUserApcDispatcher

Ok breakpoint set, now let’s let the code execute until it hits the breakpoint (windbg: g):

0:009> g
Breakpoint 0 hit
ntdll!KiUserApcDispatcher:
77298d50 8d8424dc020000  lea     eax,[esp+2DCh]

Great, the process hasn’t crashed yet, and we have now stopped at our breakpoint.

Looking at KiUserApcDispatcher we should probably resume the execution of the process until the call to ds: ___guard_check_icall_fptr:

Figure 2: KisUserApcDispatcher

Figure 2: KisUserApcDispatcher

We’ll do this by executing until the next call (windbg: pc):

0:001> pc
ntdll!KiUserApcDispatcher+0x25:
77298d75 ff15d0c13277    call    dword ptr [ntdll!__guard_check_icall_fptr (7732c1d0)] ds:002b:7732c1d0={ntdll!LdrpValidateUserCallTarget (772aaa30)}

I’ll try to step over this call (windbg: p), and guess what’s going to happen?

0:001> p
(44d4.37a8): Security check failure or stack buffer overrun - code c0000409 (!!! second chance !!!)
ntdll!RtlFailFast2:
7718b5a0 cd29            int     29h

Let’s take a look at the call stack (windbg: k):

0:001> k
ChildEBP RetAddr 
007bf990 7716c7a2 ntdll!RtlFailFast2
007bf9bc 7718aa88 ntdll!RtlpHandleInvalidUserCallTarget+0x73
007bfa44 77178d7b ntdll!LdrpValidateUserCallTargetBitMapRet+0x3b
007bfee8 770338f4 ntdll!KiUserApcDispatcher+0x2b
007bfefc 77165de3 KERNEL32!BaseThreadInitThunk+0x24
007bff44 77165dae ntdll!__RtlUserThreadStart+0x2f
007bff54 00000000 ntdll!_RtlUserThreadStart+0x1b

LdrpValidateUserCallTarget is CFG’s validation function. It checks whether the indirect call target (ECX) is CFG valid.

Let’s have a look at ECX:

0:001> u ecx
ntdll!NtSetContextThread

ECX points to NtSetContextThread, which is of course not a valid indirect call target because it can and has been used to bypass CFG.

What we need to do is mark NtSetContextThread as valid in the CFG bitmap. For more information on how to do this please take a look at a previous article on BreakingMalware: http://breakingmalware.com/documentation/documenting-undocumented-adding-control-flow-guard-exceptions/

 

Overcoming CFG’s Stack Pivot Protection

Ok, so now that we took care of CFG let’s try to run it again.

Figure 3: AtomBombing mspaint.exe - Attempt #2

Figure 3: AtomBombing mspaint.exe – Attempt #2

This time no crash, but also no calc.

Since there was no crash we can assume that NtSetContextThread was called, but for some reason the shellcode never executed.

Figure 4: NtSetContextThread

Figure 4: NtSetContextThread

Let’s put a breakpoint on NtSetContextThread inside of mspaint.exe:

0:000> bp NtSetContextThread
0:000> g
Breakpoint 0 hit
ntdll!NtSetContextThread:
771782f0 b872010000      mov     eax,172h

Now that the program stopped at the beginning of NtSetContextThread, let’s try to let it execute until the return (windbg: pt). If everything works, the kernel should change the context of the thread and there should be no return.

0:000> pt
ntdll!NtSetContextThread+0xc:
771782fc c20800          ret     8

Obviously it didn’t work as well as we had hoped. The kernel did not change the context of the thread and we did reach the return instruction.

Let’s take a look at the NTSTATUS that the syscall returned by displaying the content of the register eax (windbg: r):

0:000> r eax
eax=c000000d

Oh no, STATUS_INVALID_PARAMETER.

 

A Quick Look at the Kernel

Let’s take a look at the internals of NtSetContextThread (by looking at its implementation in the Windows kernel).

NtSetContextThread calls PsSetContextThread

Figure 5: call PsSetContextThread

Figure 5: call PsSetContextThread

 

PsSetContextThread calls PspSetContextThreadInternal:

Figure 6: call PspSetContextThreadInternal

Figure 6: call PspSetContextThreadInternal

 

PspSetContextThreadInternal calls KeVerifyContextRecord:

Figure 7: call KeVerifyContextRecord

Figure 7: call KeVerifyContextRecord

 

KeVerifyContextRecord calls CFG’s RtlGuardIsValidStackPointer. This function will check that the value of ESP in the CONTEXT structure passed to NtSetContextThread is valid.

Figure 8: call RtlGuardIsValidStackPointer

Figure 8: call RtlGuardIsValidStackPointer

 

Let’s put RtlGuardIsValidStackPointer under our magnifying glass. RtlGuardIsValidStackPointer sets ESI to the value whose validity we’d like to check (lpContext->Esp).

Figure 9: RtlGuardIsValidStackPointer Prologue

Figure 9: RtlGuardIsValidStackPointer Prologue

 

It then loads EAX with the address of the ETHREAD structure of the current thread, and then uses EAX (which now points to the current thread’s ETHREAD structure) to load ECX with the address of the current thread’s TEB:

Figure 10: RtlGuardIsValidStackPoint - Store TEB in ECX

Figure 10: RtlGuardIsValidStackPoint – Store TEB in ECX

 

If ESI is located “below” the stack limit (ecx+8 points to the stack limit)

Figure 11: RtlGuardIsValidStackPoint - Check Stack Limit

Figure 11: RtlGuardIsValidStackPoint – Check Stack Limit

 

Or if ESI is located “above” the stack base (ecx+4 points to the stack base)

Figure 12: RtlGuardIsValidStackPoint - Check Stack Base

Figure 12: RtlGuardIsValidStackPoint – Check Stack Base

 

Return FALSE (=0)

Figure 13: RtlGuardIsValidStackPoint - Invalid Stack Pointer - Return False

Figure 13: RtlGuardIsValidStackPoint – Invalid Stack Pointer – Return False

 

Else

Return TRUE (=1)

Figure 14: RtlGuardIsValidStackPoint - Valid Stack Pointer - Return True

Figure 14: RtlGuardIsValidStackPoint – Valid Stack Pointer – Return True

 

To put it simply, if the value passed to RtlGuardIsValidStackPointer is not between the stack base and the stack limit, the function returns FALSE. If it is between the stack base and the stack limit the function returns TRUE. Basically this function checks whether the value of ESP in the CONTEXT structure passed to NtSetContextThread is really located within the stack of the current thread.

 

After the call to RtlGuardIsValidStackPointer returns, we have some bitwise manipulation that will cause KeVerifyContextRecord to return STATUS_SUCCESS (0x00000000) if RtlGuardIsValidStackPointer returns TRUE (0x1), and STATUS_INVALID_PARAMETER (0xC000000D) if RtlGuardIsValidStackPointer returns FALSE (0x0).

Figure 15: call RtlGuardIsValidStackPointer

Figure 15: call RtlGuardIsValidStackPointer

 

One Last Hurdle

We have two options to bypass this protection mechanism:

  1. The TEB is stored in the user mode part of the virtual address space of the target process, and it has RW protection. We can change the values (StackBase and StackLimit) so that our ROP chain appears to be on the stack.
  2. We can query the StackLimit of the target process and place our ROP chain close to it. This way we won’t corrupt the actual data stored on the stack (which will be used once we restore execution to the hijacked thread) and CFG (along with other security products, and mitigation solutions) will not flag our stack pivot.

I decided to go with option 2. I used GetThreadSelectorEntry to query the address of the target process’s TEB, and then ReadProcessMemory to read the StackBase and StackLimit.

Generally speaking, stack pivoting is quite prone to detection by anti-exploitation tools. Moving the ROP chain to the stack makes this attack much more stealthy and harder to detect. A fully weaponized injection will also construct its ROP chain to bypass EMET-like mitigation.

In order to avoid malicious use of this technique, we will not publish this code.

 

Success

Let’s try to inject our code into mspaint.exe one more time:

Figure 16: AtomBombing mspaint.exe - Success

Figure 16: AtomBombing mspaint.exe – Success

 

Check out the PoC:

 

CATEGORIES

FEATURED ARTICLES

tag cloud