php反序列化基础知识前奏

php反序列化基础知识前奏

​​

文章初衷

序列化?反序列化?好高大尚的词语,在学习think PHP5版本的框架反序列化时,多序列化和反序列化的过程以及函数的触发调用有了很大的感触。遂此篇文章来做一个分享学习,若文章中出现一些知识点的错误,请大佬指出。

反序列化前奏

序列化和反序列化的出现,是为了漏洞的出现吗?答案当然是否,所谓的序列化是把对象/数据变成一串能存文件,字节传输的流,通俗来讲,一般会序列化为json格式或者xml格式,以及在PHP中的serializae()和unserialize()、python的pickle,而xml格式在现在的使用并不多见,故大多数是json的格式,那么反序列化就是相反的喽,便是将字符串转为对象。

那么为啥要搞序列化和反序列化呢?搞这个漏洞很多啊,POP链的构造从而导致RCE,这个出现的目的到底是为啥?网络上有很多的答案,但是我认为比较重要的有两点,一个时网络不能直接传输对象,前后端通信只能通过字符串进行,那么这个时候序列化的重要性就来了,他不就是转为字符串吗?第二个就是他能跨语言进行交互,Java对象序列化为json的格式中,给python和PHP都能用,这样不停方便吗?这就是本人认为的两大重要点。

那么这个漏洞是咋产生的呢?在序列化之后还是在序列化之前?漏洞产生的时间段是在序列化之后,因为我们可以进行构造一个具有危害的序列化之后的数据,就拿php来说可以使用代码执行和命令执行,从而来进行RCE,反序列化的最大利用点便是RCE,假如不能RCE,我要他干啥,没啥触发的。

php类与对象

类是啥?类是定义一系列属性和操作的模板,而对象,就是把属性进行实例化,完事交给类里面的方法,进行处理,举个小例子。

php 复制代码
 <?php
 class people{
    //定义类属性(类似变量),public 代表可见性(公有) ----所谓的属性(公有属性、私有属性、保护属性)
     public $name = 'joker';
    //定义类方法(类似函数)----->所谓的方法可以当函数理解
    public function smile(){
         echo $this->name." is smile...\n";
    }
 }
 ​
 $psycho = new people(); //根据people类实例化对象
 $psycho->smile();
 ?>

使用Class定义了一个pepole,这个类中有公共属性,公共属性便是都可以进行调用。此类中有一个smile方法,使用this->拿到共有属性中的name值,使用new进行实例化对象,从而来进行调用函数进行打印。这样就能有漏洞吗?当然不是。这就要谈到PHP的魔术方法​

魔术方法

啥又是个魔术方法,魔术方法是触发了某个事件后,魔术方法会自动执行,并不需要要手动调用,普通的函数需要进行调用才能执行,而魔术方法在触发一个事件自动执行。PHP中将所有的魔术方法(__xxxx())类似的命名。下面是一些常见的魔术方法。

方法名 作用
__construct 构造函数,在创建对象时候初始化对象,一般用于对变量赋初值 构造函数 类初始 类实例化 new xxx 自动执行
__destruct 析构函数,和构造函数相反,在对象不再被使用时(将所有该对象的引用设为null)或者程序退出时自动调用 当类执行结束后 自动执行
__toString 当一个对象被当作一个字符串被调用,把类当作字符串使用时触发,返回值需要为字符串,例如echo打印出对象就会调用此方法 相当相当重要 对象和字符串的拼接 stu =new stu . 'is running'
__wakeup() 使用unserialize时触发,反序列化恢复对象之前调用该方法 序列化功能 便于存续 序列化 类 序列化后 ssrf
__sleep() 使用serialize时触发 ,在对象被序列化前自动调用,该函数需要返回以类成员变量名作为元素的数组(该数组里的元素会影响类成员变量是否被序列化。只有出现在该数组元素里的类成员变量才会被序列化) serialize
__destruct() 对象被销毁时触发
__call() 在对象中调用不可访问的方法时触发,即当调用对象中不存在的方法会自动调用该方法 第一不存在 private
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 读取不可访问的属性的值时会被调用(不可访问包括私有属性,或者没有初始化的属性)
__set() 在给不可访问属性赋值时,即在调用私有属性的时候会自动执行
__isset() 当对不可访问属性调用isset()或empty()时触发 private isset
__unset() 当对不可访问属性调用unset()时触发 private $name
__invoke() 当脚本尝试将对象调用为函数时触发 stu = new student() stu()

