Handlebars 4.1.2 命令执行漏洞深入分析与利用
漏洞概述
Handlebars 4.1.2 版本存在一个严重的沙箱逃逸漏洞,可导致远程代码执行。该漏洞源于Handlebars模板引擎对JavaScript原型链的不当处理,攻击者可以通过精心构造的模板绕过安全限制,执行任意JavaScript代码。
漏洞背景
Handlebars是一个弱逻辑的模板引擎,设计初衷是为模板提供限制性的沙箱环境。引擎会将模板编译为JavaScript代码,正常情况下应该严格限制用户可以执行的操作。然而,该版本存在设计缺陷,使得攻击者可以绕过这些限制。
漏洞利用目标
要成功利用此漏洞,攻击者需要完成以下任务序列:
- 使用始终返回true的函数覆盖
propertyIsEnumerable,绕过针对NPM 755漏洞的缓解措施 - 获取对
Function构造函数的引用 - 使用攻击者控制的对象来调用函数的构造函数
- 调用构造的函数以执行payload
漏洞利用技术细节
1. 创建初始payload
攻击者首先需要创建一个包含单个字符串元素的数组,该元素定义了要执行的JavaScript代码。这可以通过将this设置为包含payload的字符串,然后调用split方法实现:
{{#with "a"}}
{{this.split "payload"}}
{{/with}}
2. 滥用helper函数
Handlebars中的with helper函数类似于JavaScript中的with操作符,但有一个关键区别:如果将函数传递给with helper,它会直接调用该函数(不带参数),并使用返回值作为上下文,而不是函数本身。
这意味着:
{{#with something}}
{{something}}
{{/with}}
实际上等价于:
output(something())
而我们想要的是:
output(something)
3. 利用__defineGetter__和__lookupGetter__
这两个较少使用的JavaScript方法在此漏洞利用中扮演关键角色:
__defineGetter__: 定义每次访问属性时调用的函数__lookupGetter__: 返回用于生成值的函数
利用代码示例:
{{__defineGetter__ "undefined" valueOf }}
{{#with __lookupGetter__ }}
...
{{/with}}
这会被编译为:
this.__defineGetter__("undefined", this.valueOf)
with(this.__lookupGetter__()) {
...
}
4. 绕过安全补丁
为了绕过针对NPM 755漏洞的补丁,攻击者需要使propertyIsEnumerable始终返回true:
{{#with __lookupGetter__ }}
{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}
{{/with}}
这等价于:
valueOf.__defineGetter__("propertyIsEnumerable", valueOf.bind(valueOf.valueOf.bind(1)))
// 结果: valueOf.propertyIsEnumerable = function() { return (1).valueOf() }
5. 获取Function构造函数引用
一旦绕过补丁,攻击者就可以获取this.constructor(即Function构造函数的引用),然后调用它执行任意代码。
完整利用流程
- 设置上下文为
valueOf函数 - 使用
__lookupGetter__获取函数引用 - 覆盖
propertyIsEnumerable使其始终返回true - 获取
Function构造函数 - 构造并执行恶意payload
漏洞时间线
此漏洞在5个月前已向NPM Security报告,但未收到回复。由于该漏洞需要配合模板注入漏洞才能利用,作者决定公开披露。
防御措施
- 立即升级到Handlebars的最新版本
- 对用户提供的模板输入进行严格过滤
- 在沙箱环境中运行Handlebars模板处理
- 禁用或限制危险的原型方法如
__defineGetter__和__lookupGetter__
总结
Handlebars 4.1.2的命令执行漏洞展示了即使设计良好的沙箱环境也可能因JavaScript原型链的复杂性而被绕过。开发人员应始终保持对第三方依赖的更新,并对用户提供的模板输入保持高度警惕。