記一次從源代碼泄漏到后台獲取webshell的過程


0x01 前言

在一次授權測試中對某網站進行測試時,marry大佬發現了一個網站的備份文件,里面有網站源代碼和數據庫備份等。根據網站信息和代碼都可以發現該系統采用的是微擎cms,利用數據庫備份中的用戶信息解密后可以登錄系統,接下來要看是否可以獲取webshell。

0x02 WEBSHELL獲取的嘗試

有了數據庫備份文件,然后找一下是否有用戶的信息,能否登錄系統。

1.登錄后台

解壓備份文件可以從data/backup目錄下找到數據庫的備份,從中找到了用戶表ims_users

知道了用戶名、加密后的密碼和salt,我們去看一下密碼加密的算法。

我這里直接搜索password,在forget.ctrl.php中找到了一處。

密碼加密方法是$password = md5($password . $member_info['salt'] . $_W['config']['setting']['authkey']);。是根據原密碼+salt+authkey的形式進行拼接,然后進行md5加密。

authkey在data/config.php文件中。

現在salt和authkey以及加密后的密碼已經獲得,開始去解密密碼是多少。這里我們將saltauthkey拼接為新的salt,然后使用md5($pass.$salt)的加密方式進行解密。

解密后即可登錄后台。

接下來就是webshell的獲取了。

本以為都已經是管理員了,獲取shell就是分分鍾的事,然而事情遠遠沒有那么簡單。

2.失敗的獲取shell過程

根據搜索發現,該cms后台獲取shell的方法也不少,主要還是圍繞執行sql這里。但我這里都失敗了,就簡單的提一下。

第一種方法:

站點管理-附件設置-圖片附件設置-支持文件后綴,任意添加一個類型,例如添加pppppp

然后執行sql語句

UPDATE ims_core_settings SET value = replace(value, 'pppppp', 'php ') 

更新緩存,之后就可以上傳"*.php "文件了。但是有限制,適用於apache下,而且版本有限制。目標站不使用該方法的原因有二,一是該系統上傳的位置是騰訊雲COS上,二是server是Tengine。

第二種方法:

第二種方法也是和sql執行有關,利用日志文件寫shell。

show variables like '%general%'; #查看配置 set global general_log = on; #開啟general log模式 set global general_log_file = '/var/www/html/1.php'; #設置日志目錄為shell地址 select '<?php eval($_POST[cmd]);?>' #寫入shell 

或者通過慢查詢(slow_query_log)的方法寫shell。但目標系統也是失敗,執行sql的時候報錯。

還有一些其他的方法,這里測試也是失敗的,就不再列舉了。

0x03 代碼審計

病急亂投醫,熬成老中醫。既然之前的方法不管用,只好去翻代碼吧,找找是否有新的利用方式。翻出之前的一個文檔,從里面找到之前的審計過程,看能否對現在有用。結果打開發現只有一個數據包和還有一句未實現的結論。

沒辦法,只好重新圍着這個點繼續審計,看是否能有所進展。

1.分析

打開文件web/source/cloud/dock.ctrl.php,找到執行的download方法。

代碼比較簡單,我大概說一下這里的流程:

如果請求包非Base64加密的格式,那么$data就是請求包的內容。然后對$data進行發序列化返回$ret,接下來獲取$ret['file']並Base64解密返回$file。當存在gzcompressgzuncompress這兩個函數時,就會利用gzuncompress函數對$file進行解壓操作。

將獲取的$file進行md5加密后,與$ret['path']以及獲取的$_W['setting']['site']['token']進行拼接為$string。當滿足$_W['setting']['site']['token']非空並且$stringmd5加密后的結果與$ret['sign']一致時,才可以進行下面的操作。下面就是文件的寫入了,根據$ret['path']進行判斷,然后寫入的位置不一樣。

這里關鍵的一點就是$_W['setting']['site']['token']這個值的獲取。這個是利用authcode函數對cache_load(cache_system_key('cloud_transtoken'))進行解密獲取的。

authcode函數位於framework/function/global.func.php文件中。

由上面代碼可以看出,要想使用authcode加解密,需要知道$GLOBALS['_W']['config']['setting']['authkey'],在上面提到過,authkey在data/config.php文件中。

那么如果想任意寫文件,就需要知道cache_system_key('cloud_transtoken')的內容了。

2.cloud_transtoken的獲取

