1、最近发生的关于虚拟机软件的漏洞
VM产品的漏洞有一些特殊性,涉及到几个操作环境,比如有主操作系统、客操作系统,还有一个比较特殊的是它的虚拟机管理器里面也可能有漏洞。我列出了大概五种可以产生漏洞的地方。


首先是VMM本身可能有漏洞,我列出了三个。其中有一个是比较新的,是在x64系统模拟上VMM存在漏洞,这使Guest OS可以提升本地权限。当然在Guest OS里面也可能有漏洞,一个比较典型的案例就是CVE-2007-5671,这个就是利用在客操作系统里面安装的一个驱动程序HGFS.sys,这个HGFS使主操作系统和客操作系统可以共享文件和目录。它里面存在一个漏洞,这个漏洞是驱动上是非常常见的,对IO ctl提供的参数没有进行足够的验证,导致从用户态能够读写任意的内存地址。这也是本地提权非常典型的一个案例。


现在我个人觉得,像这种利用驱动IO ctl来提权的情况发生的比较多。原因是因为有很多IO ctl fuzz工具的产生,可以向一个驱动设备任意地发送IO  ctl,并且变换它的参数。如果发现机器蓝屏了,你就会觉得有可能这是可以利用的漏洞,它比较好找,发生的原因也比较简单,一般都是用户态传进来的参数,内核态可能直接拿参数里面的一个地址进行读写,而没有验证参数的合法性。比较好分析漏洞的成因,所以数量就比较多。


当然在HOST OS里也存在比较多的漏洞,我列出了两个是我自己发现并上报给VMware的,是在Host OS中进行本地提权的两个漏洞。如果一会儿还有时间,我会讲一下。


另外一个比较有趣的,也是比较独特的类型,是从客操作系统向主操作系统的逃逸。比如说在Windows里执行Linux代码,可以从Linux VM里面渗透出来,在Windows主操作系统环境里面执行代码,这就叫做VM逃逸。最近比较常见的就是我列出的三个,一个HGFS堆溢出,一个是HGFS共享目录遍历漏洞,还有一个是通过RPC的调用。从VM里面可以向VMX进程发起RPC调用,RPC可以携带参数,如果参数是畸形的,可以导致在VMX这个进程里产生溢出,从而执行代码。


最后一种情况,因为VMware会大量的使用第三方软件,如果第三方软件有漏洞,比如像OpenSSL,像JAVA的运行库,图形库出现问题,VMware一样会受到影响。


虚拟机逃逸,这个比较有意思,据我所知,大概有三种可能的方式从VM里面的操作系统逸出到外面的系统。以VMware为例:


首先是共享文件夹,今年已经有两个这样的漏洞了,里面的操作系统可以通过HGFS的问题访问读写外面主操作系统里的任何文件。


还有一种是通过DHCP Server的问题,这是VMware自带的一个服务,它存在一些整数溢出漏洞。如果在Guest OS里面的某个程序发送一些精心构造的数据包,就会导致在主操作系统里执行代码。


最后一种方式,利用VMware中的用于支持客操作系统向外面的操作系统通讯的渠道,它是由一个特定的IO端口来实现的,端口号是5658。当虚拟机监控器捕获到里面的操作系统对这个端口的访问,它就会认为是里面的Guest OS想要让外面的程序提供某一种服务。比如说共享一下剪切板,拖拽一些文件,很多类似这样的功能都是用这个后门端口来实现的。


这个后门包括很多命令,可以在下面这个URL找到这些命令的列表,这些命令会携带一些参数。如果这些参数有问题,是畸形的,而且外面的程序对此验证不足,就会引起在Host OS里执行任意代码。


除了上述虚拟机漏洞可能出现的地方,在虚拟机监控器中也会出现问题。这些问题,一般来说是比较难以去调试和跟踪的。为什么这么说?因为虚拟机监控器运行的环境,用任何现有的一般调试器跟踪不到,或者说根本跟不进去。当你进行上下文切换的时候,切换到客操作系统空间时,调试器不被映射,所以不能跟进去。只有用一个相当于硬件调试器的BOCHS,在里面再装VMware才可以抓到主客两个操作系统进行上下文切换的动作,来进行调试。


可能用过VMware的人有时候会发现,在客操作系统中执行某一个程序以后,VMware会弹出一个对话框,告诉你虚拟机监控器出现了问题,这时它就会自动产生一个转储文件,用IDA可以加载这个文件进行分析。我们怎么来保证每次都能产生这个文件呢?可以用下面我这个方法,写一个程序,这个程序将访问一些没有实现的虚拟设备,比如对IOAPIC的未实现的寄存器的读写,访问将引起VMware的崩溃,VMware自身会产生一个转储文件,里面包含了VMM的状态信息,包括VMM代码全部在里面,可以进行调试,由此来发现一些在VMM里面的漏洞。


VMM在执行过程中,在主客操作系统会进行切换,会拥有两个独立的空间,并有五个不同的上下文环境,大家有兴趣的话调试一下VMware就可以看到这些东西。


