060-WEB攻防-PHP反序列化&POP链构造&魔术方法流程&漏洞触发条件&属性修改

知识点:
1、PHP-反序列化-应用&识别&函数
2、PHP-反序列化-魔术方法&触发规则
3、PHP-反序列化-联合漏洞&POP链构造

在实战情况下,是不需要知道这些具体分析的,都是利用工具去扫一些框架爆出的反序列话漏洞直接利用即可。
学这些具体分析就是为了以后往漏洞挖掘方向发展或者打CTF比赛及面试会被问

1、PHP-DEMO1-序列化和反序列化

为什么会产生序列化?

为了解决开发中数据传输和数据解析的一个情况(类似于要发送一个椅子快递,不可能整个椅子打包发送,这是非常不方便的,所以就要对椅子进行序列化处理,让椅子分成很多部分在一起打包发送,到目的后重新组装,也就是反序列化处理)

什么是反序列化操作? - 类型转换

  • PHP & JavaEE & Python(见图)

63b7809a3530f569c350be7fc77ccc4e

580444fb1a4681b61529ce7d97299718

序列化:对象转换为数组或字符串等格式

序列化(Serialization)
把“复杂的数据结构(如数组、对象)”转换成一个字符串,方便你:

  • 存进文件
  • 放进数据库
  • 在网络上传输(如 Cookie、Session、API)

不能直接把“数组”或“对象”这样的复杂数据,塞进一个只支持字符串的地方(比如 Cookie)。

为什么不能直接把数组或对象塞进只支持字符串的地方?

  1. 本质问题:类型不兼容

PHP 的数组、对象是内存中的复杂结构,比如:

  • 数组有键名、键值、可能还嵌套其他数组
  • 对象有属性、方法、类名等元信息

而像 Cookie、数据库字段、URL 参数,这些地方只能存储字符串类型(即文本)。

所以你不能直接把这样的“结构”放进去,比如:

1
setcookie("user", ['name' => 'Tom']); // ❌ 错误!数组不能直接当 cookie 值

image-20250527193851523

反序列化(Unserialization)
是序列化的逆操作——把字符串还原成原来的数据结构

image-20250527194236223

serialize() //将对象转换成一个字符串
unserialize() //将字符串还原成一个对象

2、PHP-DEMO2-魔术方法触发规则

常见的php魔术方法

b89050ab42fb6cfcb31c7288a7e815cc

__construct():

  • //当对象new的时候会自动调用
  • 当new Test实例化对象 触发魔术方法_construct() 输出__construct()初始化
  • unset() 是 PHP 的一个语言结构,用于销毁变量,也就是说,它会让一个变量“消失”。

image-20250527195651058

__destruct()

  • //当对象被销毁时会被自动调用
  • __destruct() 是对象销毁时自动触发的魔术方法。你没有主动销毁对象 $test,但当脚本结束时 PHP 自动回收内存,系统帮你调用了 __destruct(),所以会输出那一行。

image-20250527195933702

__sleep()

  • : //serialize()序列化执行时被自动调用

image-20250527200833741

image-20250527200951800

__wakeup()

  • //unserialize()反序列化时会被自动调用

  • 这个和__sleep()截然相反

image-20250527201342084

image-20250527201407419

__invoke()

  • : //把对象当作函数调用时触发

image-20250527201626664

__toString()

  • : //把对象当作字符串使用时触发

原因详解:为什么 __toString() 会被自动调用?

  • \1. echo / 字符串上下文要求是字符串类型
  • 在 PHP 中,echo 是一个语言结构,它只能输出字符串。所以当你:echo $a
  • 其中 $a 是一个对象,PHP 必须把它转换成字符串,否则就会报错。

image-20250527201721403

__call():

  • //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。

image-20250527202336593

image-20250527202430152

__get()

  • 读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数

image-20250527202656204

image-20250527202952109

__ set()

  • 魔术方法 设置一个对象的属性时, 若属性存在,则直接赋值;若不存在,则会调用__set函数。

  • __set():设置对象不存在的属性或无法访问(私有)的属性时调用

  • __set($name, $value)
     * 用来为私有成员属性设置的值
     * 第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值,没有返回值。
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    ![image-20250527203600241](/img/image-20250527203600241.png)

    ![image-20250527203703407](/img/image-20250527203703407.png)

    ### __isset()

    * __检测对象的某个属性是否存在时执行此函数。当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用
    * name没被调用的原因是因为name是私有属性

    <img src="/img/image-20250527204233957.png" alt="image-20250527204233957" style="zoom:50%;" />

    ![image-20250527204411525](/img/image-20250527204411525.png)

    * 这里因为name是私有属性所以不可访问 并且不可访问属性还使用了isset()所以调用了__isset()魔术方法显示123



    * **为什么echo empty($person->sex) 为1**

    原因是:**重写了 `__isset()` 魔术方法** PHP 做了下面这些步骤:

    1. 检查 `$person->sex` 能不能访问;
    2. `sex` 是公共属性,所以可以访问,不走 `__isset()`;
    3. 但出于一致性,PHP 会**调用 `__isset()` 方法**来判断属性是否存在;
    4. 你的 `__isset()` 方法返回:

    return isset($this->$content);