vmware漏洞之二——简评:实战VMware虚拟机逃逸漏洞
下文取自360,是vmware exploit作者自己撰写的。本文从实验角度对作者的文章进行解释,有助于学习和理解。文章虚线内或红色括号内为本人撰写。
----------------------------------------------------------------
转:http://bobao.360.cn/learning/detail/4143.html
作者:skyer
0x00 前言
最近长亭把Pwn2Own中遗憾的在比赛前一天被补上的漏洞利用发了出来,Amat大佬的博客有这篇文章,同时在长亭知乎专栏有杨博士发的中文版。 但是并没有公开的exp,如何真正实现呢?自己花了十几天才写出exp,其中踩坑无数,本着分享精神,于是就有了本文。
0x01 Backdoor
backdoor是vmware实现的一套Guest和Host通信的机制,我们不需要去深入研究这种机制如何实现的,只需要大概了解一下这个机制的实现。 先看通信的代码,这部分代码在open-vm-tools的github上也有,链接在此。由于需要在VS中编译,所以需要先转换成为intel的asm格式。
在正常操作系统中直接执行in指令会导致出错,因为这是特权指令。但是在Guest中这个错误会被vmware捕获,然后传给vmware-vmx.exe进程内部进行通信。
而后面我们需要操作的message,全部通过backdoor通信方式来通信。 关于message的操作,open-vm-tools里面也有相关实现,链接在此。直接拿过来用就行了。 有了Message_Send和Message_Recv这些函数,我们就可以直接在Guest里面与Host进程进行通信。 需要注意的是Backdoor通信在Guest内部不需要管理员权限,所以此bug可在普通用户触发。
0x02 Drag and Drop RPCI
RPCI是基于backdoor实现的通信方式。open-vm-tools相关实现在此。可以直接使用这个发送RPC的函数。 这个漏洞存在在DnD操作的v3版本代码中,对应bug代码在此。
ida中更加明显:
----------------------------------------------------------------------------------
个人分析情况:
char __fastcall my_TransportBufAppendPacket_4F0540(__int64 a1, struct_v5 *a2, unsigned __int64 a3)
{
struct_v3 *v3; // rbx@1
__int64 v4; // rcx@1
struct_v5 *v5; // rdi@1 v5为发送的包。//IDA直接分析时没有结构体,需要右键自动创建
int v6; // eax@3
unsigned int v7; // edx@3
__int64 v8; // rax@9
__int64 v9; // rcx@10
char result; // al@11
v3 = (struct_v3 *)a1;
v4 = a2->payload_size;
v5 = a2;
if ( a3 != v4 + 20 )
goto failed;
if ( a3 > 0xFF9C )
goto failed;
v6 = a2->offset;
v7 = a2->totalsize;
if ( v6 + (signed int)v4 > v7 || v7 > 0x3FFFF3 )
goto failed;
if ( v3->seq != v5->seq )
sub_4F0430(v3);
if ( !v3->buffer ) // 第一次分配。
{
if ( v5->offset )
goto failed;
v3->buffer = my_malloc_3D4A90(v5->totalsize);// 以totalsize为准
v3->totalsize = v5->totalsize;
v8 = v5->seq;
v3->offset = 0i64;
v3->seq = v8;
}
v9 = v3->offset;
if ( v9 == v5->offset )
{
memcpy((char *)v3->buffer + v9, &v5->payload, v5->payload_size);// 用户更改binarysize为很大。
result = 1;
v3->offset += v5->payload_size;
return result;
}
failed:
free(v3->buffer);
v3->buffer = 0i64;
v3->seq = 0i64;
v3->totalsize = 0i64;
v3->offset = 0i64;
v3->qword20 = 0i64;
return 0;
}
----------------------------------------------
由于没有realloc或者totalsize的判断,导致第二个包的totalsize可以改成一个大值,payloadsize因此也可以变大导致一个堆溢出。
顺带一提,发送DnD操作的命令在dndCPTransportGuestRpc.hpp中。 通过阅读open-vm-tools的代码,可以得出RPC的发送对应路径:
rpcv3util::SendMsg->DnDCPTransportGuestRpc::SendPacket->RpcChannel_Send->Message_Send->backdoor
0x03逆向分析
看完相关的open-vm-tools的代码之后,开始逆向vmware-vmx.exe,我的版本是12.5.2.13578,workstation是12.5.2-build4638234版本。
首先很容易通过字符串“tools.capability.dnd_version”的xref找到对应的处理函数。
my_rpcicmd_func_map_8A140()
bindfun只是把对应的参数值写入了全局变量,其实是一个表。bindfun参数4就是对应rpc命令的处理函数,而rpc命令函数的参数3和参数4分别是我们发送的RPC原始request和RPCrequest的长度。参数5和参数6是我们得到的 reply的地址和reply的长度。
可以看出这个命令有一个参数,也就是版本号。
其他的RPC命令类似,在发送“vmx.capability.dnd_version”命令的时候,对应的处理函数中如果发现当前版本和设置的版本不一致,就会调用函数创建新的 object,把原来的版本的object销毁。
my_vmx_capability_dnd_83EE0
DnD和CP的Object的size都是一样的,都是0xa8大小。vmware_vmx+9C470
0x04 漏洞利用
Amat大佬的文章中推荐用info-set和info-get来操作堆,其中info-set对应的handle函数内部很复杂,通过windbg动态调试,可以发现我们发送“info-set guestinfo.test1 “+’a'*0xa7可以创建一个0xa8大小的buffer。实际测试我在malloc和free下断点,整个info-set过程大概有10-13次malloc(size=0xa8),也有 接近10次的free操作,最终剩下一个buffer。也就是说整个info-set过程干扰很大。
info-get可以读取刚刚set的值,这就没什么好说。 关于windows的LFH的风水,由于info-set中有多次malloc 0xa8操作,所以比较困难。我没有什么好的办法,目前我exp成功率还是比较低。
思路大概就是把内存变成这个样子:
如果一旦没有布局成功。。vmware-vmx就会崩溃。。。
如果你正好挂了windbg调试器。那么整个host就会其卡无比(暂时无法解决。Ollydbg,immunity只适合于32位。vmware12只有64位。不过linux环境下用gdb调试居然不卡!)。未知bug。只能缓慢的对windbg调试器按q退出调试。
推荐安装windbg的pykd插件,大爱python。 我写了个小脚本用来辅助调试:(其实就是打印rax)--(也可以直接用windbg打印)
1
2
3
4
5
6
|
from pykd import * import sys s = '' if len (sys.argv)> 1 : s = sys.argv[ 1 ] + ' ' print s + 'Object at ' + hex (reg( 'rax' )) |
所以就可以在attach上vmx进程的时候这么输入:
1
2
3
|
bp 7FF7E394C4D8 "!py dumprax DnD;gc;";bp 7FF7E394BF68 "!py dumprax CP;gc;";bp 7FF7E3DA05AB "!py dumprax vuln;gc;";bp 7FF7E3DA05DB;bp 7ff7e38c1b2d;bp 7ff7`e38f1dc2;g |
第一个地址是DnD Object malloc完毕后的下一条指令,第二个地址是CP Object的,第三个是vuln的,第四个地址是memcpy触发的地方,后面两个是gadget地址。
因为windows中进程重启后基地址还是不会变,所以只要你不重启电脑,可以一直用。
通过一些布局(运气)变成了如上的内存之后,就可以开始leak了。
主要是通过覆盖info-set的value buffer,修改value buffer内部的值,如果此时info-get读取的valuebuffer值不同,那就说明被覆盖了。
而如果溢出到了Object头部,从info-get读取的信息就会包含vtable的地址,从而泄露出程序基地址。
当然这个过程中有可能触发RtlHeapFree等堆函数然。。因为堆chunk头被覆盖,理所当然崩溃。。。
--------------------------------------------------------------------------
目前来看成功率越为30%左右。不过跟环境可能有关系,一次将虚拟机大小设为2G,双核,命中率极低;将大小改为512MB,单核,命中率达到30%。
在ASLR完成后,可以通过"s -a 0 L8000000 "exploit test"来定位dnd/cp对象。
--------------------------------------------------------------------------
0x05 DnD Object 覆盖
如果覆盖的是DnD Object,那么在DnD_TransportBufAppendPacket(vmware_vmx+4F0540)函数结束之后的上层函数(vmware_vmx+4FE960)会立刻发生调用。
所以在这之前,需要先在一块内存布局好vtable,原文推荐使用“unity.window.contents.chunk” 命令,这个RPC命令会把我们的参数复制进去data段上一个堆指针内部。
这个全局变量指针由命令“unity.window.contents.start” 创建。
这两个unity的命令。有反序列化操作而且没有官方文档可以看,只能自己慢慢debug,摸索出对应的结构。。具体的结构请看文章末尾的Github代码。
call之后,首先需要一个stack pivot到堆上,然后就是愉快的ROP。
需要说明的是,vmware中的data段居然是rwx的。。直接复制shellcode上去就能执行了。(用!address查看地址熟悉,内存布局)
具体的ROP见文章末尾的Github代码。
----------------------------------------------------------
断点参考:由于无法单步跟踪,只有通过r,kb指令打印信息。
ba r8 vmware_vmx+0xb87100 ".echo chunk_addr;r;kb;g"//chunk地址,全局固定偏移
bp vmware_vmx+0x11b2d ".echo stack_piovt;kb;g"//栈迁移
bp vmware_vmx+0x69220 ".printf \"msg:%ma\\n\",@r8;dc r8 L10;.echo;g"//vmware_vmx接收的rpci命令
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x44fb40 ".echo appendbuf;kb;g"//appendbuf:my_DnD_TransportBufAppendPacket_4F0540,包拼接
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x44fbdb ".echo datacopy;r r8;dc rdx;dc rcx;kb;g"//call appendbuf->memcpy;+4F05DB,包拷贝情况
ba r8 04d2c670 ".echo datacopynow;kb;gu;dq 04d2c670;g"//开始数据拷贝,04d2c670是dnd对象。
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x45e00b ".echo call0;r rbx;kb;g"//分析调用rop前,vtable如何跳转
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x45e01c ".echo call1;r;kb;g"//vtable调用
调用的函数过程(数据拷贝)
1.vmware_vmx接收rpci命令
2.my_rpci_command_check_69220对rpci进行检查,是否是支持的命令
3.sub_9D5F0?
4.my_finish_DnD_TransportBufAppendPacket_4FE960//函数5结束后,调用
.text:00000004FEA0B mov rcx, [rbx+8] ; r rbx
.text:00000004FEA0F mov r8, [rsp+28h+Memory]
.text:00000004FEA14 mov r9, rax
.text:00000004FEA17 mov r10, [rcx] ; r rcx
.text:00000004FEA1A xor edx, edx
.text:00000004FEA1C call qword ptr [r10+10h] ; 这里发生虚函数调用。调用伪造的虚表。
dnd+0x38存储chunk的地址,全局变量:base+0xb87118;->chunk内容:0460f1f0;->Stackpiovt_Gadget:base+0x11b2d(poi(0460f1f0+10))
5.my_DnD_TransportBufAppendPacket_4F0540
6.memcpy//dnd.transport传输的包,拷贝。
----------------------------------------------------------
0x06 CopyPaste Object 覆盖
如果覆盖的是CP Object,那么覆盖掉vtable之后,vmx进程不会崩溃,原文推荐使用cp命令触发vtable调用,而我用了这个Object的destructor。也就是再把版本设 置回4的话,程序会调用vtable中对应的destructor.
通过上面提到的”unity.window.contents.start“命令可以设置一个qword大小的gadget在程序的数据段上,而之前已经通过leak得到了程序的基地址,所 以可以得到这个gadget的指针的地址。
这个点不是特别好用,寄存器的值不是很方便,但最终依然找到了合适的gadget来利用。详细ROP见文章末尾Github 代码。
-------------------
个人调试附图:cp destructor调用过程
需要设置的断点
ba r8 03c4cf50 ".echo access_cp;r;kb;g"//在访问cp对象时触发。cp地址可以用s -a 0 L8000000 "exploit test"后确定。
bp vmware_vmx+0x33700 ".echo getchunk_stackpiovt;dq 0399b090 l40;r;kb;g"//栈迁移命令。已经调用cp虚表,进入rop.
ba r8 vmware_vmx+0xb87100 ".echo accesschunkaddr;"//访问chunkaddr,固定的全局地址。
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x437120 ".echo ropnow;r;kb;g"//rop地址
bp vmware_vmx+0x69220 ".printf \"msg:%ma\\n\",@r8;dc r8;g"//vmware_vmx接收的所有rpci消息。
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x44fbdb ".echo datacopy;dc rdx;dc rcx;r;kb;g"//my_TransportBufAppendPacket_4F0540,进行数据拷贝,发生溢出的地方。
-------------------
0x07 最后说两句
这个漏洞能不能稳定利用,关键在于堆布局做的怎么样,这个方面我研究不多。。以后还得继续看。长亭在这种情况能达到60-80%的成功率,太厉害了。
该漏洞在VMware Workstation 12.5.5之后被修补。
如果文章中有任何错误请在评论指出,谢谢各位表哥。
完整EXP:点我。
vmware漏洞之二——简评:实战VMware虚拟机逃逸漏洞的更多相关文章
- vmware漏洞之三——Vmware虚拟机逃逸漏洞(CVE-2017-4901)Exploit代码分析与利用
本文简单分析了代码的结构.有助于理解. 转:http://www.freebuf.com/news/141442.html 0×01 事件分析 2017年7月19 unamer在其github上发布了 ...
- VMware 虚拟机逃逸漏洞
所谓虚拟机逃逸(Escape Exploit),指的是突破虚拟机的限制,实现与宿主机操作系统交互的一个过程,攻击者可以通过虚拟机逃逸感染宿主机或者在宿主机上运行恶意软件. 针对 VMware 的虚拟机 ...
- VENOM cve-2015-3456 Qemu 虚拟机逃逸漏洞POC
#include <sys/io.h> int main() { int i ; iopl(3); outb(0x8e, 0x3f5); outb(0x41, 0x3f5); outb(0 ...
- GitHub现VMware虚拟机逃逸EXP,利用三月曝光的CVE-2017-4901漏洞
今年的Pwn2Own大赛后,VMware近期针对其ESXi.Wordstation和Fusion部分产品发布更新,修复在黑客大赛中揭露的一些高危漏洞.事实上在大赛开始之前VMware就紧急修复了一个编 ...
- 【转载】利用一个堆溢出漏洞实现 VMware 虚拟机逃逸
1. 介绍 2017年3月,长亭安全研究实验室(Chaitin Security Research Lab)参加了 Pwn2Own 黑客大赛,我作为团队的一员,一直专注于 VMware Worksta ...
- vmware漏洞之一——转:利用一个堆溢出漏洞实现VMware虚拟机逃逸
转:https://zhuanlan.zhihu.com/p/27733895?utm_source=tuicool&utm_medium=referral 小结: vmware通过Backd ...
- Linux(二):VMware虚拟机中Ubuntu安装详细过程
Linux(二):VMware虚拟机中Ubuntu安装详细过程 目录 1 准备 2 安装 2.1 虚拟机的建立 2.2 虚拟机安装Ubuntu系统 2.3 虚拟机设置 3 完成 1 准备 1.操作系统 ...
- VMware Fusion 中如何复制centos/linux虚拟机
今天想在mac本上,弄几个centos的虚拟机,尝试搭建hadoop的全分布环境.一台台虚拟机安装过去太麻烦了,想直接将现有的centos虚拟机复制几份完事,但是复制出来的虚拟机无法上网,折腾了一翻, ...
- windows 下使用VMware Workstation Pro 工具,ubuntu创建虚拟机
本文记录windows 下使用VMware Workstation Pro 工具,ubuntu创建虚拟机 的步骤 第一步 [文件] --- [新建虚拟机] 第二步 弹出的新建虚拟机向导对话框 标准 ...
随机推荐
- python---aiohttp的使用
1.aiohttp的简单使用(配合asyncio模块) import asyncio,aiohttp async def fetch_async(url): print(url) async with ...
- js:鼠标移动到文字显示div,移出文字div显示,鼠标能移进div
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" ...
- [LeetCode] 4. Median of Two Sorted Arrays ☆☆☆☆☆
There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two ...
- MyBatis框架的使用及源码分析(十二) ParameterHandler
在StatementHandler使用prepare()方法后,接下来就是使用ParameterHandler来设置参数,让我们看看它的定义: package org.apache.ibatis.ex ...
- 通过java客户端连接hbase 注意事项
1.通过Java客户端连接Hbase,其中hbase通过zookeeper去管理,需要注意的是客户端端口. 通过在浏览器端输入地址查看:http://192.168.3.206:60010/maste ...
- 例子Architecting Android…The clean way?----代码分析
Presention层: 整个应用启动的时候,就执行依赖的初始化.编译项目之后,Dagger依赖框架使用ApplicationComponent生成一个DaggerApplicationCOmpo ...
- 【游记】CTSC&APIO2017
GDOI回来不到两天就前往北京参加CTSC和APIO. CTSC Day1 [考试] T1一道神奇的题,很快想到O(n2)做法,感觉ctsc题目难度应该很大,就没马上想着出正解(事实上这届CTSC偏水 ...
- 【bzoj】1927 [Sdoi2010]星际竞速
[算法]最小费用最大流 [题解]跟滑雪略有类似,同样因为可以重复所以不是最小路径覆盖. 连向汇的边容量为1足矣,因为一个点只会出去一次(路径结束). bzoj 1927 [Sdoi2010]星际竞速 ...
- How to write educational schema.
Sometimes, writing such educational schemas could be of much use, and creating such docs can be bene ...
- bzoj 3207 可持久化线段树
首先因为固定询问长度,所以我们可以将整个长度为n的数列hash成长度为n-k+1的数列,每次询问的序列也hash成一个数,然后询问这个数是不是在某个区间中出现过,这样我们可以根据初始数列的权值建立可持 ...