ISC 2018 藍鯨魔塔線上挑戰賽 Write Up


WEB

藍鯨文件管理系統

逆战炼狱双蝎 www.sbtxt.icu 首先訪問網站,看到幾個關鍵功能:上傳、改名、刪除。對應的文件為:upload.php,rename.php和delete.php

?在網站源文件中找到相應文件并查看結構,發現有全局配置文件:common.inc.php。在common.inc.php中可以看到全局進行了轉義,這樣常規注入少了大部分。遍觀代碼,輸入處沒有任何反轉義、反解壓、數字型等特殊情況,基本可以確定不存在直接的注入漏洞。

接下來觀察上傳處的代碼:upload.php,第一段檢查

段代碼定義了我們如何上傳文件以及允許后綴名(檢查后綴)并且數據庫中存儲了上傳的文件名和后綴名(分開存儲)

第二段存儲

可見,上傳的文件名走過的流程是:

$file[‘name’] -> pathinfo() –> $path_parts["filename"] -> quote() -> insert

由于經過了pdo的quote方法轉義,所以此處也不存在注入。

再看rename.php改名功能

根據$req['filename']從數據庫里查詢到已存在的一行,并調用update語句進行修改。

但這里oldname=‘{$result[’filename‘]}’ 將從數據庫里查出的$result[‘filename’]再一次入庫,結果造成一個“二次注入“。

這個二次注入有什么作用呢?這是個update的類型的二次注入,所以我們用來去修改文件名。源碼中的上傳,重命名,刪除功能,我們一次查看首先upload.php是文件上傳的操作,但可見上傳處對文件進行了白名單驗證

導致我們無法上傳惡意文件。

其次是delete.php,這個文件其實是個煙霧彈,刪除操作并不能利用。再次是rename.php,這里明顯是getshell的關鍵。

這幾行最為重要:

Oldname和newname,有幾個特點:

  • ?后綴相同,都是$result['extension']
  • ?oldname的文件名來自數據庫,newname的文件名來自用戶輸入

?

首先后綴相同這個特點,就導致getshell似乎難以完成,如果要getshell那么一定要將非“.php”后綴的文件重命名成“.php”的文件。后綴相同怎么重命名?

除非后綴為空!

所以我們的update型注入就開始派上用場了。通過update型注入,我們可以將數據庫中extension字段的值改為空,同時也可以控制filename的值,那么等于說我能控制rename函數的兩個參數的值,這樣getshell就近在咫尺了。

?

但是在重命名的時候還有個坑:if(file_exists($oldname))

我雖然通過注入修改了filename的值,但我upload目錄下上傳的文件名是沒有改的。因為我利用注入將extension改為空了,那么實際上數據庫中的filename總比文件系統中真實的文件名少一個后綴。

再次上傳一個新文件,這個文件名就等于數據庫里的filename的值就好了。

  • 首先構造注入用的文件和getshell的文件。
  • 插入語句為:insert into `file` ( `filename`, `view`, `extension`) values( ‘文件名’, ‘后綴名’);
  • 更新語句為:update `file` set `filename`=‘新文件名’, `oldname`=‘查詢文件名' where `fid`=fid
  • 首先想要后綴名為空就得有一個語句為
  • Extention = ‘’并且還原一個我們可以上傳的文件名
  • 構造如下: update `file` set `filename`=‘x.jpg’, `oldname`=‘’,extension=‘’,filename=‘x.jpg’ where `fid`=fid
  • newname就是:’,extension=‘’,filename=‘x.jpg 而原始上傳文件名應該為這次的oldname,但是注意加上多一個后綴名因為后綴名將入庫到extension中。原始為: ’,extension=‘’,filename=‘x.jpg .jpg

整理為:需要兩個文件,一個后綴名為:

',extension='',filename='x.jpg.jpg

一個木馬文件,重命名為x.jpg

然后找到rename功能,輸入文件名重命名

注意這里提示輸入不加后綴名~所以我們輸入的就是filename

注入后,數據庫中filename=x.jpg,extension=‘’,文件系統中文件名為x.jpg.jpg

?

接著上傳真正包含webshell的x.jpg

?

再次利用rename改名,就不會被添加上jpg的后綴名了


?藍鯨筆記系統