详细的举一个例子吧:

php 复制代码
 <?php
     class animal {
         private $name = 'caixukun';
 ​
         public function sleep(){
             echo "<hr>";
             echo $this->name . " is sleeping...\n";
         }
         public function __wakeup(){
             echo "<hr>";
             echo "调用了__wakeup()方法\n";
         }
         public function __construct(){
             echo "<hr>";
             echo "调用了__construct()方法\n";
         }
         public function __destruct(){
             echo "<hr>";
             echo "调用了__destruct()方法\n";
         }
         public function __toString(){
             echo "<hr>";
             echo "调用了__toString()方法\n";
         }
         public function __set($key, $value){
             echo "<hr>";
             echo "调用了__set()方法\n";
         }
         public function __get($key) {
             echo "<hr>";
             echo "调用了__get()方法\n";
         }
     }
     
     $ji = new animal();
     $ji->name = 1;
     echo $ji->name;
     $ji->sleep();
     $ser_ji = serialize($ji);
     //print_r($ser_ji);
     print_r(unserialize($ser_ji))
 ?>

对上面的进行解释。在new一个实例化对象的时候,会自动调用__construct()方法,在对私有属性进行赋值时,会调用__set()f方法,由于魔术方法是用__进行的,所以sleep()并不算,在进行打印反序列化的数值时,调用了__wakeup()方法,最后两次使用__destruct()方法,如下图所示:

那么序列化之后的数据是啥样的?反序列化之后有没有影响?有没有一些特殊的?那么我们来看一个序列化的小例子。

复制代码
      
php 复制代码
 <?php
 // 类名别用 object,改成 MyObject 更安全
 class MyObject{
     public $team = 'joker';
     private $team_name = 'hahaha';
     protected $team_group = 'biubiu';
     // 先声明 $team_member 属性
     public $team_member;
 ​
     function hahaha(): void{
         // 给已声明的属性赋值(注意不要加 $)
         $this->team_member = '奥力给';
     }
 }
 ​
 $object = new MyObject();
 // 调用方法,给属性赋值
 $object->hahaha();
 // 序列化对象
 echo serialize($object);
 ?>  
复制代码

如上所示,以上时序列化之后的结果,o代表一个对象,8是这个object的长度,4是有四个类属性。s表示是string类型的数据。5表示长度为3,后买你便是具体的内容。那么你就会发现一个问题,为啥后面的长度和名称对不上,这就和私有属性和保护属性有关,私有属性会在前面加上类的名称长度会比正常大小多两个字节,那么这两个字节的具体内容是啥,其实是\0多出来的东西便是这两个,在前后各一个。在遇到保护属性时,会在前面加上*,并且还是会多处\0的字符,那么保护属性便是多出来三个字符,两个\0和*

那么这是序列化之后数据,我假如在这里加上eval、system等函数呢?传输到后端进行反序列化的时候会执行吗?答案是会执行,这就到了POP的链的构造。

今天的分享到此结束,继续加油,继续学习,后续出一篇think PHP5框架通过反序列化构造POP链从而RCE,从代码一步一步进行。

相关推荐
努力努力再努力wz2 小时前
【MySQL进阶系列】拒绝冗余SQL:带你透彻理解视图的底层逻辑
android·c语言·数据结构·数据库·c++·sql·mysql
常利兵2 小时前
安卓黑科技:实现多平台商品详情页一键跳转APP
android·科技
星光开发者2 小时前
基于springboot电动汽车租赁管理系统-计算机毕设 附源码 11217
javascript·spring boot·mysql·django·php·html5·express
_李小白3 小时前
【android opencv学习笔记】Day 5: 高效的图像扫描
android·opencv·学习
xxjj998a3 小时前
PHP vs C++:性能与用途全解析
php·laravel
liang_jy10 小时前
Android 窗口容器树(一)—— 窗口和窗口容器树
android·源码
HUGu RGIN11 小时前
MySQL--》如何在MySQL中打造高效优化索引
android·mysql·adb
Joseph Cooper13 小时前
Linux/Android 跟踪技术:ftrace、TRACE_EVENT、atrace、systrace 与 perfetto 入门
android·linux·运维
空中海14 小时前
安卓逆向03. 动态调试、抓包分析与 Frida Hook
android