反序列化

dawn_r1sing Lv3

序列化与反序列化

基本内容

目的:解决 PHP 对象传递的问题,方便数据的传输与储存

因为 PHP 文件在执行结束以后就会将对象销毁,那么如果下次有一个页面恰好要用到刚刚销毁的对象就会束手无策,总不能你永远不让它销毁,等着你吧,于是人们就想出了一种能长久保存对象的方法,这就是 PHP 的序列化,那当我们下次要用的时候只要反序列化一下就 ok 啦


魔术方法

魔术方法的调用是自动进行的,不需要人工干预

因此只要魔法方法中出现了一些我们能利用的函数,我们就能通过反序列化中对其对象属性的操控来实现对这些函数的操控,进而达到我们发动攻击的目的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__construct()		//创建对象时自动调用

__wakeup() //对象重新醒来,即由二进制串重新组成一个对象的时候(在一个对象被反序列化时调用)

__destruct() //当某个对象成为垃圾或者当对象被显式销毁时执行
/*
1、显式销毁,当对象没有被引用时就会被销毁,所以我们可以unset或为其赋值NULL
2、隐式销毁,PHP是脚本语言,在代码执行完最后一行时,所有申请的内存都要释放掉
*/

__get() //用于从不可访问的属性读取数据

__set() //用于将数据写入不可访问的属性

__unset() //使用不可访问的属性

__call() //在对象中调用一个不可访问方法

调用(清晰可见!!!):

  1. 先创建新对象,调用__construct()

  2. 执行unserialize()时调用__wakeup()

  3. 反序列化之后调用__destruct()

    1
    2
    3
    //但当程序抛出异常时,destruct方法不会执行,也就无法实现文件的写入:
    unserialize($_GET[0]);
    throw new Error("那么就从这里开始起航吧");
  4. 程序结束时调用__destruct()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
其他一些魔术方法调用举例:
$a = new test('test.txt', 'data');
# __construct() 被调用
# __sleep() 被调用

# __set() 被调用(此时给类中并没有定义过的类属性进行赋值触发了set方法)
$a->var = 1;

# __get() 被调用
echo $a->var;

# __isset() 被调用
var_dump(isset($a->var));

# __unset() 被调用
unset($a->var);

var_dump(isset($a->var));

echo "\n";

#__destruct() 被调用

属性

类中变量一样的东西(不负责任的讲

例如

1
2
3
4
5
6
7
8
9
10
11
class K0rz3n {
private $test;
public $K0rz3n = "i am K0rz3n";
function __construct() {
$this->test = new L();
}

function __destruct() {
$this->test->action(); //test指类中的test属性
}
}

test、K0rz3n就是K0rz3n类中的属性

private-私有变量

private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。

对于private变量,我们需要在类名和字段名前面加上\0的前缀(指 ASCII码为0的空字符)

但注意:python传入加 \0 ,浏览器直接传入加 %00(十六进制)

protected

序列化后需在类名和字段名前面加上%00*%00


攻击

攻击前提:

  1. 必须有 unserailize() 函数
  2. unserailize() 函数的参数必须可控(可能需要绕过一些魔法函数)

攻击思路:

首先,当前作用域中的类是我们能够用的,类中的魔术方法能够让我们调用类中的其他方法(function),所以反序列化对象的选取应该找这样的类。

之后,我们说反序列化攻击依托的是类的属性,关键属性自然是在魔术方法中用到的那些,属性的内容我们需要结合魔术方法中的描述进行思考,最终形成payload

其实这整个过程就像是在走迷宫,路径是一条线,线的两端点分别是unserailize() 和我们要获取的信息(flag),中间路上需要动力驱使我们行走,动力就是魔术方法(自动进行)。就是一点一点地调用,形成一条串起来整个思路。

攻击步骤:

  1. 确定反序列化攻击可行性

  2. 寻找反序列化对象(目标),重点在有__destruct()、__wakeup()魔术方法的类

  3. 寻找对象所属类中的重点攻击属性(一层一层地研究)

  4. 构造属性内容

    • 单纯赋值(类里面/创建对象后)

    • file_put_content()函数

      1
      2
      3
      $K0rz3n = new K0rz3n;
      $data = serialize($K0rz3n);
      file_put_contents("seria.txt"内容, $data序列化变量?);

​ 4.注意补上%00…

注意:

  1. 反序列化时必须保证当前的作用域环境下有该类的存在
  2. 反序列化攻击是依托类的属性进行攻击

高级学习

POP链

面向属性编程,个人感觉是一种思路链

比如

1
writer->shutdown()->render()->filter()->preg_replace(我们控制的属性)->代码执行

这就是一个POP链


phar反序列化

phar文件本质上是一种压缩文件,会以序列化的形式储存在meta-data里,在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。

image-20231113193915293

攻击条件:

不再局限于传统反序列化攻击条件(unserialize+参数可控),phar://拓展了php反序列化攻击面,降低了攻击起点

1
2
3
1、phar文件要能够上传到服务器端。
2、要有可用的魔术方法作为“跳板”。
3、文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。
phar文件结构
  1. stub

    必须以__HALT_COMPILER();?>来结尾

  2. manifest

  3. contents

  4. signature

使用:

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class TestObject {
}

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
1
2
3
4
5
6
7
8
9
10
<?php 
class TestObject {
public function __destruct() {
echo 'Destruct called';
}
}

$filename = 'phar://phar.phar/test.txt';//已经序列化
file_get_contents($filename); //执行该函数触发反序列化进而触发__destruct()方法
?>

执行输出结果

img

绕过

php识别phar文件是通过文件头的stub中的__HALT_COMPILER();?>,对前面的内容和后缀名没有要求

所以我们就可以 添加任意文件文件头+修改成响应文件的后缀名 以绕过检查

1
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");

phar与反序列化


其他

  1. $this指当前所在的class变量
  2. 序列化如果有private和protected属性注意补全空的字符串
  3. 序列化对象首字母代表参数类型 O:Objext S:String...

参考:反序列化初级介绍

  • Title: 反序列化
  • Author: dawn_r1sing
  • Created at : 2023-11-16 22:43:15
  • Updated at : 2023-11-16 22:52:43
  • Link: https://dawnrisingdong.github.io/2023/11/16/反序列化/
  • License: This work is licensed under CC BY-NC-SA 4.0.