Nodejs调试端口rce&CVE复现(水文)

RCE via Nodejs debug port & DNS Rebinding

前言

翻Nodejs的历史漏洞的时候翻到一个CVE

漏洞描述中的这个美丽的域名深深吸引住了我

“localhost6”

于是仔细看了一下这个CVE以及前后两个相关的CVE(CVE-2018-7160&CVE-2022-32212)

本文将介绍如何通过Nodejs的debug端口进行rce以及三个与之相关的CVE:CVE-2018-7160,CVE-2021-22884,CVE-2022-32212

Nodejs debugger

在介绍三个CVE之前,我们先来看一下Nodejs的Inspector模块

Nodejs在运行服务的时候,可以通过--inspect参数来开启一个debug端口

1
2
3
4
5
node --inspect app.js

Debugger listening on ws://127.0.0.1:9229/bd095ef7-8094-4472-a2c6-838c13d5ca6a
For help, see: https://nodejs.org/en/docs/inspector
Example app listening on port 3000

默认端口为9229

官方文档中给出了不同工具使用这个端口的方法,接下来我们以Crhome为例

在Chrome地址栏中输入chrome://inspect/#devices,这时可以看到本地开启调试的服务

点击inspect,我们将会打开一个debug窗口,此时可以调试程序并执行任意代码

XSS -> RCE ?

那除了使用Devtool,我们能否通过script来直接访问这个端口进行rce呢?

我们观察前面开启服务时的提示信息

1
Debugger listening on ws://127.0.0.1:9229/bd095ef7-8094-4472-a2c6-838c13d5ca6a

在此可以看到这个debugger运行时使用的是WebSocket协议,而ws是可以跨域的

同时我们可以在Chrome DevTools Protocol文档找到Runtime.evaluate

我们在本地开启debug端口的状态下去访问以下页面

1
2
3
4
5
6
7
8
poc page
<script>
var ws = new WebSocket("ws://127.0.0.1:9229/bd095ef7-8094-4472-a2c6-838c13d5ca6a");
ws.onopen = function() {
data = 'require = process.mainModule.require; execSync = require("child_process").execSync; execSync("open /System/Applications/Calculator.app");'
ws.send(JSON.stringify({"id":1,"method":"Runtime.evaluate","params":{"expression": data}}))
}
</script>

发现是可以成功执行的

但是webSocketDebuggerUrl后带有的uuid是未知的,想要得到该uuid,需要访问debug端口的/json路由

但是这样的话就需要重新面临同源的问题,根据同源策略,其他来源的javascript是无法取得127.0.0.1:9229/json的响应的

由此延伸出以下几个CVE

CVE-2018-7160

https://nodejs.org/en/blog/vulnerability/march-2018-security-releases/#node-js-inspector-dns-rebinding-vulnerability-cve-2018-7160

在nodejs官方给出的CVE情报中写到:

Node.js 6.x and later include a debugger protocol (also known as “inspector”) that can be activated by the –inspect and related command line flags. This debugger service was vulnerable to a DNS rebinding attack which could be exploited to perform remote code execution.

即基于DNS rebinding绕过同源策略从而进行rce

虽然理论上来说,如果攻击者能够实施DNS rebinding,那已经不是nodejs的问题了,但nodejs官方也不能排除用户被攻击的可能性。

于是Nodejs官方给出了这样的修复:

We updated the inspector implementation with an additional check for the browser provided Host header. If the connection is via a hostname, i.e. subject to DNS resolution, we ensure that the connection is to either localhost or localhost6 precisely.

在连接websocket时增加对Header中Host字段的验证

后面的两个CVE都是对这个验证的bypass

CVE-2021-22884

于是我们来到前言里提到的这个CVE和这个神奇的域名

先说结论:

。。。CVE情报写错了(无语=_=b)

笔者一开始以为“localhost6”是一个什么神奇的利用unicode特性进行的绕过,然后去找到了这个CVE的修复commit

https://github.com/nodejs/node/commit/43ae9c46c3

。。。发现修复里的域名是localhost6

再找别家的CVE情报看看

写的也只是localhost6

官方公告里也是localhost6,没有奇怪的符号

。。。。基本可以确认是贴CVE情报时贴错了
(我不理解什么样的剪切板会把引号变成“

回到这个CVE,这个CVE的意思就是:inspector对Host校验的白名单里有localhost6这个域名,但是有些机器的/etc/hosts中并没有存这个域名(比如笔者的个人电脑就是没有的),攻击者可以使用这个域名继续进行DNS rebinding达到CVE-2018-7160的攻击

CVE-2022-32212

https://nodejs.org/en/blog/vulnerability/july-2022-security-releases/#dns-rebinding-in-inspect-via-invalid-ip-addresses-high-cve-2022-32212

该CVE依然是一个对Host检查的绕过:检查时并没有验证ip是否合法,譬如10.0.2.555,当浏览器访问该ip时会发起一个Dns request,攻击者可以在此时实施DNS rebinding

https://github.com/nodejs/node/commit/a1121b456c

总结

虽然理论上可以rce,但是利用条件实在有点苛刻……

  • 需要nodejs服务开启了debug端口
  • 需要利用DNS rebinding绕过同源获取到uuid(SSRF应该也可以?)
  • 需要执行在受害者机器上执行js(xss或者恶意页面)

整体上利用难度较高,不过一旦成功利用可以rce,收益还是比较高的。

碎碎念

本来以为“localhost6”是一个什么神奇的利用unicode进行的绕过,结果单纯是CVE情报写错了,有点小失望……

博客好久没更新了,先更没啥技术含量的水文章,后面有空再慢慢打理起来吧