訪問網站后注冊登錄(看似漏洞可能存在的點很多,登錄功能是否存在注入可以進行探測,鏈接存在文件包含的嫌疑,筆記提交存在注入或者xss或者CSRF的嫌疑等等)

有一條筆記,帶來了一個提示:

?

告訴我們有一個dbinit.sql于是下載該文件,其中有flag表

這就說明,flag在數據庫中,此題目很有可能是sql注入(但是希望沒有跑偏)在看一下題目給出的url,開始就很懷疑這樣的動態url。

Index.php?action=front&mode=index

Index.php?action=front&mode=newnote

Index.php?action=front&mode=delete

那么index就是入口,直接訪問/front/index.php和/front/delete.php等連接,發現訪問被拒絕了。

猜測這樣的動態連接在腳本中是這樣構造的:

Include ($action.’/’.$mode.’.php’)那么包含了php后綴名的文件,不需要再進行截斷就可以直接利用包含漏洞,于是利用php為協議中的filter過濾方法加base64編碼方法獲取源代碼。

例如:index.php?action=php://filter/read=convert.base64-encode/resource=./&mode=index這樣我們就能夠審計源代碼了。

掃描發現存在后臺地址:admin/index.php和admin/login.php

所以重點來審計一下后臺的登錄功能(利用連接: index.php?action=php://filter/read=convert.base64-encode/resource=admin/&mode=admin/login )

調用了set_login函數,所以我們來看一下set_login函數(common.php)

這?設置了cookies和session,cookie是admin|md5(SECURITY_KEY+’admin’),其中SECURITY_KEY是6位隨機數.

再來看一下后臺的驗證登錄過程(admin/index.php):

主要調用了check_login()和get_level()兩個函數

兩個函數在:common.php中

這兩個函數返回用戶的userid和level,如果cookie驗證失敗,則userid為false。level如果是0,也會返回false但是如果我們沒有登錄,而且繞過了cookie的驗證,那么? $_SESSION[‘userid‘]和$_SESSION[’level’] 的默認初始值都是null。

在php中,$null!==false是反回true的,所以我們只要能夠偽造cookie,就可以繞過這?的驗證

進入后臺后,主要看下生成cookie至關重要的值$_SESSION[‘SECURITY_KEY’]的產生過程:

當用戶一訪問,如果沒有設置token和security_key就會使用rand生成隨機字符串,但是前面我們也說過,如果rand函數不適用srand產生種子是有缺陷的。

不過這里無法繞過,因為前面測試發現php已經修復了漏洞。所以這里的隨機字符串我也修改成了兩個元素:wh中的6位隨機排列,降低了很多難度。

以防今后出現類似題目,還是提供大家一個腳本去進行隨機數的預測,這里貼出關鍵代碼:

由于測試發現預測出來的值可

能與原始相差1,還需要計算

可能的范圍,其中用了itertools

中的方法去計算笛卡爾積

還有就是需要提前訪問兩次站點,先生成32位以上的字符串產生預測缺陷。(sek為6位,tooken為16位,而tooken將下發給用戶,所以44位情況如下)

?那么如果存在缺陷,多運行幾次腳本總能夠猜測出來。但是本環境中不存在了,我們就用到暴力破解啦。

這樣我們就能夠獲取到網站管理員的cookie了。

利用cookie manager修改當前cookie,訪問index.php?action=admin&mode=index就能登錄后臺啦

?

登錄后發現鏈接:setpagenum功能

再次利用剛才的LFI漏洞下載源碼審計

Page參數雖然沒有經過單引號包裹,但是經過了is_number()檢查,導致無法注入。

接著看到index.php中:

?

Page_size直接放入sql語句中進行查詢,如果可以控制page_size,這里就能夠存在注入,但是好像page_size在代碼中設置了只能是整形?

好像不能夠利用,打開最開始下載的dbinit.sql查看,竟然發現了num是varchar的字符類型,典型的數據庫和代碼不一致。

?

接下來只需要利用php 5.x中的is_numberic缺陷(php 7.0已修復)認為0x…是整數,就能夠二次注入了

先看note的字段數為:4

所以構造注入語句為:1 union select 1,flag,3,4 from flags

然后將語句轉換為16進制數據:0x3120756E696F6E2073656C65637420312C666C61672C332C342066726F6D20666C616773提交就可以注入了。

?