[GFCTF 2021]Baby_Web的CVE-2021-41773

知识点:

CVE-2021-41773是Apache HTTP Server 2.4.49版本的目录路劲穿越漏洞。

原理如下:

                      

简单来说: 检测路径中是否存在%字符,并且如果%后面的两个字符是十六进制字符,就会对后面的两个字符进行url解码转化,如路径中有%2e./,则会解码为../,转换后判断是否存在../,加入路径中使用.%2e/则能绕过,导致目录穿越漏洞,因为当遍历到第一个.时,后面两个字符是%2并不是./,就不会被处理,绕过检测。

arrary_merge()函数:array_merge() 函数用于把一个或多个数组合并为一个数组,如果两个或更多个数组元素有相同的键名,则最后的元素会覆盖其他元素。

extract():extract(array,extract_rules,prefix)
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量,该函数返回成功设置的变量数目。

如:

<?php
$code='';
$arg='';
$flag=array('code'=>'a','arg'=>'b');
extract($flag);
print(extract($flag));
printf($code);
print($arg)
?>


运行结果:2ab

        

<?php
$a1=array("a"=>"a","b"=>"b");
$a2=array("c"=>"c","b"=>"d");
print_r(array_merge($a1,$a2));
?>

输出:
    Array
(
    [a] => a
    [b] => d
    [c] => c
)

题目详解:

             

1,进入题目:

                        

很明显需要使用路径漏洞进行穿越,其实使用dirsearch扫一遍也能扫出来。

 

利用BP进行抓包修改:

         

 

读取到源代码:

<h1>Welcome To GFCTF 12th!!</h1>
<?php
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);

?>

可以使用GET传入参数filename并且类Temp接收POST数据传送过来的值。 

可以看到还有一个Class.php,继续进行源代码读取:

源代码:

        


<?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;
    }
}

访问代码中/template/admin路径:

对源代码进行分析:

        

  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);
    }

对display传进来的的值进行extract后进入getTempName(),然后文件包含$this->template,如果$dir==admin,拼接/template/admin/.$template并且进行包含。

继续分析下面源代码:

数组定义的流程:


        $params = explode(' ', $_params);  #利用空格分解$_params变量为数组
        if (in_array($params[0], ['list','function'])) {  #$params[0]是为在list或function
            $params[0] = 'action='.$params[0];
        }
        foreach ($params as $t) {
            $var = substr($t, 0, strpos($t, '='));  #取$t的等号前面的值
            $val = substr($t, strpos($t, '=') + 1); #取$t的等号后面的值
            if (!$var) {
                continue;
            }
            if (isset($system[$var])) {        
                $system[$var] = $val;
            } else {
                $param[$var] = $val; 
            }
        }
        // action
        switch ($system['action']) {   #对action进行分情况处理

            case 'function':

                if (!isset($param['name'])) {   #一定要存在name变量的key
                    return  'hacker!!';
                } elseif (!function_exists($param['name'])) {   #并且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);
        }

很明显需要利用call_user_func()进行函数的回调,那么就调用listdata的数组定义,要调用listdata就得把/template/admin/index.html包含进来,为mod传值,action的值为function,可以传入name=phpinfo。这样就会调用listdata函数,并且由于system中不存在system[name]$,所以param[name]=phpinfo,执行了phpinfo()函数。

payload为:

        

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M03-Aiwin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值