php序列化和反序列化&魔术方法

前言

可能会需要有类的知识但这里不多赘述

序列化

在PHP中,序列化用于存储或传递 PHP 的值的过程中,同时不丢失其类型和结构。
内存数据——稍纵即逝,不可持久化。
变量所储存的数据,即内存数据,而文件是持久数据。
l
序列化:
序列化就是将内存中的变量数据,保存为文件中的持久数据的过程。将内存变成文件。
反序列化:
反序列化就是将序列化后存储到文件中的数据,恢复成php程序代码的变量表现形式的过程,将文件变成内存。
序列化函数原型如下:
string serialize ( mixed $value )

eg

class C {
public function __construct($data, $pass)
	{
		$this->data = $data;
		$this->pass = $pass;
	}
	public $data;
	private $pass;
}
$number = 10;
$str = 'asd';
$bool = true;
$null = NULL;
$arr = array('a' => 1, 'b' => 2);
$c = new C('cc', true);

var_dump(serialize($number));
var_dump(serialize($str));
var_dump(serialize($bool));
var_dump(serialize($null));
var_dump(serialize($arr));
var_dump(serialize($c));

输出

string(5) "i:10;"
string(10) "s:3:"asd";"
string(4) "b:1;"
string(2) "N;"
string(30) "a:2:{s:1:"a";i:1;s:1:"b";i:2;}"
string(50) "O:1:"C":2:{s:4:"data";s:2:"cc";s:8:" C pass";b:1;}"//o是对象,1是长度,2表示两个键值对

序列化对于不同类型得到的字符串格式为:

String : s:size:value;
Integer : i:value;
Boolean : b:value;(保存1或0)
Null : N;
Array : a:size:{key definition;value definition;(repeated per element)}
Object : O:strlen(object name):object name:object size:{s:strlen(property name):property name:property definition;(repeated per property)}

序列化对象

序列化对象的时候,只会保存属性值。

序列化对象时,不会保存常量的值。对于父类中的变量,则会保留。

class CB {
	public $CB_data = 'cb';
}

class CC extends CB{
	const SECOND = 60;

	public $data;
	private $pass;

	public function __construct($data, $pass)
	{
		$this->data = $data;
		$this->pass = $pass;
	}

	public function setPass($pass)
	{
		$this->pass = $pass;
	}
}
$cc = new CC('uu', true);

var_dump(serialize($cc));

输出结果为:

string(75) “O:2:“CC”:3:{s:4:“data”;s:2:“uu”;s:8:” CC pass";b:1;s:7:“CB_data”;s:2:“cb”;}"

序列函数

serialize(PHP 4, PHP 5, PHP 7)
serialize 一产生一个可存储的值的表示
描述
serialize ( mixed $value ) : string
serialize()返回字符串,此字符串包含了表示value的字节流,可以存储于任何地方。
unserialize(PHP 4,PHP 5,PHP 7)
unserialize 一从已存储的表示中创建 PHP的值
说明
unserialize ( string $str ) : mixed
unserialize()对单一的已序列化的变量进行操作,将其转换回PHP的值。

pop链构造

property
构造pop时,当访问一个不存在的属性的时候,会直接调用__get方法,并将变量传递给__get方法中变量

phar:// (不懂,暂缓)

对对象反序列化
file_get_contents()函数把整个文件读入一个字符串中

字符串逃逸

反序列化

a:1:{i=0;s:3:“123”;}abc
a:1:{i=0;s:3:“123”;};}

魔术方法

所有的魔术方法必须声明为public

1.__construct()
类的构造函数,每次创建新对象时先调用此方法
2.__destruct()
类的析构函数,某个对象的所有引用都被删除或者销毁时调用
3.__toString()
把类被当做一个字符串使用时调用
4.__wakeup( )
使用unserialize函数,反序列化恢复对象之前时调用
5.__sleep()
使用serialize()函数,序列化对象之前时调用
6.__call()
在对象中,调用不存在的方法时调用
7.__callstatic()
在静态上下文中,调用不可访问的方法时触发
8.__get()
访问不存在的成员变量时调用
9.__set()
设置不存在的成员变量时调用
10.__isset( )
在不可访问的属性上调用isset( )empty()触发
11.__unset()
在不可访问的属性上使用unset()时触发
12.__toString()把类当作字符串使用时触发
13.__invoke()
当尝试以调用函数的方式调用一个对象时触发

__sleep()函数,是在序序列化时被自动调用。__wakeup()函数,在反序列化时,被自动调用。

绕过

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

protected
O:4:“Name”:2:{s:11:"%00*%00username";s:5:“admin”;s:11:"*password";s:3:“100”;}
public 不加申明

wakeup绕过

反序列化时,如果表示对象属性个数的值大于真实的属性
数时就会跳过_wakeup( )的执行。
$target = ‘admin’;

当目标被private修饰时,序列化结果为:
0:4: "Name" :1:{s:12:"%00Name%00target";s:18: "< ?php phpinfo();?>";}
当目标被protected修饰时,序列化结果为:
0:4: "Name" :1:{s:12:"%00*%00target";s:18: "<?php phpinfo(); ?>";}

Session反序列化漏洞

当session_start()被调用或者php.ini中session.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp)

PHP处理器的三种序列化方式

处理器对应的存储格式
php_binary键名的长度对应的ASCII字符+键名+经过serialize() 函数反序列处理的值
php键名+竖线+经过serialize()函数反序列处理的值
php_serializeserialize()函数反序列处理数组方式

[极客大挑战 2019]PHP

网页中提到了备份网站,因此可以尝试使用一个网站备份文件名的字典来进行爆破,发现网站中有www.zip文件
如果网站存在备份文件,常见的备份文件后缀名有:“.git” 、“.svn”、“ .swp”“.~”、“.bak”、“.bash_history”、“.bkf” 尝试在URL后面,依次输入常见的文件备份扩展名。
解压后打开flag.php输入flag错误再打开index.php
将class.php文件包含,将select反序列化

<?php
            include 'class.php';
            $select = $_GET['select'];
            $res = unserialize(@$select);
            ?>

再看class.php

class Name{
    private $username = 'nonono';
    private $password = 'yesyes';

    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }

    function __wakeup(){
        $this->username = 'guest';
    }

    function __destruct(){
        if ($this->password != 100) {
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();
        }
    }
}

destruct魔术方法的时候,如果用户名为admin,密码为100则可以输出flag的值。
但是wakeup方法会导致username成为guest,因此需要通过序列化字符串中对象的个数来绕过该方法
因为私有化所以在类名前后加%00(wakeup绕过)
O:4:“Name”:2:{s:14:"%00Name%00username";s:5:“admin”;s:14:“Namepassword”;s:3:“100”;}
将2转化成比2大的数即可

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值