原文链接 https://www.blackhat.com/docs/us-15/materials/us-15-Kettle-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-wp.pdf

0x00 科普


要讨论模板引擎的攻击,先来了解下什么是模板引擎

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。(百度百科) 说白了,就是使得后端数据转换成前端html以及样式的中间应用件。

0x01 摘要


模板引擎(Template engines)在现代web应用被广泛使用,它们被用来动态地显示数据。然而,不安全地将用户输入嵌入到模板中,会导致一系列针对服务器的注入攻击。然而,这种漏洞很容易被误当做Xss对待。然而,针对模板的注入其实可以攻击服务器,往往还能够RCE(远程命令执行),触发任意对象构造,任意文件读写,远程文件包含,信息泄露,和提权。

模板注入的成因有两种:

一:开发人员的错误,
二:现有框架为了提供丰富功能而导致暴露过多函数交由用户操纵。

本文为上述攻击提供了一套方法论,从漏洞探测到利用漏洞,并且我将实例讲解两个被企业广泛使用的模板是如何被挖掘出模板注入漏洞。

0x02 介绍


当今web应用频繁地使用模板系统,诸如Twig,FreeMarket等。当用户输入嵌入在一个很没有安全性的模板时,模板注射就可能发生。

现在我们假设调用了Twig框架用来显示欢迎页面,如果直接把名字传递给模板,不会出现错误 就像这样

#!php
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );

然而,如果内嵌的名字是由用户输入得来的,问题就会出现

#!php
$output = $twig->render($_GET['custom_email'], array("first_name" => $user.first_name) );

在这个例子中,用户能够控制$_GET['custom_email'],这时,很多安全审计人员可能就会把它划归为一个反射型的Xss,然而,Xss只是表象,只需要改变下利用方式,漏洞其实能够威胁到服务器,然而往往这点会被忽略

输入:custom_email={{7*7}} 
输出:49


输入:custom_email={{self}}
输出:Object of class __TwigTemplate_7ae62e582f8a35e5ea6cc639800ecf15b96c0d6f78db3538221c1145580ca4a5 could not be converted to string

可以看到这里实际上存在一个远程代码执行(沙箱模式),针对不同的框架,利用不同的语法,绕过沙箱执行任意代码是我们的目标。

可能说到这里,有些人还是不能了理解模板注入以及注入发生的场景。我打了一个这样不恰当的比方来解释这个攻击背景: markdown相信很多人都用过,它支持各种标识符,让我们能够编辑格式,插入图片,模板注射就是利用我们的markdown的语法,在我们能够提交markdown文章的网站(drops.wooyun)实施攻击。

只是说,现在的一些web模板框架更加成熟,其后端语言(php/java)能造成比markdown语言更大的危害,比如说攻击服务端。

0x03 模板注射之方法论


这里给出了寻找模板注射的流程图。

enter image description here

漏洞检测:

漏洞可能出现在两种环境

一、可控内容为文本

smarty框架下

输入:Hello {user.name} 
输出:Hello user1

freemarker框架下

输入:Hello ${username}
输出:Hello newuser

在这种情况下,模糊测试框架的XSS探测结果可以作为发现漏洞的线索,由于黑盒审计攻击的payload往往是纯html或js的,这些地方很容易被误判成XSS(事实上也是一个xss),要想判别是否是一个模板注入,我们需要利用特定的框架语法来完成漏洞检测

smarty=Hello ${7*7} 
Hello 49


freemarker=Hello ${7*7}
Hello 49

二、可控内容为代码主体

用户输入有时候用来构成模板主体,比如说是一个变量名

输入:personal_greeting=username
输出:Hello user01

那么这种情况更容易遗漏,因为此时的自动扫描工具检测到这里时,payload根本不会让模板框架返回一个有效的回显,你必须遵循特定模板的语法,才能看到页面的变化,比如这里我们必须先用}}提前闭合模板代码段,才能插入任意一个html的标签

输入:personal_greeting=username<tag> 
输出:Hello


输入:personal_greeting=username}}<tag> 
输出:Hello user01 <tag>

模板识别

在侦测到注射漏洞之后,我们自然要分析对应的是哪一类模板,方便对症下药

enter image description here

上图描述了测试流程的树形结构,分支从左到右对应检测顺序,绿色箭头表示能够返回预期的结果,也就说明此时存在漏洞所使用的模板框架就是箭头指向的框架。

0x04 利用漏洞


阅读

利用漏洞之前,我们首先应该研究清楚四项内容,我们需要做的是

一:仔细阅读你要攻击的框架的用户手册,因为所有的攻击都是基于框架提供的语法而来的。

二:阅读框架内建的方法,函数,过滤器和变量清单,以及插件的清单。正是利用它们,才能发起模板注入。

三:重点关注‘安全考虑’,这些框架开发者提醒用户的安全风险,很可能就是我们漏洞利用的突破口。

四:研究插件/扩展清单,其中的有一些可能是默认开启的。

探索

当官方手册研究完毕之后,下一件要做的事情就是落实到某个特定的环境(某个目标站)中你所能控制的。你可以通过fuzz去发现模板引擎提供的对象,有些是模板默认提供的,有些是开发者为了特定的需求而开放的。特别要提到的是,很多引擎应用了self对象,而这个对象里面包含了域内的所有。

如果没有内建的self对象,你就只能通过爆破来得到变量了。我用爬虫爬到了整个Github里面所有有关php项目的POST/GET方法的变量名,这个工具发布在了fuzzDB和Burp Intruder wordlist collection。

攻击

这时候,你应该对攻击面有一个明确的目标了,现在你也可以用你传统的安全审计技术结合你对这个框架的探测,来审计每个函数。

0x05漏洞范例


往往,你只需要细读开发文档三十秒就足以找到一个RCE,举个例子。攻击没有沙箱隔离的“smarty”框架只需要这样

{php}echo `id`;{/php}

Mako框架也能很简单地RCE

<%
import os x=os.popen('id').read() %>
${x}

然而,许多模板引擎总会阻碍应用将自己的代码注入到模板里面去,引擎们往往会限制执行任意代码,有一些甚至采用沙箱技术来处理不受信任的输入。

0x06写在后面


本文摘自2015BH大会paper,转述,翻译部分内容,目的是帮助大家理解原理,有了原理的了解,大家就能很快地上手,去看原文作者提供的真实案例了。这也是花这么多时间翻译,总结这篇文章的目的。