freeBuf
记一次VMware的崩溃调试分析过程
2020-09-14 16:45:35
所属地 北京

一.研究背景

VMware Workstation是一款主流的虚拟机软件,近期启明星辰ADLab安全研究员在使用VMware虚拟机的过程中遇到虚拟机异常崩溃的问题,当从7zip中直接将文件拖拽到VMware虚拟机中,会造成虚拟机异常关闭。目前已测试过VMware 15.5.0、15.5.2、15.5.5 以及7zip 19.0、20.02等版本。本文将通过对VMware和7zip程序进行跟踪分析,最终定位虚拟机异常关闭原因。

二.VMware端调试分析

使用WinDbg-I指令将WinDbg设置为即时调试器,VMware-vmx.exe程序崩溃后自动弹出WinDbg。堆栈信息如下:

1600152663.png!small

调试信息显示stack buffer overrun异常,最初推断可能是缓冲区溢出漏洞。

1600152683.png!small

通过查询资料后发现,从Windows 8开始,Windows设计了一个新的中断INT 29H,用以快速抛出失败,在sdk中被声明为__fastfail, __fastfail内部函数不会返回。

1600152737.png!small

在上图中,程序终止于int 29h,而它的参数为0xa,对应FAST_FAIL_GUARD_ICALL_CHECK_FAILURE,由此推断问题可能出现在CFG的检查过程中。

1600152781.png!small

从函数调用栈中vmware_vmx+0x58b21地址向上追溯,动态调试程序,比较程序正常运行与异常崩溃的函数调用区别,定位到与程序崩溃相关的函数sub_1400965A0。

使用Windbg Attach vmware-vmx.exe程序,在sub_1400965A0函数设置断点,开始动态调试。从7z打开的压缩文件中拖拽cdp.pcapng的文件,程序在断点处停下。通过动态调试可知该函数中calloc分配了三个堆空间,分别用于存放:主机临时文件路径temp_path、目标文件名file_name以及VMware中的缓存目录名vm_cache_dir_name。

1600152806.png!small

但是打开主机Temp目录下却没有发现该文件,于是初步断定这是程序崩溃原因。继续往下看,3个文件相关参数全都传入了sub_140579b30函数。

1600152834.png!small

进入函数sub_140579b30,定位temp_path参数的处理。其中,sub_14057FF90函数对传入的temp_path进行了逐一遍历,sub_1405B2080函数对传入的temp_path进行了非法性检查。下面重点分析sub_140576460函数。

1600152850.png!small

sub_140576460函数将路径参数temp_path传入了sub_14049DA50。

1600152866.png!small

首先,函数sub_14049DA50通过sub_140477C70对字符串进行了处理。然后,调用wstat64获取相应路径的文件状态,如果成功获取则保存到一个结构体中,否则返回0xffffffff。由于Temp目录下并未发现备份文件,导致获取状态失败,从而返回0xffffffff。

1600152892.png!small

1600152906.png!small

返回0xffffffff后,重新回到sub_140579b30函数中,程序跳出while循环到达如下位置,输出错误信息并跳转至sub_140572A70。

1600152922.png!small

从sub_140572A70最终执行到sub_1400960C0,到达如下位置将vmware_vmx+0xb1ed90处的值赋给了rsi,即为0。

1600152936.png!small

继续往下执行,将rsi中0值赋值到rax中,然后调用0x7ff8fab0c510处,即ntdll!LdrpDispatchUserCallTarget。

1600152951.png!small

此处与静态下的过程有一点不同,静态下该处调用如下:

1600152967.png!small

如果按照静态过程执行,应当到达sub_1407C7650,即如下位置:

1600152988.png!small

在ntdll.dll被加载之前,该处数据依旧为上图所示地址:

1600153001.png!small

后来在ntdll.dll中实施CFG(ControlFlowGuard)保护机制,将vmware_vmx+0x7c9668地址处数据进行了改写,从而执行到ntdll!LdrpDispatchUserCallTarget中。

1600153021.png!small

在ntdll!LdrpDispatchUserCallTarget函数中,取r11+r10*8处的值赋值给r11时出现了问题,该地址为空,就造成了空指针引用,从而执行了int 29h,造成异常。然而,即使没有CFG机制,程序也会在执行“jmp rax”处崩溃,通过下图可以看出,CFG机制仅仅是在原本程序跳转指令前添加了一些检查。

1600153040.png!small

1600153052.png!small

至此,VMware崩溃的原因基本分析清楚了。另一个疑问是,为什么7zip已经在系统Temp下生成了文件,并且VMware也已经获取到了路径参数,却在移动前自动删除了文件呢。这就需要从7zip中寻找答案。

三. 7zip端调试分析

由上一节分析可知,Vmware crash原因是Temp目录下文件被删除。阅读7zip源码,锁定了CPP/Windows/FileDir.cpp中的文件删除函数。

1600153165.png!small

使用WinDbg加载7zip,然后在Remove函数位置进行下断,程序运行后进行拖拽操作,在Remove函数中断后对应的调用堆栈如下所示。

1600153187.png!small

堆栈中7zFM+0x5b212地址位于函数CPanel::OnDrag中,该函数为鼠标拖拽操作函数。当检测到对7zip打开的目录进行操作时,便会在Temp目录下生成一个以7zE开头的随机命名文件夹。

1600153201.png!small

然后,将该文件夹设置为目标目录,并且设置了一些数据及IpDropSourse结构体。

1600153217.png!small

继续往下可以看到一个DoDragDrop函数,该函数功能是进行OLE拖放相关操作,通过检测光标的行为分别调用一些方法并返回对应的数值。

1600153311.png!small

然后,根据DoDragDrop函数的返回值来判断光标的拖拽是否有效,从而执行对应的操作。

1600153252.png!small

1600153261.png!small

从7zip中拖拽文件到虚拟机,由于无法获知文件拖拽的目标路径,因此DoDragDrop会返回DRAGDROP_S_CANCEL(0x40101),不会执行拷贝操作的分支,而是直接将Temp目录下生成的临时目录删除。

1600153327.png!small

四.小 结

7zip压缩包中文件拖拽操作会触发DoDragDrop函数调用,该函数会获取文件数据及光标停止的位置。但是将文件拖拽到VMware窗口时,DoDragDrop函数不能获取准确的目标路径,因此无法将文件拷贝到目标位置,从而直接删除临时文件,最终导致VMware无法获取文件状态造成崩溃。

参考链接:

[1]https://0cch.com/2016/12/13/int29h/

[2]https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-dodragdrop

[3]https://github.com/kornelski/7z/tree/20e38032e62bd6bb3a176d51bce0558b16dd51e2

本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