第26天:安全开发-PHP应用&模版引用&Smarty渲染&MVC模型&数据联动&RCE安全

为什么会有模板?

主要是用来标准化和简化网站页面的创建和管理的。它们就像预先设计好的蓝图或者模具,定义了网页的整体结构,布局,风格和功能区域。简单来说就是提前写好了html的样式,那些需要经常修改变化的地方使用后端语言进行传输。比如网页的标题,网页的内容,一个网站可以有很多网页难道每个网页都重新写html代码吗,所以这个时候就会使用模板来统一网页的风格。

当这个模板使用变量进行传输数据的时候,如果这个时候没有对这个变量进行管控或者对这个变量过滤不严谨,就会导致模板注入

这里使用php+html简单实现一下模板的操作:

new.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
include "./config.php";
$template=file_get_contents('new.html');
//将new.html 整个文件读入一个字符串中赋值给$template


$id=$_GET['id'] ?? '1';
$sql="select * from demo01 where id=$id";
$data=mysqli_query($con,$sql);
while ($row=mysqli_fetch_row($data)) {
$page_title=$row['1'];
$heading=$row['2'];
$subheading=$row['3'];
$content=$row['4'];
$item=$row['5'];
echo $content;
}

echo "$page_title<br>$page_title";


$template=str_replace('{page_title}',$page_title,$template);
//由于html页面中已经有了{page_title}类似这种,然后这里进行一个替换,将数据库中遍历的值替换成html网页中的值
$template=str_replace('{heading}',$heading,$template);
$template=str_replace('{subheading}',$subheading,$template);
$template=str_replace('{content}',$content,$template);
$template=str_replace('{$item}',$item,$template);
eval('?>' . $template);
//'?>' 是 PHP 的闭合标签,表示退出 PHP 模式(进入纯文本/HTML 模式)。这里的$template就是html,相当于进入template模板

?>

