这道题是比较简单的PHP代码审计与pop链构造
<?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);
}
}
这里首先看到include(“flag.php”),就可以知道他们把flag文件包含到这个页面了,所以我们往后看,发现file_get_contents这个函数可以利用进行读取flag.php,但想要执行这个函数,我们需要执行read,但read需要在process中调用,并且op必须为2,那我们往后看,调用process是在__destruct中调用的,这个__destruct是个魔术函数,就是当对象销毁的时候会自动的执行这个函数,所以我们需要构造这个类,这里还有is_valid函数,这个是用来判断提交的数据是否在这个ASCII码范围内的,这里我们看看好像大致没什么影响,所以我们继续往后构造,我们现在需要将filename,还有op的构造为flag.php和2,前者没什么问题,但后者在
这里会继续一次重置op,但看这里为===,为强等于,PHP中的强等于需要判断数据的类型与数据本身,如果我们传一个int型的2,则会和“2”进行比较,会比较出false,这里就可以绕过了,最后再进行序列化就可以了。然后貌似我们已经构造完pop链了,但is_valid函数会对序列化的结果进行判错,因为private和protected型的变量序列化后会产生不看见字符\x00这个在ASCII码中为0,所以我们可以改属性类型为public,因为PHP7.1以上对属性类型不敏感,所以可以用来绕过。
这里为我构造的pop链
当然这里的flag.php可以改为PHP的伪协议,php://filter/read=convert.base64-encode/resource=flag.php,然后需要对其进行base64解码即可
获取flag