[网鼎杯 2020 青龙组]AreUSerialz WP
看到题目,首先进入代码审计
<?php
include("flag.php");//在文件中插入flag.php文件的内容
highlight_file(__FILE__);//highlight_file() 函数对文件进行 PHP 语法高亮显示
class FileHandler {//定义类
protected $op;
protected $filename;
protected $content;//定义成员变量
function __construct() {//如果new了一个FileHandler新对象,机会执行该函数
$op = "1";
$filename = "/tmp/tmpfile";//初始化成员变量
$content = "Hello World!";
$this->process();//在新对象中调用process()函数
}
public function process() {//定义上文提到的process()函数
if($this->op == "1") {
$this->write();//如果op等于1执行write()函数
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {//定义上文提到的write()函数
if(isset($this->filename) && isset($this->content)) {//使用有filename和content变量,很显然有
if(strlen((string)$this->content) > 100) {//如果对象中content字符串长度大于100...很显然小于100
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);//把对象定义的content的内容写入filename文件中,定义res为content的长度
if($res) $this->output("Successful!");//如果写入成功了即res有长度则输出successful
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {//如果filename存在
$res = file_get_contents($this->filename);//函数是用于将文件的内容读入到一个字符串中的首选方法,即将finename文件读入进res
}
return $res;
}
private function output($s) {//定义输出结果的格式
echo "[Result]: <br>";
echo $s;
}
function __destruct() {//反序列化结束后定义op为1,content为空字符,再调用process()函数一次
if($this->op === "2")//强类型比较,这里输入的op如果不是字符串的2,则false
$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))//ord() 函数返回字符串中第一个字符的 ASCII 值
return false;
return true;//确保字符串中的字符都是asc码在32-125中间的
}
if(isset($_GET{'str'})) {//检验是否有字符串输入
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);//将str字符串反序列化
}
}
通过如上代码审计,可以发现,如果单纯只进入write()函数没什么用,最后输出的东西只能是succseeful或者fail等没用的东西,而如果进入了read()函数,那么可以利用file_get—contents()函数将开头就导入的flag文件写入res字符串中输出出来。
因此需要进入process,这里我们通过反序列化结束时的__dustruct进入process()函数。
同时进入read()函数有一个关键点:op需要等于2,并且需要绕过强比较,将op=2,而不是op=”2“。
题目中让我们自己输入字符串,由于最后将输入的字符串反序列化,因此输入的应该是一个序列化之后的字符串。
而上面的is_valid()函数又必须让asc码在32-125,因此序列化之后不能存在 * %00之类的东西,所以输入序列化的时候需要进行绕过。
当php版本大于7.1时,对成员变量类型不敏感,而此时可以得到php版本大于7.1,因此在构造字符串时直接使用public格式。
【另外:将payload中,表示字符类型的S进行大写,会将成员变量作为16进制解析。
也可以对不可见字符进行绕过。】
因此构造的字符串如下:
O:11:“FileHandler”:3:{s:2:“op”;i:2;s:8:“filename”;s:8:“flag.php”;s:7:“content”;s:5:“hello”;}
发现啥也没有,然后进入f12去看一眼,找到了。
但是我之后看wp的时候,才发现php伪协议也可以使用,不过我不太懂。