PHP代码审计——Day2-Twig
前置知识 - Twig
Twig是一个PHP模板引擎,它提供了一种更简洁和可扩展的方法来创建PHP应用程序的视图层。Twig模板引擎旨在将设计与业务逻辑分离,并为开发人员提供一种更加清晰和易于维护的方式来构建网页。
使用前提:在PHP项目中安装,然后引入Twig的自动加载文件
composer require twig/twig
require_once 'vendor/autoload.php';
基本配置示例:
$loader = new \Twig\Loader\FilesystemLoader('path/to/templates');
$twig = new \Twig\Environment($loader, [
'cache' => 'path/to/cache',
'debug' => true,
]);
使用FilesystemLoader
来加载模板文件。将path/to/templates
替换为你实际的模板目录。配置中的cache
参数指定Twig编译后的缓存目录,debug
参数设置为true可以使Twig在有错误时输出更详细的错误信息。
漏洞解析
// composer require "twig/twig"
require 'vendor/autoload.php';
class Template {
private $twig;
// 初始化 Twig 环境并加载模板
public function __construct() {
$indexTemplate = '<img ' .
'src="https://loremflickr.com/320/240">' .
'<a href="{{link|escape}}">Next slide »</a>';
// Default twig setup, simulate loading
// index.html file from disk
$loader = new Twig\Loader\ArrayLoader([
'index.html' => $indexTemplate
]);
$this->twig = new Twig\Environment($loader);
}
// 从 $_GET 中获取名为 nextSlide 的参数,并通过 filter_var 函数验证为一个有效的 URL,并返回该URL
public function getNexSlideUrl() {
$nextSlide = $_GET['nextSlide'];
return filter_var($nextSlide, FILTER_VALIDATE_URL);
}
// 渲染模板,将下一张幻灯片的链接作为参数传递给 Twig 模板引擎,然后输出渲染后的 HTML 内容
public function render() {
echo $this->twig->render(
'index.html',
['link' => $this->getNexSlideUrl()]
);
}
}
(new Template())->render();
考察点:xss漏洞
代码中使用了escape
和filter_var
两个过滤方法,但还是可以被绕过,程序使用 Twig 模板引擎定义的 escape 过滤器来过滤link,而实际上这里的 escape 过滤器,是用PHP内置函数 htmlspecialchars 来实现的
{{link|escape}}
:
这是twig自带的过滤器,在twig的文档中我们可以知道它是通过PHP的htmlspecialchars来实现的,他会将特殊字符转换为 HTML 实体。
htmlspecialchars
函数:(PHP 4, PHP 5, PHP 7) 将特殊字符转换为 HTML 实体
定义 :string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string$encoding = ini_get("default_charset") [, bool $double_encode = TRUE ]]] )
& (& 符号) =============== &
" (双引号) =============== "
' (单引号) =============== '
< (小于号) =============== <
> (大于号) =============== >
filter_var
: (PHP 5 >= 5.2.0, PHP 7) 使用特定的过滤器过滤一个变量
定义 :mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )
过滤函数,有一些常用的过滤器类型及对应的预定义变量:
- FILTER_VALIDATE_EMAIL:验证是否为有效的电子邮件地址。
- FILTER_VALIDATE_URL:验证是否为有效的URL。
- FILTER_VALIDATE_IP:验证是否为有效的IP地址。
- FILTER_SANITIZE_STRING:去除字符串中的 HTML 和 PHP 标记。
- FILTER_SANITIZE_NUMBER_INT:去除字符串中的所有字符,除了数字和符号。
绕过方法
使用JavaScript伪协议绕过
<a href="javascript:;">这个标签中的javascript是啥意思?</a>
// href 属性的值可以是任何有效文档的相对或绝对 URL,包括片段标识符和 JavaScript 代码段。
// 这里的href=”javascript:;”,其中javascript:是伪协议,它可以让我们通过一个链接来调用javascript函数。
// javascript:是表示在触发<a>默认动作时,执行一段JavaScript代码
demo测试
<?php
$url = filter_var($_GET['url'], FILTER_VALIDATE_URL);
var_dump($url);
$url = htmlspecialchars($url);
var_dump($url);
echo "<a href='$url'>Next slide~~</a>";
?>
构造payload:?url=javascript://comment%250aalert(1);
- 这里的
//
在JavaScript中表示单行注释,所以后面的内容均为注释 - 字符
%0a
为换行符,所以 alert 语句与注释符 // 就不在同一行,就能执行 - 这里我们要对
%
百分号编码成%25
,因为程序将浏览器发来的payload:javascript://comment%250aalert(1)
先解码成:javascript://comment%0aalert(1)
存储在变量$url
中,然后用户点击a标签链接就会触发 alert 函数。
修复建议
对于XSS漏洞,最好过滤关键词,将特殊字符进行HTML实体编码替换
参考文章
https://xz.aliyun.com/t/2457?time__1311=n4%2BxnieDqQqWwqCwx0v%2Bb%2BoitrGQkQetKz4D&alichlgref=https%3A%2F%2Fxz.aliyun.com%2Fu%2F10394%3Fpage%3D2
https://lanvnal.com/2020/02/19/rips-php-security-calendar-2017-xue-xi-ji-lu/#toc-heading-2