new.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{page_title}</title>
<style>
/* 一些样式 */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
header {
background-color: #4CAF50;
color: white;
text-align: center;
padding: 20px;
}
nav {
background-color: #f2f2f2;
display: flex;
justify-content: space-between;
padding: 10px;
}
nav a {
color: black;
text-decoration: none;
padding: 10px;
border-radius: 5px;
transition: background-color 0.3s ease;
}
nav a:hover {
background-color: #4CAF50;
color: white;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1 {
font-size: 36px;
font-weight: bold;
margin-bottom: 20px;
}
p {
font-size: 18px;
line-height: 1.5;
margin-bottom: 20px;
}
ul {
margin-left: 20px;
margin-bottom: 20px;
}
li {
margin-bottom: 10px;
}
</style>
</head>
<body>
<header>
<h1>{heading}</h1>
</header>
<nav>
<a href="#">首页</a>
<a href="#">新闻</a>
<a href="#">留言</a>
<a href="#">关于</a>
</nav>
<div class="container">
<h1>{subheading}</h1>
<p>{content}</p>

<img src="{$item}" width="1000" height="3000" >


</div>
</body>
</html>

<?php phpinfo();?>

总结:其实原理就是将写好的模板html,通过函数或者其他方式,写入php中,然后用一个变量$x进行接收,然后创建一个数据库,遍历这个数据库,然后将这个这个数据库中对应的值取出来,赋值给$x,这个赋值的时候需要用到str_replace函数,将原本html的内容替换成数据库中的内容。

数据库的结构

Snipaste_2025-06-04_21-28-59

展示:

Snipaste_2025-06-04_21-29-33

Snipaste_2025-06-04_21-29-39

zblog

Snipaste_2025-06-04_21-27-31

Snipaste_2025-06-04_21-34-50

这种cms中同样会有使用模板这个功能。

那么这样说明,如果一个网站在对模板进行管理的时候,如果有可控变量参数,那么就会出现:

Snipaste_2025-06-04_21-46-04

这里假设往这里插入一个php语句。

Snipaste_2025-06-04_21-46-36

刷新页面就会发现这里的phpinfo已经被执行了。这个就是代码执行。

这个时候你会发现自己的写的模板没有任何的安全过滤,漏洞百出,那么这个时候就可以调用一些第三方的模板。网上用的比较多的是:Smarty

Smarty

Smarty 是一个基于 PHP 的模板引擎,旨在将业务逻辑(PHP 代码)与表现层(HTML/CSS/JS)分离。它遵循 MVC 设计模式,由 Monte Ohrt 和 Andrei Zmievski 于 2001 年创建,是 PHP 生态中最老牌且广泛使用的模板引擎之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
// 引入 Smarty 类文件
require('smarty/libs/Smarty.class.php');

// 创建 Smarty 实例
$smarty = new Smarty;

// 设置 Smarty 相关属性
$smarty->template_dir = 'smarty/templates/'; // 设置模板文件的目录
// 指定 Smarty 模板文件所在的目录,这里使用了相对路径,可以根据实际情况修改
$smarty->compile_dir = 'smarty/templates_c/'; // 设置编译文件的目录
// 指定 Smarty 编译生成的模板文件所存放的目录,用于存放 Smarty 编译生成的 PHP 文件
$smarty->cache_dir = 'smarty/cache/'; // 设置缓存文件的目录
// 指定 Smarty 缓存文件的存放目录,用于存放 Smarty 缓存文件
$smarty->config_dir = 'smarty/configs/'; // 设置配置文件的目录
// 指定 Smarty 配置文件的存放目录,用于存放 Smarty 配置文件

// 赋值变量到模板中
$smarty->assign('title', '欢迎使用 Smarty'); // 将变量 'title' 赋值为 '欢迎使用 Smarty'

// 显示模板
$smarty->display('index.tpl'); // 使用 'index.tpl' 模板文件进行显示
?>

Snipaste_2025-06-04_22-21-53

Snipaste_2025-06-04_22-23-12

Snipaste_2025-06-04_22-23-17

如果没有templates这个目录,可以手动创建,然后这个index.tpl注意这个后缀。

Snipaste_2025-06-04_22-23-24

然后进行访问:

Snipaste_2025-06-04_22-27-12

现在我们还是像之前一样在html中写入一个phpinfo看看是否会进行解析,我们之前自己写的模板,是可以正常解析的。

Snipaste_2025-06-04_22-30-40

Snipaste_2025-06-04_22-30-56

发现还是没有解析。

Snipaste_2025-06-04_22-31-25

这个操作就说明,正常来说自己写的话,没有安全过滤,需要自己花时间花精力去写安全过滤,那么用了第三方模板的时候就可以发现这里就算我正常的去写一个正常的模板,我去写phpinfo直接就是解析不了,说明代码自带过滤。那么第三方的模板就绝对安全吗,如果你有实力可以自己代码审计这个模板,一般来说只能去找有没有历史漏洞。

Smarty <= 3.1.32 远程代码执行(CVE-2017-1000480)

使用小于这个要求的模板:

Snipaste_2025-06-04_22-36-36

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

define('SMARTY_ROOT_DIR', str_replace('\\', '/', __DIR__));

define('SMARTY_COMPILE_DIR', SMARTY_ROOT_DIR.'/smarty3/templates_c');

define('SMARTY_CACHE_DIR', SMARTY_ROOT_DIR.'/smarty3/cache');

include_once(SMARTY_ROOT_DIR . '/smarty3/libs/Smarty.class.php');

class testSmarty extends Smarty_Resource_Custom
{
protected function fetch($name, &$source, &$mtime)
{
$template = "CVE-2017-1000480 smarty PHP code injection";
$source = $template;
$mtime = time();
}
}

$smarty = new Smarty();
$smarty->setCacheDir(SMARTY_CACHE_DIR);
$smarty->setCompileDir(SMARTY_COMPILE_DIR);
$smarty->registerResource('test', new testSmarty);
$smarty->display('test:'.$_GET['x']);
?>

当我们触发这个链接:http://localhost:63342/new.php/index1.php?x=*/phpinfo();//

Snipaste_2025-06-04_22-37-45

发现可以正常执行。

总结

1
2
3
4
5
6
7
8
9
10
11
1、自己写的模板-->直接RCE安全问题
2、第三方smarty模板-->没有直接的安全RCE安全问题
3、但是有历史漏洞

一个网站的漏洞:
1、本身的代码漏洞
2、第三方插件的漏洞(ueditor)
3、第三方模板(smarty)
4、第三方组件(shiro fastjson等)

也就是说一个网站不仅仅是看自身代码的安全,同时也要看是否使用了第三方插件,模板,组件等等,如果这些第三方出现了问题,不管你的网站的代码写的多好,这里同样会被影响。