模板引擎是什么?我们为什么要用到它?
Template processor (template engine or template parser),模板处理器或者叫做模板引擎,Wikipedia给的定义是一个将数据和模板组合形成文档的软件。
模板引擎的输入一般是用template language 模板语言写的 模板, 以及需要替换模板中占位内容的 真实数据, 经过模板引擎的解析之后,最后的输出就是数据与模板结合的文档,网页或者源代码。
我们可以拿Powerpoint的Design Ideas功能来做一个例子,我认为这本质上也是一种模版引擎,我们只需要输入文字和每一张slide的内容,Powerpoint自动会生成排版来呈现这些数据;
我们在Web开发中所用到的模板应该具体到 Web template system 网页模板系统,这是运用在网页开发中的模板引擎,网页开发者可以利用其将 数据与页面分开
, 通过将静态的页面与动态获取的数据进行结合生成新的HTML文件(根据浏览器发出的请求参数,例如一次搜索),提高了开发效率,也使代码重用变得更加容易
模板引擎的主要思想就是要将 用户界面
与 业务数据
的内容分离,比如在我们提到的Powerpoint的例子中,用户只需要思考每个页面上需要展示什么数据即可,排版布局就交给了软件本身。
同样的,在网页模板系统中,我们可以说将前后端的工作进一步分离开来,前端可以可以更专注于页面布局,业务逻辑的设计上,而后端可以专注于数据的处理;
在传统的网页开发中,为了展示数据,需要如下将JS代码与HTML拼接在一起,存在的问题就是拼接很容易出错,同时一旦出错或者需要修改,那么就需要前后端一起进行修改,代码不易维护。
for (var i = 0; i < result.length; i++) {
html += '<!DOCTYPE html>\
<html lang="en">\
<head>\
<meta charset="UTF-8">\
<title>'+ title +'</title>\
</head>\
<body>\
<h1 οnclick="sayHi('+name+')">你好,'+name+' 我今年 '+age+'岁</h1>\
<ul>\
<li title="'+hobbies[0]+'">'+hobbies[0]+'</li>\
</ul>\
</body>\
</html>';
}
使用模板引擎就可以很好的解决这个问题,前端开发者可以专注于开发模版,即网页的布局与框架,同时留下数据部分,等待填充:
template.html
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<div>Hello, </div>
{{ name }}
</body>
</html>
twig_render.php
<?php
require_once('../../../../PHP_Resources/vendor/autoload.php');
$loader = new \Twig\Loader\FilesystemLoader('./');
$twig = new \Twig\Environment($loader);
echo $twig->render('template.html', ['name' => 'John']);
?>
接着访问twig_render.php即可:
包括现在比较成熟的框架例如VUE等,我觉得也是利用了模板引擎的思想,尽量使前后端的工作分离,同时又方便了协作
SSTI 模板注入
SSTI 模板注入是一种Web注入漏洞,其产生的原因本质上是对于用户的输入没有做全面的审核,导致了 模板
拼接了用户输入的 模板语言
代码,进而被模板引擎所执行,从而引发敏感信息泄露,远程代码执行等问题
Web应用可以使用模板系统来动态的显示一些信息,比如下面的例子就是可以动态的显示用户的姓
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
一切看起来都没有问题
但是如果我们允许用户进行输入,就会引发风险,我们这里不再是简单的使用一个值了,而是引入了一个用户输入 $_GET['custom_email']
进行拼接
$output = $twig->render($_GET['custom_email'], array("first_name" => $user.first_name) );
custom_email={{7*7}}
49
custom_email={{self}}
Object of class __TwigTemplate_7ae62e582f8a35e5ea6cc639800ecf15b96c0d6f78db3538221c1145580ca4a5 could not be converted to string
如果是在黑盒的条件下,我们很容易测试出来XSS,但是这往往就会让我们忽略了模板引擎的存在,并且与XSS不同的是,模板注入可以直接对服务器造成危害;
模板注入的方法
我们根据 James Kettle
所提供的模板注入流程来进行介绍:
Detect 检测
第一步自然就是检测该页面是否存在模版注入,我们可以通过两种不同的用户输入来进行判断:
- Plaintext context 纯文本的上下文:
大部分模板语言都提供纯文本的输入,可以直接看到反馈的结果;
smarty=Hello {user.name}
Hello user1
freemarker=Hello ${username}
Hello newuser
any=<b>Hello</b>
<b>Hello<b>
- Code context 代码上下文
我们通过输入一些代码片段来观察返回的内容是否显示了我们想要得到的拼接结果:
personal_greeting=username
Hello user01
personal_greeting=username<tag>
Hello
personal_greeting=username}}<tag>
Hello user01 <tag>
Identify
在成功检测到了模板引擎的存在之后,下一步就是要确定哪一种引擎被使用了,我们可以根据不同的语法来进行输入并观察返回的结果:
James Kettle 为我们提供了这张决策树,绿色表示成功识别,红色代表失败需要进一步尝试:
例如当我们在尝试 {{7 * 7'}}
时,如果返回49那么就是Twig,如果是 7777777 就是 Jinja2
Exploit
接着我们就要尝试利用这些模板引擎所存在的漏洞:
Read
在我们识别成功之后,下一步就是非常关键的阅读文档阶段,只有了解了模板语言之后我们才能找到利用的方法:
- 基础的语法
- 查看有关安全的相关章节,我们可以假设一些开发者并没有关注这些容易出现问题的点
- 内置的方法,函数,过滤器以及变量,比如查看内置的函数的源码,是否有出现危险函数的使用,从而进一步引发远程代码执行?
- 查看插件或者扩展,特别是那些默认开启的内容
Explore
下一步就是关注模板引擎运行环境,通常这些模板引擎会提供一些全局变量或者实例对象,这些实例中往往就存在一些敏感信息或者危险的函数可以被调用:
如果没有这样的内置对象可以为我们所用,也可以通过fuzz寻找可能的变量名,比如SecLists
Attack
在经过以上两步的信息获取之后,最后就是要用所有可以利用的资源来组合进行攻击,尝试造成一些常见的攻击,比如非法对象创建,敏感信息泄露,远程文件包含,webshell,提权等;