对于VMM我有一些安全性上的考虑,如果我们把一段恶意代码,用某种方法把它注入到VMM的空间里面,它就会在特权级0来执行。这个想法和今年一个国外研究者在BlackHat上讲的如何通过DMA或者是利用某些漏洞的方式来改写Hypervisor类似。大家可以从网络上找到演讲的PPT,非常精彩的一个演讲,它的基本思想和我说到的这个有异曲同工的地方。


如果你想运行在ring0下,可以直接写一段代码并注入到VMM中,它将会自动执行。问题是怎么样把代码写到VMM中,一个比较简单的方法是你打开VMX这个进程,VMM会被映射到这个进程的内存地址空间中,找到它以后把一些没有用的函数替换掉,然后直接把代码注入进去,这段代码就会在客操作系统环境中有机会执行。


这是前面看到的,在主客操作系统切换后会有一些不同的上下文环境,只有VMM是真正运行在ring0的,而Guest OS在VMM的控制下,他自己认为自己是运行在ring0,而实际上不是,这就是一种欺骗。TSS和IDT并不是由Guest OS直接操纵的,并且对于产生的异常VMM有的会自己处理,有的则会转发给Guest OS去处理。GDT中的描述符,有一部分是给Guest OS使用的,有一部分是给VMM自己使用的。


2、虚拟机执行环境的检测与反检测
为什么要进行虚拟机执行环境的探测?因为有一些病毒/恶意程序不想在mi-guan里进行发作,它们要是发现自己执行在这样的环境中,就不会把某些(恶意)动作表现出来,所以实际上看不到它们在做什么。


我们为什么要进行反检测?作为一个反病毒工程师,当他发现什么病毒迹象都看不到的时候,是非常痛苦的,所以我们下面就教你如何使这些病毒样本在VMware中进行发作。


现在已知的虚拟机检测方法有下面几种:
检测虚拟机软件的一些进程、注册表项,虚拟的硬件设备名称,利用不可虚拟指令,或者通过计时的方法。
通过检测VM软件留下的印记或虚拟的硬件设备,这种方法原理比较简单:VM软件会在里面的客操作系统中安装一些特定的程序,它有自己特定的一些进程和注册表项名称。同时,有些虚拟硬件设备名称中的某些字样是固定的。病毒和恶意程序通过直接检测这些名称,就可以知道自己身处在虚拟机环境中。


针对这种检测方法,我们的反检测可以使用一个API监控器来看一下它到底是在检测哪些东西。当然,最好使用一个尽量底层实现一些的。


对于使用不可虚拟的指令的检测,比如说像SIDT、SGDT这种指令在虚拟机里面执行出来的效果和在真实的机器上执行的结果是不一样的。比如说SIDT它取到的IDT的地址和实际的Guest OS IDT的地址是不太一样的(ring3下执行)。如果你发现取得的地址非常高,你就能确定你处在一个虚拟环境中执行。这个原因是由于VMware对于Guest OS ring3的执行没有采用二进制翻译,而是让它直接执行,所以它对这些指令没办法去进行干预。


可以解决的办法就是在VMware的配置文件里禁止加速(或打开模拟模式),这样的话,VMware对ring3也会进行二进制翻译,运行会相对慢一些,虽然慢,但这种反检测方法对于利用不可虚拟指令的检测方法是非常有效的。
刚才我们讲到VMware Backdoor Port,这同样也是一个可以用来检测VMware环境的一个方法。当然我们也有相应的对策,可以在同样的配制文件里,通过使用一些配置项,把某些Backdoors的调用禁止掉。当然,有一些Backdoors调用不可以把它取消,否则机器就不能正常启动,这点我们只能手动地来进行,在这个机器启动起来以后,再手工地把它们禁止掉。这是VMware VMX进程中的GetMemSize函数,在用红色标记的一行处可以把ESI寄存器的内容清零,这样利用这个Backdoor的检测就会失效。


还有一种检测方法,就是通过利用一些VMware模拟实现上的Bugs,就是VMM模拟出来的效果和真实执行的效果可能有一些不同。报出来的漏洞其中有一个是VMware对于超过CS段界限的近程控制转移,没有进行正确的模拟。应该是先产生一个#GP(通用保护错误),而EIP保持不变。而VMware模拟出来的却是先改变了EIP,再产生#GP,这和真实环境有一些差异,所以可以用来检测VMware的模拟模式。对于这种检测方法,我们目前暂时还无能为力,只能等待VMware来修补这个模拟上的缺陷。


最后一种检测方法是通过计时。有些指令必须通过VMM的模拟才可以执行,所以说它这条指令的执行所需要的CPU周期和真实的指令执行是不一样的。我们可以在这条指令执行之前,读一下TSC,执行之后再读一下,如果发现它的差值过大的话,这条指令就有可能被进行了模拟。针对这种检测方法,经过我的实验,有一个尚可接受的解决方案,就是可以通过配置文件的一个配置项,让它把VIRTUAL_RDTSC这个选项设置成False,这样可以在一定程度上解决这个问题(需要使用直接执行模式)。还有一个方法,就是把控制寄存器CR4的TSD位置位,这样RDTSC这个指令在ring3下执行就会产生异常,连接一个内核调试器上去就能够捕获到这个异常,并继续跟踪下去。本文主要内容整理自McAfee研究员孙冰先生在2008中国软件安全峰会上的演讲