通過搜索發現,這個值是在文件framework/model/cloud.mod.php中的cloud_build_transtoken函數中被寫入的,通過進入cache_write方法,發現會寫入數據庫中。

既然會寫入到數據庫中,而且目標系統下載到時候有數據庫的備份文件,我們直接在數據庫備份文件中搜索cloud_transtoken。結果並沒有找到,可能原因是沒有寫入cloud_transtoken的時候就進行了數據庫備份。

我們往上回溯,看哪里調用了cloud_build_transtoken

發現了其中的一條利用鏈:

當訪問http://ip:port/web/index.php?c=cloud&a=profile 時,就會判斷站點ID和通信密鑰是否為空(即站點是否注冊),如果站點注冊了,就會調用cloud_site_info()函數獲取站點信息。函數cloud_site_info()調用了cloud_api('site/info'),這里的method為site/info,所以繼續調用cloud_build_transtoken從會而將cloud_transtoken的內容寫入數據庫。然后通過數據庫備份的功能,就可以看到數據庫中保存的cloud_transtoken,進而可以利用之前的分析寫shell。

3.自定義數據庫備份

由於數據庫備份需要關閉站點,為了不影響目標站點的使用,這里我們搭建一個環境演示一下過程(需要注冊站點)。

登錄成功后更新緩存,然后訪問http://ip:port/web/index.php?c=cloud&a=profile ,關閉站點后進行數據庫備份。

發現可以獲取cloud_transtoken,但是數據庫目錄和文件的名字是隨機的。

而且如果備份文件里面的數據庫文件不是最新的,那么即使獲取到cloud_transtoken也無法利用,我們需要最新的備份文件。

然后我們看一下數據庫備份是怎么實現的,打開web/source/system/database.ctrl.php

發現文件夾和分卷名可以自定義,如果為空或不滿足條件的話,文件夾是時間戳、下划線和8位隨機字符串的拼接,分卷名是volume-10位隨機字符串-1.sql的形式,既然可以自定義,那么就簡單多了。

訪問鏈接http://ip:port/web/index.php?c=system&a=database&do=backup&status=1&start=2&folder_suffix=123&volume_suffix=456 進行數據庫備份,則數據庫備份文件的地址為:http://ip:port/data/backup/123/volume-456-1.sql

然后就可以隨時獲取cloud_transtoken了。接下來就可以進行shell的獲取了。

4.獲取WEBSHELL

根據上面的分析,cloud_transtokenauthkey已經知道了,接下來就是構造payload了。

然后請求http://ip:port/web/index.php?c=cloud&a=dock&do=download ,data為生成的payload。


可以進行任意文件的寫入,對目標系統進行測試,也成功獲取了shell。

5.延伸

上面是因為有系統文件備份,然后獲取/data/config.php中的authkey。如果沒有文件備份,登錄了一個管理員權限的用戶,能否獲取shell呢。答案也是可以的。

該系統有一個木馬查殺功能,可以根據這個功能讀取文件內容。

選擇一個目錄,然后提交並攔截數據庫包,修改查殺目錄為data/.,特征函數為password。然后就可以看到查殺結果,獲取authkey的值。

在對最新版 v2.5.7(202002140001)進行木馬查殺的時候,可以從查殺報告中看到該文件,但是查看時提示文件不存在。原因是最新版利用正則對文件路徑進行匹配,如果匹配成功就提示文件不存在(windows下可以利用大寫路徑繞過)。

0x04 總結

根據上面對分析過程,該漏洞的利用過程如下:

1.成功登錄后台,且擁有管理員權限。

2.更新緩存(非必須),訪問鏈接http://ip:port/web/index.php?c=cloud&a=profile 寫入cloud_transtoken到數據庫中。

3.關閉站點並進行使用自定義的目錄進行數據庫備份,鏈接地址:http://ip:port/web/index.php?c=system&a=database&do=backup&status=1&start=2&folder_suffix=123&volume_suffix=456 。然后下載數據庫備份,地址為:http://ip:port/data/backup/123/volume-456-1.sql (多個分卷的話文件名為volume-456-2.sql、volume-456-3.sql... ),然后找到cloud_transtoken

4.生成payload,請求http://ip:port/web/index.php?c=cloud&a=dock&do=download ,寫入shell。

總的來說,利用上述方法獲取shell需要滿足兩個條件,第一是擁有一個管理員權限的用戶,第二就是該站點注冊了雲服務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM