摘要
要查看此由AI生成的摘要,您必须具有高级访问权限。了解更多信息请登录。
摘要
模板引擎在现代Web应用程序开发中发挥着关键作用,它们能够实现内容、产品和用户界面的动态渲染。如今,对于处理动态数据的任何网站(从电子商务到社交媒体)来说,模板引擎都是必不可少的。然而,它们的广泛采用也使它们成为攻击者的目标,攻击者试图利用漏洞以获取对Web服务器的未经授权的访问权限。本文全面评估了与模板引擎相关的风险,特别关注服务器端模板注入(SSTI)的后果以及此类漏洞如何轻易升级为远程代码执行(RCE),这是Web应用程序开发中的一个关键安全问题。
AI摘要
AI生成的摘要(实验性)
此摘要是使用自动化工具生成的,并非由文章作者编写或审核。它旨在帮助发现、评估相关性,并协助来自相关研究领域的读者理解本文内容。它旨在补充作者提供的摘要,后者仍然是论文的官方摘要。完整文章才是权威版本。点击此处了解更多信息。点击此处对摘要的准确性、清晰度和实用性进行评论。您的反馈将有助于改进未来的版本。要查看此由AI生成的通俗语言摘要,您必须具有高级访问权限。
1 引言
模板引擎[29, 43]是Web开发和其他软件应用程序中的重要工具,因为它们有助于将表示层与应用程序的逻辑分离。它们允许开发人员创建用于生成动态内容的模板或模式,这些内容可以根据运行时提供的数据或变量渲染为HTML、XML或其他标记语言。模板引擎是软件组件,通常以库或模块的形式提供,提供一组函数来解析和操作字符串或文件,遵循预定义的语法规则。此外,模板引擎通常会应用分词技术,将字符串或文件分解为结构化表示。这一过程允许将数据绑定到占位符、应用转换以及执行条件逻辑和循环。模板引擎的主要用途是在网站上提供动态内容。例如,提供一个包含根据访问者而变化的信息的仪表板。一个具体的用例是显示产品列表的电子商务网站。这些列表通常包含动态内容,如产品名称、价格、可用性和用户特定推荐,这使得模板引擎对于为每个产品渲染最新信息至关重要。无数现代网站都需要模板引擎,因此这项技术被广泛采用。虽然模板引擎带来了许多好处,但它们也伴随着潜在的安全隐患,开发人员应该对此有所了解。服务器端模板注入(SSTI)是与模板引擎相关的主要漏洞。SSTI是OWASP十大漏洞列表[39]中的一个注入漏洞,其影响可能是极其严重的。SSTI被利用的最糟糕后果是实现远程代码执行(RCE)[4, 20],允许攻击者控制整个目标服务器。由于大多数模板引擎都可以被攻击者用作执行任意代码的途径,因此RCE是SSTI的常见后果。然而,SSTI还可能导致许多其他潜在后果,例如:
- 信息泄露:SSTI可能会暴露服务器端配置文件、源代码和其他敏感信息,帮助攻击者进一步利用系统。
- 未经授权的访问:通过SSTI,攻击者可以获取对应用程序或服务器受限区域的未经授权的访问权限,可能控制管理员面板或其他特权功能。
- DoS攻击:SSTI可以被用来对服务器或相关系统发起拒绝服务(DoS)攻击,干扰合法用户的服务。
- 跨站脚本攻击(XSS):XSS是SSTI的常见症状,攻击者可以利用它从合法用户那里窃取敏感信息。
尽管SSTI的后果非常严重,但RCE代表了最严重的威胁,因此成为本文的重点。为了解决这个问题,我们对广泛采用的模板引擎进行了全面分析,研究了它们在通过SSTI漏洞被利用时对RCE的易感性。通过关注这一特定威胁,我们的目标是加深对模板引擎设计和行为如何使系统暴露于RCE的理解,从而有助于开发更安全的Web应用程序和服务器环境。
当前的研究尚未充分解决模板引擎中的RCE问题,尽管在现实世界场景中存在许多例子。近年来,研究的重点主要集中在SSTI的攻击性和防御性方面,开发了旨在规避保护的工具和策略。然而,模板引擎中的RCE问题在很大程度上被忽视了,缺乏用于评估和隔离的工具。未来的研究可以提供通过创建专门用于识别模板引擎中RCE的自动化工具来进行RCE检测和预防的方法。此外,鉴于大多数模板引擎当前的易感性,探索加强防御的新方法至关重要。我们的评估旨在揭示关于SSTI、RCE和模板引擎整体安全性的现有研究空白。
此外,我们将展示用于SSTI检测的主要工具,分析它们的功能及其主要差异。我们对SSTI进行了文献回顾,因为它提供了关于这一主题的开放研究问题的有趣见解。我们还将详细介绍多年来模板引擎的广泛采用及其影响,重点关注它们的使用范围以及自SSTI被发现以来发现的漏洞数量。
我们首先提供有关模板引擎使用的见解,展示这项技术的广泛应用程度。我们的第一步是查询GitHub以获取与模板引擎相关的仓库数量的数据,提供它们随时间变化的全面概述。此外,我们探讨了过去报告的实际SSTI和常见漏洞和暴露(CVEs)[11]实例。利用HackerOne[18]漏洞赏金平台,我们收集了与SSTI相关的报告,并使用CVE搜索引擎来识别记录了SSTI相关漏洞的框架和应用程序。这种多方面的方法使我们能够展示模板引擎的广泛采用及其特定的安全问题。
然后,我们展示了模板引擎的基本特性以及它们如何用于渲染动态内容的实际代码示例。我们还研究了使模板引擎容易受到RCE影响的底层机制,考察了各种情况。此外,我们将介绍我们开发的一种方法来分析模板引擎中的RCE攻击和防御,并应用该方法对八种编程语言中的34个模板引擎进行了分析。从结果中,我们展示了我们在分析的模板引擎中发现的四种RCE路径。此外,我们探讨了开发人员为防止RCE攻击而采取的缓解策略和最佳实践。我们将这些策略分为我们在分析过程中遇到的四种方法。我们还将探讨与SSTI保护相关的其他策略,包括输入验证和安全的模板引擎配置。我们的评估还强调了保持依赖项更新的重要性,因为一些模板引擎会进化以解决安全问题。我们讨论了开发人员和模板引擎社区在减少RCE漏洞风险和促进更安全的Web开发环境中的作用。
接下来,我们总结了本文的主要贡献:
- 我们展示了模板引擎在现实世界应用程序中的使用和普及情况,包括关键的SSTI漏洞及其相关CVE的示例。我们还介绍了我们研究中选择的模板引擎,强调了它们的总体特征、受欢迎程度(例如GitHub星标数量)以及可用的月度下载统计信息。
- 我们通过识别导致RCE的模板引擎行为,并检查这些漏洞出现的不同情况,提供了SSTI漏洞的全面概述。
- 我们提出了模板引擎中遇到的RCE类型的分类,以及实施的预防机制的分类。
- 我们回顾了关于SSTI的现有文献,涵盖了攻击方法、沙箱逃逸技术和防御策略。我们还识别并讨论了仍未解决的重大研究空白。
- 我们分析了目前可用的主要SSTI检测工具,概述了它们的能力、局限性和对漏洞识别的实际影响。
- 我们提出了一种系统化的方法来评估模板引擎的安全态势,从实现RCE的可行性到实施的保护措施的存在和有效性。
- 我们应用我们的方法评估了八种不同编程语言中的34个模板引擎,提供了旨在为研究人员和实践者提供信息的全面比较分析。
- 我们提供了在评估过程中产生特别显著结果的特定模板引擎的详细案例研究。
文章的其余部分结构如下:第2节展示了这些年模板引擎和SSTI的发展情况,分析了使用情况、CVE和实际漏洞。第3节提供了模板引擎的工作原理、SSTI的产生原因及其后果,以及为什么任意代码执行是一个常见问题。第4节重点讨论了模板引擎中的RCE,解释了类别、防御措施及其局限性。第5节总结了关于SSTI的最重要研究。第6节提供了现有SSTI检测工具的概述,描述了它们的特点和局限性。第7节展示了我们对34个模板引擎的分析结果以及我们采用的方法。第8节重点讨论了我们在分析过程中遇到的五个模板引擎,提供了详细分析。第9节总结了我们工作的主要收获。第10节总结了本文关于SSTI和模板引擎中RCE问题的未来研究方向。
2 模板引擎和野外的SSTI
在深入探讨模板引擎的工作原理之前,我们收集并展示了这项技术在现代Web应用程序领域中的采用情况、普及程度和影响的相关统计数据。本节分为四个部分:第一部分围绕模板引擎的使用,其余三部分关注与此技术相关的问题,即SSTI和RCE。图1显示了在GitHub上搜索模板引擎时找到的仓库数量。我们按仓库的创建日期对结果进行了划分,考虑了2008年至2024年的年份。虽然结果中的每个仓库并不都是模板引擎,但可以合理推断出模板引擎的数量每年都在持续增加。这一趋势表明,尽管现有的模板引擎很多,但新的模板引擎仍在不断出现。这是由于模板引擎的功能方式和效率不同所致。
图1显示了在GitHub上搜索模板引擎时响应的仓库数量。左侧显示了按创建日期划分的每年仓库数量,右侧显示了累计总数。2020年仓库数量显著增加。虽然确切原因尚不清楚,但这一趋势与COVID-19大流行的开始相吻合,这通常与开发人员行为的变化有关,包括开源贡献的增加和远程软件开发活动的增加[62]。
2.1 模板引擎的使用
由于每种编程语言都需要自己的模板引擎库,因此程序员可以根据需要选择多种不同的模板引擎。因此,在考虑模板引擎的当前和未来使用情况时,我们还必须考虑到为新兴编程语言定制的新引擎的开发。大多数模板引擎都是开源的,并且经常与流行的框架(如Flask[41](使用Jinja2[49])或Laravel[38](拥有自己的引擎Blade[37])相关联。为了了解它们的广泛采用情况,我们可以分析基于星标评分的高人气GitHub仓库以及基于这些模板构建的框架的月度下载统计信息。还有许多不太知名或专有的模板引擎,表明这种技术的使用更加广泛。表1展示了所分析的模板引擎及其对RCE的易感性。我们观察到,许多最常用的模板引擎都允许远程代码执行(RCE),这将在第4节中进一步探讨。这表明在选择模板引擎时,通常没有考虑到RCE的风险。实际上,像Flask、Vue和Laravel这样的Web框架的流行程度表明,模板引擎的选择往往不是优先考虑的因素,默认或用户友好的引擎是最常被采用的。表1总结了所分析的模板引擎及其流行程度以及它们是否允许RCE。
2.2 实际应用中的SSTI漏洞
本节重点关注那些被发现存在SSTI漏洞的实际应用程序,以及这些漏洞对公司和机构的影响。在以下小节中,我们还将展示在使用模板引擎的流行框架和应用程序中发现了许多与SSTI相关的漏洞(CVE)。实际网站和框架中SSTI的严重性通过漏洞赏金报告得到了证实。表2显示了六个SSTI漏洞报告。报告列表明,大型公司和机构也可能存在SSTI漏洞。此外,对这些报告的作者支付的赏金表明,公司认为SSTI是一个关键的漏洞。关键词显示,SSTI常常会导致RCE,因此公司必须修复它。此外,多年来这种漏洞的持续存在表明,SSTI仍然是实际应用程序中需要妥善解决的问题。SSTI对应于CWE-1336(模板引擎中特殊元素的处理不当),该漏洞属于更广泛的CWE-94(代码注入)类别。尽管有精确的CWE分类,但SSTI经常出现在其他CWE ID下,这使得很难准确估计与此漏洞相关的CVE数量。尽管如此,我们考虑了所有被归类为CWE-1336的CVE,并搜索了模板注入相关的漏洞。表3显示了10个具有SSTI漏洞的CVE。基础分数突显了这些漏洞出现时的严重性,以及RCE通常是SSTI的直接后果。此外,我们可以注意到这些CVE背后的引擎通常是相同的(Twig、Velocity和FreeMarker),这可能是由于它们的流行度和语言使用(Java和PHP)。
2.3 SSTI场景
在实际应用中,有两种常见的模板引擎使用场景。首先,利用模板引擎动态渲染HTML页面的网站是当代网站中的普遍用法。在这种情况下,防止SSTI至关重要,全面了解模板引擎的工作原理可以降低与RCE或SSTI漏洞相关的风险。其次,内容管理系统(CMS)通常需要为用户提供模板功能。在这种情况下,使用不允许RCE的引擎是必不可少的。另一个例子是“网站即服务”平台,如GitHub Pages或Netlify,它们提供静态网页托管服务。例如,GitHub Pages依赖于Jekyll这个Ruby框架来构建静态网站,其中包含Liquid模板引擎。尽管Liquid在流行的框架中被使用,但我们在本研究中没有详细分析它,因为它没有出现在我们用于选择分析的任何来源中。幸运的是,虽然Liquid目前没有RCE漏洞,但假设的漏洞可能会让恶意行为者利用GitHub的构建过程获取反向shell。这一实例强调了为这类应用程序选择安全模板引擎的关键作用。
通过将这些模板引擎的使用场景进行分类,我们可以强调根据应用程序需求选择模板引擎的重要性。在典型的Web应用程序中,必须完全避免SSTI;因此,选择非RCE的模板引擎仍然很重要,但影响较小。相反,在允许用户编写模板的CMS或平台上,选择非RCE的引擎是基本的。参考文献[23]还确定了两种主要的SSTI场景:无意和有意。无意的情况是Web应用程序错误地将用户输入嵌入模板中;而有意的情况是Web应用程序希望允许用户与模板引擎交互。
3 模板引擎和服务器端模板注入
本节解释了模板引擎的工作原理、SSTI是如何产生的,以及为什么RCE是一个常见的后果。为此,我们将使用Jinja2 [49]作为模板引擎来展示实际代码示例,因为它是最流行的模板引擎之一。本节将分为五个部分。在第一部分(第3.1节)中,我们将全面概述模板引擎的工作原理。在第二部分(第3.2节)中,我们将探讨模板引擎中发生RCE的可能原因。在第三部分(第3.3节)中,我们将展示如何利用SSTI来实现RCE的示例。在第四部分(第3.4节)中,我们将介绍Web应用程序中可能出现的SSTI类型。最后,在第五部分(第3.5节)中,我们将提供一个威胁模型,展示SSTI漏洞的利用方式。
3.1 模板引擎概述
为了提供这项技术的工作原理的总体概念,我们可以将模板引擎分为三个组成部分,如图2所示:(i) 数据源,通常是用于从用户请求中检索信息的数据库;(ii) 模板引擎本身,它是负责从数据库中提取数据并将其显示在HTML页面(index.html)上的核心;(iii) 模板页面(index.tpl),这是一个包含引擎可以解释以执行特定功能的符号的自定义HTML文件。模板引擎还允许程序员包含文件、生成循环、使用条件语句(if-else)以及调用函数来转换或修改数据,然后再呈现它。
为了更好地理解模板引擎的工作原理,我们提供了一个简单的实际示例。假设一个用Python编写的Web应用程序使用Jinja2作为模板引擎,这是一个与Web框架Flask [41]集成的流行引擎。假设开发人员希望将用户通过GET请求发送的字符串嵌入到HTML页面中。正确使用模板引擎的方法如下:
```python
# Listing 1: Flask Jinja2安全代码示例
# 在第一行,我们通过表单收集用户输入。我们需要安全地处理这个输入以避免任何注入。
user_input = request.form['user_input']
# 将用户输入传递给模板,将模板中的user变量绑定到Python变量user_input。
jinja2.render_template('index.html', {'user': user_input})
```
如果user_input包含“John”,生成的HTML页面将包含标题“Welcome, John!”。
相反,以下代码容易受到SSTI的影响:
```python
# Listing 2: Flask Jinja2易受攻击的代码示例
# 这段代码的主要区别在于用户输入被放在了模板内部。这意味着引擎会将用户输入视为模板的一部分,而不是单独的数据。
# 对于正常输入,易受攻击的模板的后果并不立即显现。例如,如果user_input再次包含“John”,我们仍然会在HTML中看到标题“Welcome, John!”。
# 然而,如果user_input是{{ 7*7 }},我们会得到标题“Welcome, 49!”。
# 这是因为Jinja2模板引擎找到了花括号并执行了其中的代码。
```
这个概念类似于其他基于注入的漏洞,例如SQL注入,当我们将用户输入连接到查询时就会发生这种情况。通过使用预处理语句传递未消毒的用户输入,可以减轻SQL注入的风险。为了防止SSTI,我们应该使用正确的函数参数来安全地将用户提供的数据传递给引擎,如前面的代码所示。然而,在许多情况下,SSTI可能比SQL注入更严重,因为它通常会导致RCE,而SQL注入很少会导致这种情况。
3.2 为什么RCE经常被允许?
尽管模板引擎的目标看似简单,但它们需要以安全的方式执行复杂的操作。虽然第3.1节展示了一个基本的示例,但实际场景要复杂得多。一个例子是考虑使用对象、属性和函数的可能性。假设我们的Web应用程序中有一个名为User的类,该类具有各种属性,包括用户名、名字、姓氏和出生日期。如果我们想使用前面提到的方法创建一个包含这些数据的模板,我们需要编写以下代码:
```python
# Listing 3: Flask Jinja2对象使用示例(服务器端)
```
我们可以看到,我们仍然可以轻松实现目标,但服务器代码变得更长了。现在,让我们看看通过在模板内部直接访问对象属性可以使代码更简洁:
```python
# Listing 4: Flask Jinja2对象使用示例(模板端)
```
`render_template_string`调用要轻得多,而且由于模板通常包含在单独的文件中,我们的服务器端代码将非常简单。假设在我们的Web应用程序中,我们想检查用户是否有高级账户,并根据此信息显示不同的页面。特别是,为了检查用户是否为高级用户,我们想调用user对象上的`isPremium`函数。如果模板引擎不允许我们调用函数或使用条件语句,我们将编写以下服务器端代码:
```python
# Listing 5: Flask Jinja2条件和函数使用示例(服务器端)
```
如上面的代码所示,我们必须将这种逻辑放在服务器端代码中,使其更加复杂。幸运的是,模板引擎允许使用函数和条件语句,因此可以用以下代码轻松完成这项任务:
```python
# Listing 6: Flask Jinja2条件和函数使用示例(模板端)
```
由于模板通常定义在单独的文件中,我们的服务器端代码将更加简洁。然而,这些有价值的功能也带来了安全上的代价。通过允许访问对象的属性,我们无意中暴露了对象使用的特殊内部属性。例如,在Python中,使用了内省属性,使得可以遍历对象并访问潜在危险的模块、类和函数。此外,允许调用函数还可能打开执行任意命令(如系统函数)的系统操作的大门。如果我们自己开发模板引擎,我们将面临一个关键决策:是允许对所有属性和函数无限制的访问,还是施加限制。
在接下来的部分中,我们将展示大多数模板引擎的常见做法是允许模板访问所有属性和函数,通常期望Web开发人员会谨慎操作以防止SSTI。然而,这种方法引发了一些问题。首先,安全不应依赖于开发过程中的其他人;它必须是每个阶段的固有部分。其次,现代Web应用程序有时需要用户访问模板引擎。一个显著的例子是CMS框架和平台,如GitHub Pages或Netlify,它们允许客户端不受限制地使用模板引擎。在这种情况下,避免RCE至关重要。比较SQL注入和SSTI会发现一个显著的区别。在SQL注入的情况下,通常认为责任在于Web应用程序,而不是数据库管理系统(DBMS)。相比之下,对于SSTI,我们发现有些模板引擎会促进RCE,而有些则不会。问题在于,通过SSTI实现的RCE是一个在Web应用程序中持续存在的严重问题。因此,网页开发者必须意识到这些风险,并知道如何正确使用和配置模板引擎以有效防止RCE。3.3 RCE示例第3.1节中的代码展示了一种不安全的模板引擎使用方法,因为恶意用户可以注入模板语法来发送服务器会执行的命令。由于Python具有可以用来访问应用程序全局范围和执行导入模块中任意函数的属性和方法,攻击者可以轻松开发出一种具有内省能力的载荷。这种载荷首先利用一个对象。在下面的例子中,我们使用了Flask的config对象,但我们可以使用任何对象。列表7. 利用config对象在Jinja2中实现RCE的内省载荷。上述载荷是许多可以用来利用Jinja2中的SSTI的载荷之一。在这种情况下,载荷从访问config开始,config是一个包含敏感服务器信息的Flask全局对象。我们可以通过首先访问__class__然后访问__init__方法来遍历其属性,从而到达__globals__对象,这是一个包含Web服务器范围内所有对象的字典。从这里,我们可以访问os模块,该模块允许使用popen函数执行任意命令。在这种情况下,载荷将执行ls命令。我们使用最后的read函数调用来在网页上查看命令的输出。列表8. 在Jinja2中实现RCE的通用内省载荷。N是__subclassess__函数调用返回的列表中Popen类所在的索引。这个载荷与列表7中的载荷不同,因为它最初使用了一个空字符串。这种区别使得载荷对特定对象的依赖性降低,增强了灵活性。然而,仍然必须使用__class__属性,因为它允许访问mro方法,进而提供String的父类和子类的层次结构。这是有用的,因为我们的目标是到达Object类。由于所有类都继承自Object,所以可以从任何类开始。在这种情况下,Object类位于该层次结构的索引1处(String位于索引0)。一旦我们获得了Object类的引用,就可以访问__subclasses__()方法。该方法提供了对象的所有子类的列表,从而有效地暴露了Web服务器上下文中可访问的所有类。这已经是不希望发生的情况,因为它允许攻击者访问危险类。现在,攻击者可以进一步升级,从__subclasses__返回的数百个值中找到Popen类。值得注意的是,确定N索引需要仔细考虑,因为它取决于Popen类在Web应用程序上下文中的位置。这个索引会根据Web应用程序中导入和使用的模块、类和函数而变化。攻击者可以尝试暴力破解这个索引,直到找到正确的类,或者打印__subclasses__()的结果来计算正确的索引。最终,这个载荷创建了一个子进程对象来执行ls命令。值得注意的是,这种方法可以适应于执行任意代码或命令,为攻击者提供了控制服务器的广泛可能性。现在我们已经了解了SSTI如何升级为RCE的细节,我们可以探讨SSTI出现的不同方式。3.4 SSTI的类型与其他基于Web的漏洞(如跨站脚本XSS)一样,SSTI也可以通过不同的方式触发。三种主要识别的类型涵盖了SSTI出现的不同场景。—非持久性。SSTI载荷由攻击者发送并嵌入到模板中。然后渲染响应,并执行载荷。—持久性。SSTI载荷由攻击者发送并存储在Web应用程序服务器上。然后检索载荷并将其嵌入到模板中。当攻击者触发模板中载荷的加载时,攻击就发生了。—盲态。如果载荷执行结果没有显示在渲染的页面或其他任何地方,我们就处于盲态场景。攻击者可以使用带外通信从服务器中提取数据。盲态SSTI的一个例子是模板被解析但没有在网页上渲染。考虑到上述类型的SSTI,我们可以有四种不同的场景组合:(i) 非盲态非持久性SSTI,(ii) 非盲态持久性SSTI,(iii) 盲态非持久性SSTI,以及(iv) 盲态持久性SSTI。3.5 威胁模型SSTI漏洞的利用始于攻击者注入自定义载荷,这些载荷被应用程序解释为模板的一部分。这些恶意载荷可以通过请求的各个部分传递,尽管HTTP请求的GET和POST参数是主要的传递方法。载荷可能从这些属性中提取并在模板声明中反映出来,表明载荷已成功执行。此时,攻击者可以通过向SSTI载荷中注入HTML字符串来触发XSS攻击——提供一个最初可利用的具体后果。攻击者还可以通过利用SSTI来实现RCE,这取决于模板引擎是否支持任意代码执行。如表1所示,大多数模板引擎都允许RCE,这大大增加了SSTI利用的可能性和影响。应用程序也可能持久存储SSTI载荷,然后在访问网站的特定部分时检索并执行该载荷。在这种情况下,攻击者可能会利用允许用户创建示例模板的功能,而用户可能不知道其中可能存在RCE。这个攻击者可能是该功能的合法用户,但不应该具有服务器级别的访问权限,但他可以利用该功能获得对服务器的未经授权的控制。专家攻击者还可以通过执行定制的载荷来发现盲态SSTI漏洞,这些载荷会引发服务器响应时间的差异。在发现并确认SSTI后,攻击者可以使用复杂的载荷在没有任何服务器视觉响应的情况下提取数据。4 模板引擎中的远程代码执行本节探讨了模板引擎中不同类型的RCE漏洞及其相应的防护措施。为此,我们将首先通过描述RCE在引擎中出现的各种方式来分类开发者如何应对RCE。这部分内容较少基于相关学术论文(因为没有其他文章以这种方式分析模板引擎),而更多基于我们对34个选定模板引擎进行的实验结果。4.1 模板引擎中的RCE类型第3.3节中的RCE示例只是模板引擎中实现RCE的众多方式之一。在分析了34个模板引擎后,我们发现了四种主要的RCE利用类型。现在我们将介绍这种分类,以了解模板引擎如何以不同的方式允许RCE。—直接代码执行。在模板引擎的默认分隔符内嵌入的任何代码的执行。这种做法在模板注入的情况下使模板引擎极其危险,因为攻击者可以轻松且立即执行任意代码。—用于代码执行的标签或函数。模板引擎中存在特定的分隔符、函数或指令,可以用来执行任意代码。这是引擎创建者提供的有意功能,因为他们认为网页开发者可能希望使用它们在引擎内部执行特定代码(例如,定义辅助函数)。即使提供此类功能没有问题,但如果出现SSTI或希望用户在其Web应用程序中使用引擎,网页开发者也应意识到这种危险。—内省。内省利用取决于模板引擎的编程语言。每种语言都有不同的函数和属性,可以用来遍历对象并实现任意代码执行。这种利用相当常见,因为模板引擎通常允许访问传递给它们的对象的属性和函数。因此,使用面向对象语言的内省功能是攻击这种机制的最简单方法。—漏洞或缺陷。模板引擎中可能会出现漏洞,并被转化为实现RCE的方式。这一类别包括模板引擎的意外行为,这些行为可以被利用来在原本安全的引擎中实现RCE。4.2 防止RCE一些模板引擎还实施了安全特性,以更好地抵抗特定的利用。这些特性通常由引擎开发者实现,以阻止或减少RCE的风险。我们使用“抵抗”一词,因为这些安全特性旨在提供更高的RCE防护。然而,它们的有效性取决于实现程度以及它们是否默认启用,或者是否有可选开关。接下来,我们将展示模板引擎中可以提供的各种安全特性。—沙箱。修补或限制模板引擎任意代码执行能力的最简单方法是阻止某些操作或阻止特定函数。因此,大多数希望提供基本安全级别的模板引擎都使用了沙箱。我们可以将此特性定义为模板引擎语法中存在一个黑名单。每当在黑名单中找到某个词时,就会阻止模板的执行。这种安全特性的问题在于它不能提供针对任意代码执行的强大保护。在许多类似的情况下,沙箱可以被绕过,因此网页开发者应该知道只需保持引擎更新就可以避免这种风险。此外,许多模板引擎只有在发现RCE漏洞后才会设置沙箱,这使得之前的版本变得脆弱。这也是开发者应该知道的事实。如果模板引擎提供了沙箱,应始终使用最新版本的引擎,否则存在沙箱未更新的风险。—无函数调用。对于各种模板引擎发现的所有内省载荷,都需要在某个点进行函数调用,无论是实例化任意对象还是调用内省函数。阻止任何函数调用可以有效避免代码执行,但也可能严重影响模板引擎的功能。—有限的代码执行。一些模板引擎大大限制了用户在分隔符内可以或不能做的事情。我们也可以说沙箱旨在限制代码执行。然而,这两种类别之间的主要区别在于,沙箱限制特定函数或属性,以系统地阻止可以用来实现RCE的特定载荷。在这种情况下,我们指的是更一般的限制,可以通过阻止大多数函数或属性访问(例如使用白名单)或仅提供一组开发者在模板中可以使用的自定义指令来实现。—无RCE漏洞。一些模板引擎可能没有任何特定的安全特性,但没有发现任意代码执行。这种情况的一个例子是模板引擎的语言不支持内省载荷。5 文献综述为了确定已发表的与SSTI相关的工作,我们在各种搜索引擎中进行了关键词搜索,如“服务器端模板注入”、“SSTI”或“模板注入”。我们的调查发现,Google Scholar是最全面的来源,提供了大多数相关论文。其他搜索引擎,如IEEE、ACM和SCOPUS,要么没有找到与SSTI相关的结果,要么重复了我们已经在Google Scholar上找到的相同的工作。我们的分析仅发现了四篇与SSTI相关的论文。在本节中,我们将深入探讨其中的三篇论文,而第四篇[12]将在第6节中讨论,因为它是一篇介绍SSTI检测工具的文章。我们在本节中回顾的三篇论文如下:(i) Kettle关于SSTI发现和利用的文章[23];(ii) Wang等人[61]提出的通过指令集随机化来防御SSTI的文章;(iii) Zhao等人[65]展示如何利用沙箱化的PHP模板引擎实现RCE的文章。据我们所知,SSTI的概念最早是由James Kettle在2015年提出的[23]。在我们的研究之前,学术文献中并没有提到这一漏洞。他的研究首次揭示了模板引擎的相关风险以及攻击者如何利用它们来实现RCE。Kettle关于SSTI的工作特点在于其对漏洞的分析、实际的利用技术,以及SSTI在现代Web应用中的深远影响。此外,他还分析了五种模板引擎(FreeMarker、Velocity、Smarty、Twig和Jade),展示了它们如何允许RCE的发生以及沙箱保护如何被绕过。最后,他的工作还提供了对易受SSTI影响的著名框架和CMS应用程序的案例研究,证明了这种漏洞的普遍性。表4总结了从2015年至今与SSTI相关的文献。
5.1 Kettle的利用方法论
在参考文献[23]中,Kettle提供了一种逐步的方法来在黑盒环境中利用SSTI。这种方法也被SSTI检测工具所采用,有助于理解不同模板引擎之间的差异和相似性,以及在利用过程中了解Web应用程序运行的是哪种引擎的重要性。该方法包括三个主要步骤:
- **检测**:在检测阶段,目标是确认SSTI的存在。SSTI可能出现在两种情况下:明文和代码中。在明文环境中(我们在示例2中看到了一个例子),HTML可以直接输入到模板中,这通常是XSS的来源。为了检测这一点,可以使用通用负载(例如典型的{{7*7}})来调用模板引擎。
- **识别**:一旦检测到SSTI,下一步就是识别所使用的模板引擎。有时,发送无效语法以引发错误消息可以揭示引擎类型。否则,需要尝试不同的负载并观察它们是否被执行。
- **利用**:找到模板注入并识别出模板引擎后,攻击者通常需要找到一种方法来升级到RCE。第一步可以阅读模板引擎的文档,因为它可以提供有关方法、变量和特殊功能的详细信息。如果此时还没有找到升级的方法,则可以探索引擎环境,看它是否泄露了可用于进一步破坏系统的敏感信息。此外,模板可能会暴露出可以被攻击者利用的对象来执行恶意操作。
尽管这种方法具有实用性和有效性,但它主要关注SSTI漏洞的攻击方面。此外,它缺乏对当今广泛使用的各种模板引擎的通用性。未来的研究可以致力于提供一种更通用的方法来检测和防御SSTI漏洞,并探索自动化评估模板引擎中RCE的方法。
5.2 通过指令集随机化防御SSTI
Wang等人[61]提出了一种新的防御SSTI的方法,该方法不需要专门的工具或扫描器。这种方法已经用于其他漏洞[7, 22, 51],被称为指令集随机化[61]。该技术通过引入随机键来为模板代码增加随机性。值得注意的是,模板引擎依赖于特定的分隔符和指令来解析模板。例如,当引擎遇到字符串{{(在Jinja2等模板引擎中通常用作默认分隔符)时,它会将其识别为要执行的变量块的开始。一些模板引擎提供了环境函数或属性,允许自定义这些分隔符。如果这些选项不可用,通常可以修改模板引擎的代码以识别不同的字符作为指令分隔符(例如,可以使用[[]]代替{{ }})。
指令集随机化基于这一功能,通过生成一对随机的分隔符(例如{{randomString}})来工作,这对潜在攻击者来说是未知的。当Web应用程序容易受到SSTI攻击时,尝试注入模板代码的攻击者将无法执行它,因为引擎无法识别默认的分隔符。需要注意的是,这种保护的有效性取决于所选或生成的随机字符串的保密性。因此,该字符串不应以任何方式在应用程序中泄露,并且需要足够长和复杂以抵抗暴力攻击。这种SSTI防御方法在SSTI是非故意的情况下特别有效。然而,在Web应用程序故意向用户暴露模板引擎的情况下,这种保护变得不切实际。此外,在非故意的SSTI情况下应用这种技术会给开发人员带来很大负担,因为他们需要手动修改每个模板,在每个指令前添加随机字符串(这个过程可以自动化)。尽管如此,自动化需要创建脚本来解析模板文件并实现随机字符串的添加。修改分隔符并不总是简单的:正如我们所说,许多模板提供自定义选项,但其他模板可能需要修改模板引擎的核心代码。这种做法引入了几种安全风险,包括每次引擎更新时都需要重新修改代码,以及在代码修改过程中引入新漏洞的可能性。
5.3 PHP模板引擎沙箱逃逸
在4.2节中,我们看到模板引擎开发者引入了沙箱模式来限制模板标签的功能,防止攻击者实现RCE。然而,Zhao Y.等人的文章[65]最近提出了一种模糊器,旨在自动检测和利用模板引擎中的沙箱逃逸漏洞。实际上,它利用了一个之前被忽视的漏洞,即模板逃逸,该漏洞允许攻击者绕过沙箱并获得RCE。当模板代码中的攻击者控制输入在转换为PHP代码时逃逸了模板的语义时,就会发生这种情况。参考文献[65]使用CVE-2021-26120作为示例来说明模板逃逸漏洞的工作原理。通过精心设计模板函数名称,攻击者可以将PHP代码注入到转换后的PHP文件中,从而获得执行权限。文章指出,模板逃逸漏洞可能并不像之前认为的那样罕见,并呼吁研究每种模板引擎中这些漏洞的普遍性和严重性。
5.4 开放研究空白
如前几节所述,关于SSTI的研究仍然有限。尽管存在一些检测工具(将在下一节进一步讨论),以及Silva[12]的工作,但SSTI检测主要依赖于通过对目标网站进行负载注入的黑盒测试。目前还没有白盒或灰盒检测工具,也没有研究论文探讨这种方法。在预防方面,虽然Wang等人[61]关于指令集随机化的研究值得注意,但仍然缺乏实用的或全面的实施策略。
6 SSTI检测工具
本节将总结三种最流行的SSTI检测工具的工作原理及其主要差异。这种比较有助于识别这些工具的优势、缺点以及未来工具可能实现的改进。我们可以根据它们的流行程度来选择合适的工具。Burp是一个广受认可的商用漏洞扫描器,而ZAP是领先的开源替代品。此外,Tplmap也因其3.5k的GitHub星标和作为Burp官方扩展的地位而受到关注。值得注意的是,在我们在GitHub上搜索“SSTI”或“模板注入”工具时,发现许多其他工具要么是Tplmap的分支,要么是从Tplmap获得灵感的。在我们分析的三种工具中,第一个是在J. Kettle发现SSTI后立即发布的Burp Suite的自动检测SSTI的扩展。由于Burp[47]是最流行的Web应用程序安全测试软件之一,这一漏洞开始受到关注。之后,又创建了两种其他工具来检测和利用SSTI。表5详细比较了这四种工具。需要注意的是,由于Burp扫描器不是开源的,因此无法分析其详细信息,所以我们的分析将集中在Tplmap、ZAP-Esup和SSTImap上。
6.1 Tplmap
Tplmap [46]是一个命令行工具,用于检测和利用Web应用程序中的SSTI漏洞。一旦用户检测到潜在的SSTI,Tplmap可以通过向应用程序注入负载来自动化利用过程,以评估该漏洞是否可以被用来实现RCE。Tplmap支持多种常用的Web开发模板引擎,如Jinja2、Smarty和Twig,并且可以检测和利用特定于某些引擎的SSTI漏洞。用户还可以向Tplmap提供自定义负载,以根据具体需求或测试独特场景来调整利用尝试。该工具的主要限制是支持的模板引擎数量有限。Tplmap支持几种流行的模板引擎,但并不涵盖所有Web上使用的模板引擎。这意味着一些使用较少见或自定义模板引擎的应用程序可能无法通过此工具有效测试。目前,Tplmap支持18种模板引擎。其次,它缺乏爬取功能:Tplmap无法自动找到易受攻击的端点,用户必须手动指定这些端点。此外,其代码库已不再维护,因此缺乏对最新模板引擎的支持。再加上前述的限制,这就需要新的工具来跟上SSTI(模板注入攻击)不断发展的趋势以及新模板引擎的日益增多。6.2 ZAP-ESUP [12] 是由Diogo Silva在2018年发布的,作为Zed Attack Proxy (ZAP) [40] 的一个扩展。该工具的主要特点是使用多语言负载来克服Tplmap在支持的模板引擎和利用方法方面的限制。这里的“多语言负载”指的是一组符号和字母,可以帮助在多种模板引擎中检测SSTI。文章还讨论了开发一种用于在Web应用程序中引发错误的多语言SSTI负载的方法。为了确定触发错误的最有效方法,作者测试了四种模板标签组合:开始标签(例如,${})、结束标签(例如,})、开始标签后跟结束标签(例如,${})以及带有变量名的开始标签和结束标签(例如,${foo})。他们发现,通过发送一个开始标签、一个不存在的变量名和一个结束标签,可以在18个测试应用程序中的16个中引发异常行为。为了检测异常行为,该工具会记录应用程序在正常输入和SSTI负载下的响应。在这种情况下,模板引擎上下文中变量的缺失会导致错误,从而产生不同的服务器响应。然而,基于Java的应用程序在处理错误方面比其他编程语言表现得更好,即使变量不存在也不会导致响应上的差异。为了解决这个问题,作者为Java应用程序创建了一种特定的多语言负载,其中包含故意违反其语法的代码以触发错误。然后,为了减小负载大小,他们将各种模板开始和结束标签组合在一个现有的负载周围。最后,他们在通用多语言负载中添加反斜杠以防止渲染,并添加Twig注释标签(#{})来解决Smarty相关的问题。目标是创建一种有效的SSTI多语言负载,能够在不同的Web应用程序中触发错误,从而无需了解具体的模板引擎即可检测SSTI。ZAP-ESUP在某些方面改进了Tplmap,并解决了我们之前提到的一些限制。尽管有这些改进,并且它可以在不依赖特定引擎负载的情况下检测SSTI,但它仍然没有考虑到当前多语言负载不支持的新引擎可能具有的新语法。目前的技术水平还缺乏无偏见的测试来评估这两种工具中哪一种能更有效地检测SSTI。6.3 SSTImap 是基于Tplmap的工具,但在功能和支持的模板方面有一些差异。它的缺点是没有作为Burp插件集成,而Tplmap支持这种集成。与Tplmap类似,SSTImap目前还不支持爬取集成,尽管这似乎是未来的计划之一。在改进方面,SSTImap支持27种引擎和上下文。尽管数量超过了Tplmap,但仍然较少,特别是如果我们考虑到其中一些实际上是类似eval的代码注入或同一引擎不同版本的负载。其工作方式与Tplmap类似,它会在目标URL上注入一组负载,并尝试检测正在使用的模板引擎。7 模板引擎分析我们强调了选择模板引擎及其相关安全风险的重要性。本节提供了一种分析模板引擎并评估其安全性的方法。我们根据流行程度选择了34种模板引擎进行分析,既在GitHub上搜索(使用星号作为流行度的衡量标准),也在其他搜索引擎上搜索(例如搜索“template engines in”加上所选的编程语言)。7.1 编程语言由于可用的模板引擎种类繁多,自动分析模板引擎以寻找RCE(远程代码执行)路径是一个具有挑战性的问题。这种多样性直接源于用于Web应用程序开发和框架的多种编程语言。模板引擎作为一系列应用程序编程接口(API)运行,只能由编写该模板引擎的相同编程语言使用。因此,用Python编写的模板引擎只能由Python Web应用程序使用。自动检测RCE的复杂性还在于负载与编程语言相关联。适用于Python的内省式攻击不适用于Java,反之亦然。这种多样性在防御RCE和SSTI时带来了许多挑战,其中之一是难以在不依赖引擎语言的情况下评估模板引擎是否允许RCE。为了了解用于开发模板引擎的编程语言种类,图3显示了在GitHub上搜索模板引擎时获得的结果,并报告了有多少仓库包含某种语言的代码。我们只考虑了分析中存在的一些编程语言。结果显示,六种流行语言占据了大约60%的仓库总数。图3. 通过在GitHub上搜索模板引擎,估计每种编程语言相关仓库的数量。7.2 分析结果为了概述模板引擎在RCE及其威胁防护方面的当前状态,我们提出了对34种不同编程语言中的模板引擎进行分析。表6显示了分析结果的一般概览,而图4显示了从中提取的一些统计数据。分析采用了三个主要步骤的方法:表6.语言名称分隔符A.A.E.F.攻击类型安全特性PythonDjango{{ }}✔✖-LCETornado{{ }} 和 {% %}✔✔标签代码执行-Jinja2{{ }}✔✔内省-web2py{{= }}✖✔内省-Mako<% %>和 $✔✔标签代码执行-Chameleon${ }✖✔内省-Cheetah3$ 和 #✖✔标签代码执行-Genshi$ 和 ✖✔标签代码执行-Pyratemp@! !@✖✖-SandboxPHPLaravel{{ }}✖✔标签代码执行-Twig{{ }}✔✔内省SandboxSmarty{ }✔✔标签代码执行SandboxJavaScriptVue{{ }}✔✔内省-Pug#{ }✔✔内省-Handlebars{{ }} 和 #✔✔内省-LCEMarko${ }✖✔内省-Nunjucks{{ }}✔✔内省-EJS<%= %>✔✔内省-doT{{= }}✔✔内省-Dust{ } 和 {@ }✔✔BugLCEJsRender{{ }} 和 {{: }}✔✔内省-Template7{{ }} 和 {{# }}✖✔标签代码执行-SquirrellyJS{{ }}, @ 和 |✖✔内省-JavaThymeleaf#{ }, ${ } 和 [[ ]]✔✔直接代码执行无函数调用Pebble{% %}✔✔内省SandboxFreeMarker${ }✔✔函数代码执行-Jinjava{{ }}✔✔内省SandboxApache Velocity$ 和 #✔✔内省-RubySlim=✔✔直接代码执行-ERB<%= %>✔✔直接代码执行-Golang默认引擎{{ }}✔✖-LCEPerlMojolicious<%= %>✔✔直接代码执行-.NETASP<%= %>✔✔直接代码执行-Razor和 ()✔✔直接代码执行-此表显示了本工作中分析的所有模板引擎的主要特性。A.A.表示已经分析过,E.F.表示发现攻击,LCE表示有限的代码执行。加粗的模板引擎名称表示我们首次发现可以通过RCE利用的引擎。图4.总结了有多少模板引擎具有或曾经具有RCE以及有多少实现了某种形式的防护。 (1) 开发一个最小的SSTI易受攻击的应用程序。分析未知模板引擎的第一步是创建一个环境,在该环境中我们可以测试模板在SSTI场景下的表现。更具体地说,这一阶段的主要目标是通过逐步修改现有代码片段来创建一个易受攻击的代码片段,以触发SSTI。这样的代码片段通常可以在模板引擎的文档中找到,并可以修改以生成一个易受攻击的情景。有了这段代码,我们可以继续测试可能的RCE负载。(2) 测试攻击和安全特性。在创建了一个包含运行中的SSTI易受攻击代码的工作环境后,我们可以测试可能的攻击。为此,我们设计了一个多步骤程序,可以揭示模板引擎是否可以通过RCE被利用。这个程序分为逐步增加复杂性的步骤,我们在第4.1节中详细介绍了这四种攻击方法。如果我们从之前的步骤中找到了一个有效的攻击方法,就可以认为目标模板可以通过RCE被利用。然而,如果我们找不到任何有效的攻击方法,并不意味着该模板引擎对RCE是安全的。除此之外,我们还根据第4.2节中描述的分类法评估目标模板可能具有的安全特性。(3) 收集结果。最后一步是收集之前的分析结果。这部分对于构建可供研究人员或程序员参考和更新的标准信息集至关重要。未来,模板引擎将会更新,它们可能会引入可能改变其执行任意代码能力的安全措施。有时,它们甚至可能会引入允许代码执行的错误或更新。获得的结果被整理成一份报告,其中包含以下信息:(i) 模板引擎的名称和语言;(ii) 引擎使用的分隔符;(iii) 可用的攻击类型;(iv) 模板使用的潜在安全特性。报告还简要描述了引擎、安全代码和易受攻击代码的示例,以及对允许RCE的负载和每种模板引擎的安全特性的更详细分析。通过分析我们的结果,我们可以得出关于模板引擎当前状态的结论。图4展示了汇总的发现,表明在分析的引擎总数中,只有少数引擎集成了任何形式的防护。值得注意的是,在评估的34个引擎中,有31个允许或曾经允许RCE。特别是在最流行的编程语言(如Python、PHP、Javascript和Java)中,这种易受攻击性更为明显。表6更详细地探讨了RCE类型和安全特性。在这里,我们了解了这些引擎所具有的RCE漏洞和安全机制的类型。内省式攻击最为普遍,影响了16种模板引擎。其次是代码执行标签和直接代码执行标签,分别影响了七种和六种引擎。关于安全措施,它们很少被实施,只有十个引擎提供了任何形式的防护。其中,沙箱是最常用的安全特性。值得注意的是,先前的研究(如参考文献[23, 65]所示)对沙箱的可靠性提出了质疑。在具有安全特性的十个引擎中,有五个包含了沙箱,而四个提供了有限的代码执行能力。这种对模板内可执行代码的限制可以显著降低RCE的风险,但也可能影响引擎的一些功能。我们从分析中获得的结果,加上第2.2节中的内容,表明SSTI仍然是一个存在于实际中的漏洞,其后果可能非常严重。尽管如此,由于不同编程语言中模板引擎的多样性,现有技术尚未通过新的解决方案来解决这个问题,这使得开发能够有效缓解SSTI风险的解决方案变得困难。此外,大多数开发者可能没有意识到为特定Web应用程序选择合适的模板引擎的重要性。相反,他们可能会选择最流行的解决方案,或者不知道某个模板引擎可能对其应用程序构成安全威胁。正如表1所示,最流行的模板也经常允许RCE。此外,分析CMS(内容管理系统)的情况时,我们可以发现一些模板引擎可以直接被用户访问的SSTI案例。CMS开发者有责任确保他们的产品可以通过SSTI实现RCE。此外,通过分析CVE(漏洞暴露),我们可以看到大多数CMS都是关键的,因为SSTI可以升级为RCE。如果这些情况下使用的模板引擎不允许RCE,SSTI的影响将会小得多。这需要提高开发者的意识,让他们在选择模板引擎时考虑到最坏的情况,即他们的网站或CMS可能容易受到SSTI的攻击。最后,当模板引擎作为服务提供给平台用户时,情况尤为突出。我们看到了GitHub页面的例子,但许多CMS允许用户使用模板引擎来构建电子邮件模板或网页。在这些情况下,风险和影响更大,因为风险在于允许任意用户控制服务器,其中可能存在用户内容,并可能泄露他们的私人数据或破坏其他应用程序。在这种情况下,选择合适的模板引擎至关重要,但这需要自动化分析RCE.7.3中的模板引擎。防范在野外观察到的并已在CVE中报告的SSTI和RCESSTI漏洞通常可以分为两大类,每一类都源于不同的根本问题。从用户输入构建模板字符串。这种情况在漏洞赏金报告中尤为常见,当用户输入未经适当清理就被嵌入到模板字符串中时就会发生。一个典型的例子是用户的注册数据被包含在模板字符串中,从而允许他们注入恶意指令。为了防止这种情况,开发人员应尽可能避免构建包含用户提供数据的模板字符串。如果必须在模板声明中包含用户控制的数据,则应对输入进行适当的清理,特别是移除或阻止敏感的模板分隔符,如大括号。使用允许RCE的模板引擎。大多数与SSTI相关的CVE都被评为严重级别,因为这些漏洞往往会导致RCE。然而,选择一个不允许RCE的框架或模板引擎可以显著降低SSTI漏洞的严重性和影响。利用表6中的数据,从业者可以快速评估并根据是否允许RCE来选择模板引擎。需要强调的是,选择安全的模板引擎在存在SSTI漏洞或应用程序允许用户构建自己的模板时,在防止RCE方面起着关键作用。
在第3.1节中,我们看到了一个使用Jinja2的模板引擎的例子。本节将通过包括其他模板引擎和不同的编程语言来扩展这一概述。值得注意的是,在分析的34个模板引擎中,有9个是之前从未分析过的,其中8个允许RCE。虽然为每一个提供详细解释会很有趣,但由于篇幅和重复性的原因,我们只关注那些能够提供有关各种编码实践、RCE类型和安全特性的有用见解的模板引擎。因此,本节中唯一一个之前从未分析过的模板引擎是Pyratemp(第8.1节)。尽管如此,我们仍可以简要总结一下之前从未分析过的其他8个允许RCE的引擎的发现。
- **Cheetah [8]** 是一个于2001年发布的Python模板引擎。文档解释了存在一个特殊标签,可以使用#{#echo}来执行直接的Python代码。
- **Genshi和Kid [54]** 是一个基于另一个名为Kid的引擎的Python模板引擎,诞生于2006年。同样,我们可以使用特殊的分隔符?>来执行任意的Python代码。
- **web2py [45]** 是一个受著名Django启发的Python框架。在web2py中,该引擎允许访问像Jinja2那样的Python内部属性。因此,可以使用类似于我们在第3.3节中看到的有效载荷来实现RCE。
- **Chameleon [5]** 是一个编译其模板以优化执行速度的Python引擎。在这个引擎中,我们可以通过尝试访问对象的内部属性来快速发现并实现RCE。
- **Laravel (Blade) [38]** 是一个用于开发PHP Web应用程序的开源框架。它允许使用不同的模板引擎,但默认使用自己的引擎,名为Blade [37]。文档分析显示存在一个raw PHP标签,允许执行代码@php echo ‘ls‘ @endphp。
- **Marko [10]** 是一个注重简单性和速度的JavaScript引擎。像许多其他JavaScript引擎一样,它允许访问内部对象函数和属性,这些都可以被轻易利用来实现RCE。以下示例展示了Marko引擎的JavaScript内部有效载荷的样子。
列表10:Marko引擎的JavaScript内部有效载荷。这种有效载荷允许执行任何shell命令,因此恶意用户可以利用它打开反向shell并在服务器上执行任何操作。总体思路是需要加载child_process模块,并使用它来调用execSync函数,该函数允许在服务器上执行任意的shell命令。内部链利用了遍历对象的能力,从简单的字符串移动到全局环境。
- **SquirrellyJS [17]** 是一个受Nunjucks、Handlebars和EJS等著名引擎启发的JavaScript引擎。通过使用上述相同的有效载荷(列表10)(但使用不同的分隔符),我们可以利用内部属性快速实现RCE。
- **Template7 [24]** 是一个专注于移动优先Web应用程序的轻量级模板引擎。在这种情况下,查看文档后,我们可以找到一个特殊的分隔符,允许我们执行任意的JavaScript代码。以下有效载荷允许在Template7上执行任意命令:js ’global.process.mainModule.require(“child_process”).execSync(“ls”)’
通过我们的分析,我们发现了这8个允许RCE的模板引擎。现在,我们可以来看五个流行模板引擎在五种编程语言(Python、PHP、JavaScript、Java和Ruby)中的五个详细示例。通过这些示例,我们希望展示模板引擎在处理RCE方面的差异以及它们的总体功能。尽管网上有关于我们分析的引擎的漏洞有效载荷的博客或速查表,但这些示例旨在进行更详细的分析。每个小节将展示模板引擎的主要特性,从简单的安全和不安全代码示例开始,然后深入探讨RCE路径、安全特性和其他特性。
**8.1 Pyratemp:Python沙箱**
Pyratemp [25] 是一个注重简单性和速度的模板引擎。它的分隔符是at符号后跟一个感叹号(@! !@)。Pyratemp还提供了一个沙箱来防止SSTI;在展示一些使用该引擎的示例之后,我们将更详细地分析这个沙箱。据我们所知,之前没有人尝试分析过这个模板引擎。
第一个示例是该模板的安全使用;以下代码不受SSTI的影响:
列表11:Pyratemp安全代码示例。
在上面的代码片段中,我们首先导入Pyratemp引擎模块。接下来,我们从HTTP请求参数username中获取用户输入。然后,我们创建模板,提供一个包含Pyratemp指令的字符串。在这种情况下,我们打算显示变量name的值。最后,我们通过调用t函数(我们为包含模板的变量指定的名称)来执行模板,并根据需要传递尽可能多的关键字参数。每个关键字参数代表模板引擎上下文中的一个变量。在这个例子中,我们将用户输入传递给name变量。
上述代码是安全的,因为我们使用了适当的函数将用户输入传递到模板上下文中。而在Pyratemp中容易受到SSTI影响的代码示例如下:
列表12:Pyratemp易受攻击的代码示例。
在这段代码中,我们没有使用正确的方法将用户输入引入引擎。在执行模板时(t()函数调用),我们直接将用户输入嵌入到Pyratemp代码中。这个小的疏忽意味着引擎会执行任何包含Pyratemp分隔符的用户输入。
正如我们在介绍这个模板引擎时所说,一个伪沙箱功能会检查我们传递给模板的代码。沙箱是通过一个名为EvalPseudoSandbox的类实现的,只允许引擎创建者认为安全的Python内置函数子集。此外,如果模板包含任何双下划线(例如__class__),则会阻止引擎执行。正如我们之前在Jinja2中看到的,我们可以使用Python的内部属性来构建RCE有效载荷,以调用如popen或system这样的函数。通过过滤包含双下划线的任何模板,这种有效载荷就不再起作用。据我们所知,没有办法从这个沙箱中逃脱。尽管如此,Web开发人员仍应小心不要将危险的对象传递给模板(例如,具有直接访问system或其他危险函数的对象)。
**8.2 Smarty:PHP沙箱**
Smarty [34] 是一个用PHP编写的模板引擎。它的重点在于简单性和安全性,语法主要使用单大括号({$variable})。
首先,我们可以从一个安全的示例开始。这个示例来自Smarty文档,分为两个文件:一个包含HTML标签的smarty源文件和两个模板变量($title_text和$body_html)。另一个文件是PHP源文件,它编译模板并设置变量的值。
我们要看的第一个文件是index.tpl文件,它是HTML与Smarty模板代码的混合:
列表13:Smarty模板代码示例。
上面的代码包含一些HTML标签和一些Smarty指令。例如,我们可以看到title标签包含一个由大括号分隔的Smarty指令,该指令显示变量$title_text并应用了转义参数。此操作将转义变量中的任何HTML语法。我们还可以看到一个Smarty注释,可以使用大括号和星号符号({* comment *})来插入。最后,我们有变量$body_html,它没有被转义,因为我们希望它包含一些HTML标签。
现在我们可以看到渲染这个Smarty模板的PHP代码:
列表14:Smarty安全代码示例。
上面的代码被简化为必要的部分。首先,我们创建一个新的Smarty对象来表示引擎。然后,我们可以使用assign函数将变量绑定到引擎中,并传递变量的名称和相应的值。在这种情况下,我们设置了之前看到的变量$title_text和$body_html。最后,我们可以渲染并显示模板,它将变成纯HTML(引擎会在调用此函数时替换变量),并将作为响应发送给用户。需要强调的是,上述示例使用单独的模板文件(index.tpl)代表了传统的模板渲染方法。在之前的和后续的示例中,为了简单起见,我们将使用模板字符串。然而,创建单独的模板文件是使用模板引擎最安全、最标准的方法。尽管如此,在某些情况下,Web开发人员可能会发现有必要使用模板字符串。在这种情况下,他们必须格外小心,以防止在模板中引入SSTI漏洞。
现在让我们来看一个不安全的模板使用示例;这次,我们只报告PHP代码以简化说明:
列表15:Smarty易受攻击的代码示例。
代码相对较短;与之前一样,我们必须创建我们的Smarty引擎对象,然后使用全局变量$_GET获取用户输入。最后,我们使用fetch函数渲染模板代码,将Smarty代码直接作为字符串,并将字符串“Hi: ”与用户输入连接起来。这种做法是不安全的,因为用户输入将被作为Smarty代码的一部分执行,使这个PHP应用程序容易受到SSTI的影响。
在Smarty中,实现RCE也相当容易,因为我们可以利用特定的指令来执行任意的PHP代码,这意味着我们也可以调用如system这样的函数在服务器上执行任意命令。以下是一个使用这种指令的示例:
列表16:使用特殊分隔符的Smarty RCE有效载荷。
{php}指令表示包含PHP代码的块的开始;在这种情况下,我们可以使用反引号包围我们想要执行的命令。它们是执行PHP中system命令的别名;然后,我们可以使用echo打印命令的输出。这种有效载荷并不是执行任意PHP代码的唯一方式。我们还可以滥用标准的Smarty语法来执行系统命令,如下例所示:
列表17:使用常规分隔符的Smarty RCE有效载荷。
在这种情况下,我们使用标准语法({})并简单地调用system函数,将要在服务器上执行的命令作为参数传递。
Smarty有一个可以设置为true或false的安全选项。默认情况下,该设置为false,意味着没有限制,我们刚才解释的情景是可能的。当我们将安全性设置为true时,就会有一些限制。我们可以在Smarty的文档中找到这些限制;其中最重要的是:
- php_handling设置自动设置为SMARTY(PHP_PASSTHRU),这意味着标签会原样显示。因此,没有任何内容会作为PHP代码执行,而这是默认值SMARTY(PHP_ALLOW)所允许的。
- 不允许使用{php} {/php}标签,从而消除了执行之前提到的有效载荷的可能性。
- 不允许在if语句和作为修饰符中使用PHP函数,除非在security_settings变量中指定了特定的函数。这禁止了在模板中直接调用系统函数。
- 模板和本地文件只能从secure_dir变量中指定的目录包含进来。
激活上述安全特性可以帮助防止RCE(远程代码执行),但仍可能产生问题。每当允许用户使用模板语法时,通常就会出现SSTI(服务器端脚本注入)风险。在适当的场景下使用这些安全特性意味着恶意用户无法利用漏洞来接管服务器。然而,在错误的场景下,攻击者仍然可能窃取敏感信息。正确的场景是我们希望允许用户使用模板引擎执行特定操作。错误的场景是程序员不谨慎地处理用户输入,因为他们知道已经采取了安全措施。传递给模板引擎的数据仍然可以被攻击者读取,并可能泄露敏感信息。此外,即使启用了安全标志,某些针对此模板引擎的沙箱绕过方法仍然允许RCE。虽然一些方法已经被修复,但未来可能会发现更多。
8.3 Dust:当漏洞允许RCE时
Dust [64]是一个异步模板引擎,在npm上的每周下载量超过17,000次。分隔符是单大括号({}),但在打开的大括号后可以使用特殊符号来调用特殊功能(例如,{@eq})。
现在我们可以通过一个安全代码示例来分析这个引擎:
**清单18. Dust安全代码示例**
在前两行中,我们导入了渲染模板所需的Dust模块。注意我们需要导入dustjs-helpers,因为我们需要使用这个库中的辅助函数来利用这个引擎。第三行,我们从HTTP请求中收集用户输入。第四行,我们编译模板;模板会安全地打印“Hi: ”,后面跟着通过name变量传递的用户输入。在compile函数中,我们还为模板设置了一个名称,这里是test。在最后几行中,我们加载、渲染并发送模板;render函数接受三个参数。第一个参数是模板的名称(我们之前已经设置了,这里是test)。第二个参数是在模板中需要使用的变量,这里是name,其中包含用户输入。第三个参数是一个函数,它要么抛出错误(err变量),要么生成包含模板的字符串(out变量)。如果这个函数正确运行,我们最终会在html变量中获得渲染后的模板。注意,这种模板渲染方式与其他模板不同。这种差异是因为Dust使用了异步函数;代码稍微长一些,但性能得到了提升。在最后一行,我们将生成的模板发送给客户端。
上述代码是安全的,因为它没有直接在模板代码中使用用户输入,而是通过render函数中的适当参数传递。而以下代码则没有安全地处理用户输入,因此容易受到SSTI的影响:
**清单19. Dust易受攻击的代码示例**
上述代码与安全代码的主要区别在于第四行和第六行。在第四行中,我们将用户输入直接连接到模板代码中,这就是为什么这段代码容易受到SSTI攻击的原因。在第六行,我们传递了一个空对象,而应该像在安全代码中那样传递用户输入。
以下有效载荷可以用来利用dustjs-helpers模块版本1.5.0及以下版本的SSTI漏洞来实现RCE。实际上,在这个版本之后的版本中,出于安全原因移除了if辅助函数,因为它允许评估任意JavaScript代码:
**清单20. 使用if辅助函数eval漏洞的Dust RCE有效载荷**
上述攻击表明,我们可以通过if语句注入eval。在eval内部,攻击者可以执行JavaScript代码来执行任意系统命令。这种内省式有效载荷旨在导入child_process模块并调用execSync函数。在这个例子中,我们制作的独特有效载荷不仅仅是执行ls,而是执行curl。原因是由于我们在if条件内部,eval的返回值被转换为布尔值,因此输出被忽略了。为了利用这个有效载荷实现RCE,攻击者可以打开反向shell或将命令输出发送到外部服务器。
8.4 Jinjava:利用Java内省功能实现RCE
Jinjava是一个基于Python的Jinja2的模板引擎。Jinjava的语法与我们看到的Jinja2非常相似(它使用{{ }}作为分隔符),漏洞也类似,只是编程语言不同。我们将看到这个模板引擎的开发者已经修复了这些问题以避免RCE。在深入探讨其易受攻击的方面之前,让我们先看一个安全使用的示例:
**清单21. Jinjava安全代码示例**
首先,我们使用getParameter函数获取用户输入。然后,我们实例化一个Jinjava对象并用它来渲染模板代码。接着,我们创建一个HashMap对象,其中包含模板引擎的上下文,并将需要的内部变量放入引擎上下文中。我们将用户输入绑定到一个名为name的模板变量上。最后,我们使用render函数解析Jinjava代码,该代码应该显示“Hi: ”,后面跟着用户输入。这个过程是安全的,因为用户输入不会被模板引擎直接解析,而只会在渲染模板时替换。现在让我们看一个不安全的使用示例:
**清单22. Jinjava易受攻击的代码示例**
这段代码与之前的类似;不同之处在于上下文是空的。我们没有将用户输入安全地绑定到模板变量上,而是直接将其连接到Jinjava代码中。这个错误可能导致SSTI漏洞,但在这个引擎中严重程度如何?我们能实现RCE吗?Jinjava的开发者付出了努力来避免RCE。此外,较新的JDK版本通过移除某些被用于攻击的功能帮助实现了这一目标。在详细解释变化之前,让我们先分析一下在旧版本的Jinjava和JDK上生效的有效载荷:
**清单23. Jinjava内省式有效载荷示例**
这个有效载荷并不像我们在Python的Jinja2中看到的那么简单,但原理是一样的:我们利用了面向对象编程的内省特性。我们从字符串'a'开始,调用getClass方法,这允许我们通过forName方法访问任何类实例。我们得到了ScriptEngineManager的实例,这是一个可以用来执行其他语言(例如JavaScript)脚本的类。eval方法是最终实现执行任意命令并实现RCE的关键。幸运的是,这个有效载荷在较新的Jinjava版本中不起作用;因为这是一个开源项目,有人在GitHub上提出了一个问题,指出如果getClass函数不可调用,就可以防止这个有效载荷生效。因此,开发者限制了这个函数,现在在模板代码中无法调用它。另外,如果我们使用较新的JDK版本(例如JDK 18),这个有效载荷也无法生效,因为ScriptEngineManager类默认没有JavaScript引擎,所以攻击者无法利用这个技巧实现RCE。
8.5 ERB:利用Ruby代码执行实现RCE
ERB是一个基于Ruby的模板引擎,其语法基于尖括号(<%= instruction %>)。正如我们在其他引擎中看到的,如果用户输入没有得到安全处理,就会发生SSTI。以下是这个模板引擎的安全使用示例:
**清单24. ERB安全代码示例**
在这个示例中,我们的第一步是定义一个Env类,作为组织要嵌入到模板中的数据的容器。这个类有一个名为name的参数。接下来,我们收集服务器接收到的用户输入(这里是username)。然后,我们通过调用Env.new方法创建一个Env类的实例,并将其赋值给名为scope的变量。为了完成设置,我们将scope对象的user属性设置为user变量中存储的值。下一步是编写模板,这由Tilt模块帮助完成。这个模块使我们能够从多种模板中选择。在这个特定示例中,我们访问erb模板并通过new函数实例化它,传递所需的参数。在这里,参数x代表希望作为模板渲染的字符串。最后,我们的最终指令是编写通过调用之前创建的模板变量返回的内容。值得注意的是,虽然Ruby使用的语法与其他编程语言不同,但底层机制是一致的:我们获取用户输入并将其绑定到一个变量上,然后传递给模板。一旦模板被渲染,网页就会显示变量的值,确保数据的安全展示。
以下是一个不安全的使用示例:
**清单25. ERB易受攻击的代码示例**
总体思路是一样的,但在这个例子中,我们没有为模板提供任何环境来展示用户输入;在这种情况下,我们直接在模板中渲染用户输入。在第一行,我们从请求参数中获取用户输入。然后,我们创建模板并传递用户输入进行渲染,这使得攻击者很容易注入模板语法并在输出中执行它。在这种情况下,攻击者可以很容易地执行SSTI,允许任意代码执行。ERB标签可以执行任意Ruby代码,因此我们可以简单地执行以下有效载荷:
**清单26. 使用直接代码执行的ERB RCE有效载荷**
在这个例子中,执行的命令是ls,但任何命令都可以执行。readlines函数允许攻击者获取命令输出。
9. 经验总结
在本节中,我们总结了分析得出的主要见解,强调了SSTI研究中仍然存在的陷阱:
(1) 针对SSTI的现有防御措施很少。我们已经展示了模板引擎在现实世界应用程序中的广泛使用以及SSTI漏洞的持久性。许多与SSTI相关的CVE(漏洞漏洞)强调了它们对安全性的重大影响。我们已经表明,SSTI的严重性既体现在高CVE评分上,也体现在漏洞赏金计划提供的丰厚奖励上。尽管如此,这个问题仍然存在,现有的工具和当前的缓解措施都不足以解决问题。
(2) 模板引擎中的RCE很常见,且防护不足。我们对34个模板引擎进行了深入分析,其中包括首次检查的9个引擎——其中8个允许RCE。通过这项分析,我们获得了宝贵的见解,识别出了四种不同类型的RCE(远程代码执行)攻击路径,并对四种可能的防御方法进行了分类。研究强调了需要使用自动化、跨语言的方法来确定模板引擎是否允许RCE攻击。在分析的34个模板引擎中,有30个存在RCE攻击路径,其中21个至今仍然存在这一问题,因此我们得出结论:RCE是一个普遍存在的问题。我们还全面讨论了为什么RCE漏洞在模板引擎中持续存在,以及最常见的防御措施——沙箱机制是如何被绕过的。
SSTI(指令集随机化)这一漏洞被低估了,针对该漏洞的防护研究也相对不足。我们发现,学术研究中唯一被提及的防御方法是指令集随机化,但在实际应用中很难实施。此外,关于这一主题的研究主要集中在SSTI和RCE的检测技术上,而缺乏创新性的预防方法。
**结论**
在本文中,我们从多个角度对模板引擎进行了深入研究。首先,我们解释了它们的基本工作原理,包括理论层面和实际应用。接着,我们探讨了与模板引擎使用相关的漏洞,特别是SSTI和RCE攻击路径。我们还详细分析了现有的工具和相关研究。我们的目标是引起人们对这种在现代Web应用中广泛使用的模板引擎中RCE问题的关注。通过展示SSTI的后果以及现有研究的局限性,我们希望激发未来针对模板引擎研究需求的相关工作。对现有和过去研究的分析表明,目前的研究主要集中在SSTI的利用、检测和绕过沙箱机制方面,而RCE问题仍然鲜有探索。我们认为,未来的研究应该聚焦于这一问题,寻找有效防御RCE攻击路径的方法。通过致力于缓解RCE攻击,SSTI漏洞的整体影响将会显著降低。