构建网页最舒适的方法之一是使用服务器端模板。通过此类模板,您可以创建HTML页面,其中包含可以动态填充和修改的特殊元素。它们对于设计人员来说很容易理解,对开发人员来说很容易维护。有许多用于不同服务器端语言和环境的服务器端模板引擎。其中之一是 胸腺 ,可与Java一起使用。

服务器端模板注入(SSTI)是使攻击者可以将代码注入此类服务器端模板中的漏洞。简单来说,攻击者可以引入服务器端模板实际处理的代码。这可能导致 远程执行代码(RCE),这是一个非常严重的漏洞。在许多情况下,此类RCE发生在模板引擎提供的沙箱环境中,但是很多时候有可能逃脱此沙箱,这可能使攻击者甚至完全控制Web服务器。

SSTI最初是由 詹姆斯·凯特尔 and later by 埃米利奥·品纳(Emilio Pinna)。但是,这些作者都没有在他们的SSTI研究中包括Thymeleaf。让我们看看此模板引擎中存在哪些RCE机会。

胸腺 简介

胸腺 是用于Java的现代服务器端模板引擎,基于XML / XHTML / HTML5语法。该引擎的核心优势之一是 自然的模板。这意味着Thymeleaf HTML模板的外观和工作方式类似于HTML。这主要是通过在HTML标签中使用其他属性来实现的。这是一个官方示例:

<table>
  <thead>
    <tr>
      <th th:text="#{msgs.headers.name}"> 名称 </th>
      <th th:text="#{msgs.headers.price}">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr th:each="prod: ${allProducts}">
      <td th:text="${prod.name}">Oranges</td>
      <td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
    </tr>
  </tbody>
</table>

如果使用浏览器使用此代码打开页面,将看到一个已填充的表格,并且所有Thymeleaf特定的属性都将被跳过。但是,当Thymeleaf处理此模板时,它将标签文本替换为传递给模板的值。

黑客窃贼

要在Thymeleaf中尝试SSTI,我们首先必须理解出现在Thymeleaf属性中的表达式。 Thymeleaf表达式可以具有以下类型:

  • ${...}:变量表达式–实际上,它们是OGNL或Spring EL表达式。
  • *{...}:选择表达式–与变量表达式类似,但用于特定目的。
  • #{...}:消息(i18n)表达式–用于国际化。
  • @{...}:链接(URL)表达式–用于在应用程序中设置正确的URL /路径。
  • ~{...}:片段表达式–它们使您可以重用模板的某些部分。

尝试SSTI的最重要的表达式类型是第一个:变量表达式。如果Web应用程序基于Spring,则Thymeleaf使用Spring EL。如果不是,则Thymeleaf使用OGNL。

SSTI的典型测试表达式为 ${7*7}。此表达式在Thymeleaf中也适用。如果要实现远程代码执行,可以使用以下测试表达式之一:

  • SpringEL: ${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL:  ${#rt = @[email protected](),#rt.exec("calc")}

但是,如前所述,表达式仅在特殊的Thymeleaf属性中起作用。如果需要在模板的其他位置使用表达式,Thymeleaf支持 表达式内联。要使用此功能,必须在其中放置一个表达式 [[...]] or [(...)] (根据您是否需要转义特殊符号来选择一个或另一个)。因此,将为Thymeleaf提供一个简单的SSTI检测负载 [[${7*7}]].

但是,上述检测有效负载可能起作用的可能性非常低。当在代码中动态生成模板时,通常会发生SSTI漏洞。默认情况下,Thymeleaf不允许使用这种动态生成的模板,因此必须提前创建所有模板。因此,如果开发人员想要从字符串创建模板 在飞行中,他们将需要创建自己的TemplateResolver。这是可能的,但很少发生。

危险特征

如果我们深入研究Thymeleaf模板引擎的文档,将会发现一个有趣的功能,称为 表达式预处理. Expressions placed between double underscores (__...__) are preprocessed and the result of the preprocessing is used as part of the expression during regular processing. Here is an official example from Thymeleaf documentation:

#{selection.__${sel.code}__}

百里香首先预处理 ${sel.code}。然后,它使用结果(在此示例中,它是一个存储值  所有 ) as part of a real expression evaluated later (#{selection.ALL}).

此功能引入了SSTI漏洞的主要潜力。如果攻击者可以控制预处理值的内容,则他们可以执行任意表达式。更准确地说,这是一个双重评估漏洞,但是使用黑盒方法很难识别。

胸腺SSTI的真实例子

宠物诊所  是基于Spring框架的官方演示应用程序。它使用Thymeleaf作为模板引擎。

此应用程序中的大多数模板都重复使用了 layout.html 模板,其中包含导航栏。它有一个 特殊片段(函数),它生成菜单.

<li th:fragment="menuItem (path,active,title,glyph,text)" class="active" th:class="${active==menu ? 'active' : ''}">
      <a th:href="@{__${path}__}" th:title=" $ {title} ">

如您所见,应用程序进行了预处理 ${path}, which is then is used to set a correct link (@{}). However, this value comes from other parts of the template:

<li th:replace="::menuItem ('/owners/find','owners','find owners','search','Find owners')">

不幸的是,所有参数都是静态的,攻击者无法控制。

但是,如果我们尝试访问不存在的路由,则应用程序将返回 error.html 模板,它也重用了 layout.html。如果发生异常(访问不存在的路由是异常),Spring会自动将变量添加到当前上下文(模型属性)。这些变量之一是  路径  (others include  时间戳记 跟踪 信息 , 和更多)。

的   路径  变量是当前请求的URL的路径部分(无URL解码)。更重要的是,此路径用于 menuItem fragment. Therefore, __${path}__ 预处理请求中的路径。攻击者可以控制此路径以实现SSTI,并因此获得RCE。

作为一个简单的测试,我们可以向发送请求 http:// petclinic /(7 * 7) and get 49 as the response.

但是,尽管有这种效果,但是当应用程序在Tomcat上运行时,在这种情况下我们仍然找不到实现RCE的方法。这是因为您需要使用Spring EL,所以您需要使用 ${}。但是,Tomcat不允许 { and } 路径中没有URL编码的字符。而且我们不能使用编码,因为 ${path} 返回路径而不解码。为了证明这些假设,我们在Jetty上运行了PetClinic而不是Tomcat,并获得了RCE,因为Jetty并不限制使用 { and } 路径中的字符:

http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})

我们不得不使用 ( and ) 字符,因为经过预处理 @{} 表达式接收以开头的字符串 / (for example, /${7*7}),因此该表达式不会被视为表达式。的 @{} 表达式允许您通过将参数放在括号中来向URL添加参数。我们可以滥用此功能来清除上下文并执行我们的表达式。

结论

服务器端模板注入比看起来的问题要严重得多,因为服务器端模板的使用越来越频繁。有许多这样的模板引擎,其中许多仍未被利用,但是如果滥用,可能会引入SSTI漏洞。还有很长的路要走 ${7*7} 达到RCE,但正如您所看到的,在许多情况下,这是有可能的。

作为安全研究人员,我们总是很有趣地看到复杂技术如何相互冲突和相互影响以及还有多少尚待探索。

资源: //www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/