top of page
  • Фото автораShelldon

Bypassing DEP with custom ROP chain. VirtualProtect

Today we look at a method for bypassing DEP (Data Execution Prevention) and getting a meterpreter reverse shell. Let's go!


I have used: 1. Kali like attacker machine 2. Windows 7 like victim

3. x32dbg debugger

4. Sublime text to writing an exploit 5. ropper and mona to finding gadgets


First of all let's talk about DEP. DEP (Data Execution Prevention) is a system-level memory mitigation, that feature in OS. DEP starting from Windows XP and Windows Server 2003.


From this image you can see that DEP has 4 states.


With "wmic OS Get DataExecutionPrevention_SupportPolicy" command we can know the state of DEP. In my case DEP is 0 (fully off).


Let's turn on DEP for all processes (the state is 1).

Command: bcdedit.exe /set {current} nx AlwaysOn My apologiese for russian language in VM, but if you execute this command you will get a "Operation Successfully completed". The goal of DEP is prevent code from being execuded in memory pool, stack, heap. If DEP on we cannot execute any code from non-executed memory.


Solution: For solution in this post I will use a Windows API function VirtualProtect (https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect) and ROP (Return Oriented Programming).

  1. VirtualProtect - function what can change the protection in memory. We can change the protection of shellcode memory (in stack) and execute it.

  2. ROP - Return Oriented Programming help us to create a chain that contains some gadgets (any instructions that ends with return).

We will safe all needed values and address in registers. After that we use pushad instruction (pushing all register values to stack).


#1 Finding EIP offset.


Skeleton of my exploit:


Let's create pattern for finding offset of EIP.

I am using cyclic pattern for 4000 bytes. 4000 bytes are more than enough.

Let's update our PoC:

I have just copied and paste to 'buf'.

Execute the PoC.


As you can see EIP is equals 61616275 which is 'ubaa'.


The 'ubaa' located in 2003 index.


#2 Filling EIP with B-s


Let's update PoC:


EIP should fill with B-s, If our calculations correct. Let's see.

Yep correct.


#3 Finding badchars

Let's find a "jmp esp" instruction and try to execute some nops.

I found "jmp esp" and pasted a break point.


With badchars script I got from 0x1-0xff chars for testting badchars.


Update PoC:

I added the address of jmp esp and added 200 break point opcodes. Let's see what it does for us.


If you loot in middle you can see there start from 01 to 16... so chars for testing in stack. Let's dump and check if every char is correct.


If you look there are no badchars. Only badchars is 0x00.


#4 Creating the shellcode

I have used msfvenom:


Update PoC:

Paste from terminal to you exploit.


#5 Testing DEP

Let's test it.


As you can see we in the beginning of the shellcode.


Buf execution of nop give us EXECPTION_ACCESS_VIOLATION.

What does it mean? So DEP is blocking execution of the shellcode. Do you remember that we change the state of DEP to on for all processes?

Let's dive in VirtualProtect API function.


#6 VirtualProtect

As I said in Solution part we will use VirtualProtect to change state of the shellcode memory to RWX (Read, write and execute).


Syntax:

  1. LPVOID lpAddress - the starting address of memory region, which the protection will be changed

  2. SIZE_T dwSize - regions' size that protection will be changed.

  3. DWORD flNewProtect - the value new protect of memory page that the protection will be changed

  4. PDOWRD lpflOldProtect - a pointer to a free memory region or variable that receives old page protection.

#7. Pushad

So Pushad is an instruction that pushed all values in register to stack.

The queue of registers:

1. eax, 2. ecx, 3. edx, 4. ebx, 5. esp, 6. ebp, 7. esi, 8. edi



In this image you can see which register must contain what.


#8. Exploitation part

Let's use mona.py for finding all gadgets to use for ROP chain. Note: In this post I don't escape ASLR protection, please take note.


This command for searching rop chains from all .dll librarires.

I also will be use ropper to find a easy gadgets faster.


The result of mona's rop gadget list:

You can see there only two modules don't have protection (vulnserver.exe and essfunc.dll).

Also in these modules no ASLR. That means the addresses are static.


#8.1 EIP For EIP I will paste address to the return instruction. After overflow we can go to return address and then return go to the next address from stack (highest value). We will do return and some instruction until registers don't contain the necessary value or address. I hope you understand the goal of this method.


As you can see there a lot of return, you can choose anyone.

Update PoC:

To EIP I stored address to ret #8.2 EDI

So EDI must contain address of return instruction. Now we will connect ROP. We can't just type " EDI = 0x..." in exploit. The exploit must automatically load necessiry address to EDI.


Let's search "pop edi" gadget.

What it does?

