ctf比赛中经常出现php反序列化的题,所以写一篇博客来记一下php序列化中的字符标识,可能以后会有补充
a:array 数组
php > echo serialize(array());
a:0:{}
php > echo serialize(array(1,2));
a:2:{i:0;i:1;i:1;i:2;}
i:integer 整数
php > echo serialize(14);
i:14;
b:boolean bool值
php > echo serialize(true);
b:1;
d:double 小数
php > echo serialize(1.1);
d:1.1;
O:class 对象
php > class a{}
php > echo serialize(new a());
O:1:"a":0:{}
//解释一下,O表示对象,1表示对象名的字符串长度,'a'是对象名,0是对象内属性个数
s:string 字符串
php > echo serialize('a');
s:1:"a";
N:null NULL值
php > echo serialize(NULL);
N;
r:reference 对象引用
<?php
echo "<pre>";
class SampleClass1
{}
class SampleClass
{
public $value;
public $test;
public function __construct()
{
$this->value = new SampleClass1();
$this->test = $this->value;
}
}
$a = new SampleClass();
echo serialize($a);
?>
输出O:11:"SampleClass":2:{s:5:"value";O:12:"SampleClass1":0:{}s:4:"test";r:2;}
r:<number> number 的大小:
O:11:"SampleClass" 表示 1
s:5:"value" 表示 2
r:2表示 s:5:"value" 的引用
注意,值的引用,也就是值的标识符中有R或r,则不计数,如果后面还有属性 比如
O:11:"SampleClass":2:{s:5:"value";O:12:"SampleClass1":0:{}s:4:"test";r:2;s:6:"value1";s:1:"a"}
s:6:"value1" 代表 3
R和r同理
R:pointer reference
<?php
echo "<pre>";
class SampleClass1
{}
class SampleClass
{
public $value;
public $test;
public function __construct()
{
$this->value = new SampleClass1();
$this->test = &$this->value;
}
}
$a = new SampleClass();
echo serialize($a);
// O:11:"SampleClass":2:{s:5:"value";O:12:"SampleClass1":0:{}s:4:"test";R:2;}
?>
利用R我们可以在反序列化的时候,将两个属性的值关联起来,是的两个属性值反序列化之后一直相同
<?php
error_reporting(0);
echo "<pre>";
class SampleClass
{
public $value;
public $test;
public function __construct()
{
$this->value = 'a';
$this->test = &$this->value;
}
}
$a = new SampleClass();
echo serialize($a)."</br>";
$b = serialize($a);
$b = unserialize($b);
$b->value='new value';
echo serialize($b);
//O:11:"SampleClass":2:{s:5:"value";s:1:"a";s:4:"test";R:2;}
O:11:"SampleClass":2:{s:5:"value";s:9:"new value";s:4:"test";R:2;}
?>
以上输出结果表明第二次序列化不改变引用的关系
C:custom object 自定义对象序列化
php有一个接口 Serializable,有两个方法serialize()和unserialize(),在序列化的时候,按照用户自定义方法序列化
c的格式如下
C:<name length>:"<class name>":<data length>:{<data>}
其中 表示类名 的长度, 表示自定义序列化数据 的长度,而自定义的序列化数据 是完全的用户自己定义的格式,与 PHP 序列化格式可以完全无关,这部分数据由用户自己实现的序列化和反序列化接口方法来管理。
例子
class MyClass implements Serializable
{
public $member;
function MyClass()
{
$this->member = 'member value';
}
public function serialize()
{
return ($this->member);
}
public function unserialize($data)
{
$this->member = ($data);
}
}
$a = new MyClass();
echo serialize($a);
echo "\n";
print_r(unserialize(serialize($a)));
// C:7:"MyClass":12:{member value} MyClass Object ( [member] => member value )
S:encoded string
可以将16进制编码成字符,可以进行绕过特定字符
<?php
class test{
public $value;
function __construct(){
}
}
echo serialize(new test);
$a='O:4:"test":1:{s:5:"value";S:1:"\61";}';
$a=unserialize($a);
var_dump($a);
结果
O:4:"test":1:{s:5:"value";N;}
object(test)[2]
public 'value' => string 'a' (length=1)
最后,来个大总结
参考链接
https://www.evonide.com/fuzzing-unserialize/#comments
https://www.cnblogs.com/wayne173/p/3747465.html