一、環境
網上自己找
二、步驟
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