概述
前言
可能会需要有类的知识但这里不多赘述
序列化
在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 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上 或%00的前缀。字符串长度也包括所加前缀的长度。其中 字符也是计算长度的。
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_serialize | serialize()函数反序列处理数组方式 |
[极客大挑战 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大的数即可
最后
以上就是疯狂麦片为你收集整理的php序列化和反序列化&魔术方法前言序列化反序列化魔术方法绕过Session反序列化漏洞[极客大挑战 2019]PHP的全部内容,希望文章能够帮你解决php序列化和反序列化&魔术方法前言序列化反序列化魔术方法绕过Session反序列化漏洞[极客大挑战 2019]PHP所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复