前言
昨天在易霖博搞的網絡安全與執法競賽看到的一道web題,實際上就是用兩個原題湊起來的。。
不過后面的一關沒見過這里簡單記錄一下
第一關
打開是個登錄界面,和BJDCTF的簡單注入一模一樣,連密碼都一樣。。
第二關
登錄后得到
即
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
include 'flag.php'; //defines $flag
class B {
function __destruct() {//當一個對象銷毀時被調用或者腳本結束時調用
global $flag;
echo $flag;
}
}
if (isset($_POST['payload'])) {
ob_start();//打開輸出緩沖區,所有的輸出信息不在直接發送到瀏覽器,而是保存在輸出緩沖區里面,可選得回調函數用於處理輸出結果信息。
$a = unserialize($_POST['payload']);
if ($a === False) {
ob_end_clean();//清空(擦除)緩沖區並關閉輸出緩沖
}
throw new Exception('Something tragic happened');//拋出一個錯誤
}
?>
還是原題 Websec level 30
payload:
a:2:{i:0;O:1:"B":0:{}i:0;i:1;}
詳細可以參考:
[https://kangwoosun.github.io/webhacking/2020/03/28/Websec/]
[https://www.evonide.com/breaking-phps-garbage-collection-and-unserialize/#comments]
[https://www.evonide.com/fuzzing-unserialize/]
以下是個人的淺薄理解
如果傳入一個序列化的數組,並且這個數組中存在兩個或多個key相等的變量,那么反序列化的時侯將刪除首先輸入的變量。因此,如果將類 B 對象作為value冗余,則將調用析構函數,而不會導致反序列化錯誤,即先解析 O:1:"B":0:{} 生成一個對象,然后在向后解析又遇到 i:0 於是將前一個 i:0 的值改為1,就相當於消滅了之前生成的對象於是觸發__destruct函數,之后也是冗余的關系使得反序列化的結果返回的是 array(0=>1) ,不為False,於是不會進入到ob_end_clean(),最后輸出flag
這題如果直接傳入
O:1:"B":0:{}
是不會輸出flag,因為雖然這樣反序列化得到的a是一個對象不等於False,但是之后會通過throw new 拋出一個錯誤,它這里的拋出錯誤就類似於下了一個斷點一樣,使得腳本程序的運行停止在了這里,並沒有結束,所以反序列化后的B對象仍然存在,不會觸發__destruct