This gadget will pop the highest value from stack to edi.


I found "pop edi; pop ebp; ret" gadget. So there two pops and ret.

In this image you can see how the addresses should be in stack. Firstly address of "pop edi; pop ebp; ret" gadget. Second one the address of "ret" instruction that will be popped to edi. Third one the junk that will popped ebp (in current time it doesn't matter what will be).


Update PoC:


After EIP I pasted rop_chain.


There we in "pop edi" address.


Stack:

After execution this gadget:

1. EDI contains 0x62501022. If look up to stack image you can see that this address is highest value in stack. After "pop edi" this address go to EDI.

2. EBP contains 0x90909090. If look up to stack image too you can see that after address of "ret" stack contains "0x90909090", so after "pop edi" ret address in EDI and this address left the stack. The highest value in this time are first four nops.


#8.3 ESI

ESI must contain a pointer to VirtualProtect API.


In this register we will work with pointer and I highly recommend you to use popular registers like eax, ecx ...

Let's search VirtuaProtect.

My second apologies for russian language in debugger XD. I go to search -> all modules -> calls.


There I choosed one of last two ones. Why? Because there used a pointer to VirtualProtect.


If look at second screenshot you can see the another address in []. So this the address of VirtualProtect.


As I said for popular register you can find a lot of gadgets.

Let's explain.

1. We will pop the address "call dword ptr ds[VirtualProtect]" to eax.

2. After we go to "mov eax, dword ptr ds:[eax]; retn" and execute it.

3. After executing eax should contains the real address of VirtualProtect.

4. In the end we just change values between eax and esi.


Update PoC:


Current EIP:

Current stack:


After execution:

EAX contains the highest value in stack. Second address (mov eax, [eax]) go ret.


Current EIP:

Current registers:

As you can see EAX contains real address of Virtualprotect. RET goes to the next gadget.


Current EIP:

Registers before execution:


Registers after execution:

EAX and ESI changed the values between.


# 8.4 EBP

EBP must contain return address for VirtualProtect. In our case after changing the protection we just jump to stack pointer where locatted the shellcode.


Choose anyone.

The second address goes to "jmp esp".


Current EIP:

Current stack:

The highest value is address of "jmp esp".


The result:

We successfully popped "jmp esp" address to ebp.



#8.5 ESP We escape ESP because ESP will be automatically set up.



#8.6 EBX EBX must contains size of page, that will be change the protection.


Like another ones I used eax and ther reason why is we cannot load 0 into stack, because 0 is badchars. Instead of 0 we can use opcode of 0x201 (513 bytes more than enough for shellcode). After popping opcode we go negate to get exact value. Than change the values of eax and esi between. In the last line you can see that I haved used 0x90909090. The reason why is afger changing we have "pop esi". We should lost something to esi.

We can just settup EBX before ESI. The reason why I did it because we setup ESI before than EBX and "pop esi" can break ESI value. It's ok, If we will load nops to esi before setting up ESI.


Current EIP:

Current stack:

The opcode is the highest value in stack and it should pop to eax


EAX after pop:


Second gadget (neg eax; ret):

After negate:

Wow! as you can see eax equals to 0x201

Third gadget (xchg eax, ebx, pop esi; ret):

The result:

  1. EAX and EBX just switched values.

  2. ESI contains nops.

#8.7 EDX EDX must contain the new protection value. In our cause 0x40 is RWX (Read, Write and Execute)


  1. I used eax to store and negate opcode

  2. I didn't found the "xchg eax, edx; ret" instruction. Insted of that I found "xchg eax, ecx". We don't setup ecx yet and it's ok to use ecx.

  3. In the end I just move the value of ecx to edx. That's it. Nothing hard there.

Current EIP:

Current stack:

After popped:

EAX has this opcode.


Second gadget (neg eax):

Current EAX:

EAX equals to 0x40.


Third gadget:

Current registers:

EAX and ECX switched values.


Fourth gadget:

Before moving:

After moving:

YES, EDX contains 0x40.


#8.8 ECX ECX must contain the address to writeable place. I recommend to choose RW- (Read and Write) place.


Update PoC:


Current EIP:

Current stack:


After popping:

ECX has address to writable place.


#8.9 EAX EAX must contains just nops

Update PoC:


Current EIP:

Current stack:


After popping:

EAX contains nops and all registers are done!!


# 9 Pushad Finally lap!! In the end just add address of "pushad; ret" gadget.

Update Poc:


Current EIP:


Stack after pushad:











So the ROP chain is done. Let's run exploit!



Final Result:

BOOM!! We successfully bypassed DEP and got reverse shell.

276 просмотров0 комментариев

Недавние посты

Смотреть все

Comments


bottom of page