oss-sec mailing list archives

Re: Nginx (Debian-based + Gentoo distros) - Root Privilege Escalation [CVE-2016-1247 UPDATE]


From: Daniel Kahn Gillmor <dkg () fifthhorseman net>
Date: Fri, 13 Jan 2017 10:03:48 -0500

On Fri 2017-01-13 09:00:36 -0500, Carlos Alberto Lopez Perez wrote:
On 13/01/17 10:35, Dawid Golunski wrote:
Attackers who have managed to replace the log file with a symlink would
have to wait for nginx daemon to re-open the log files. 
For this to happen nginx service needs to be restarted, or the daemon needs
to receive a USR1 process signal. 

However, the USR1 is sent automatically on default installations of 
Debian-based systems through logrotate script which calls do_rotate() 
function as can be seen in the files quoted below:


--------[ /etc/logrotate.d/nginx ]--------

/var/log/nginx/*.log {
     daily
     missingok
     rotate 52
     compress
     delaycompress
     notifempty
     create 0640 www-data adm
     sharedscripts
     prerotate
             if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                     run-parts /etc/logrotate.d/httpd-prerotate; \
             fi \
     endscript
     postrotate
             invoke-rc.d nginx rotate >/dev/null 2>&1
     endscript
}

------------------------------------------

This looks to me like an issue on the logrotate side rather than on the nginx one..

I agree that this looks like a flaw in logrotate, but there may also be
flaws in nginx.  The only part of nginx that's being used is the
"rotate" subcommand of /etc/init.d/nginx , which just sends USR1 to the
running daemon.

the nginx master process is running still running as the root user
(presumably to do things like bind to ports 443 and 80), and that's the
process that gets USR1, so it could potentially be at risk here too.

If I have:

/var/log/nginx/error.log -> /etc/ld.so.preload

Why does logrotate "create 0640 www-data adm" over /var/log/nginx/error.log
removes and creates /etc/ld.so.preload ??? That is shocking!

It should do that on /var/log/nginx/error.log, by removing that symlink
and creating a new empty standard file on /var/log/nginx/error.log !!

Dont you agree??

I'm not sure whether this is the right thing to do -- perhaps the right
thing for logrotate to do is to notice that this is a weird case and
fail with an error.

This is more evidence that having an entirely separate daemon manage
logfiles (e.g. svlogd from the runit suite, or journald from systemd) is
a better system design than trying to teach every daemon how to manage
its own logfiles sanely.  If the main nginx daemon did not retain root
privs, then it *couldn't* mount the attack described here.

So the other question left is why nginx needs root privs in the first
place.  If the only reason is "privileged ports", that's a terrible
reason.  One amelioration would be to ship the binary with
CAP_NET_BIND_SERVICE and make it only executable by the www-data user.

Another approach would be to use socket activation, where the service
supervisor opens the privileged ports and hands them off to the running
child process which has no special privileges at all.

This would result in less code for the daemon: no socket opening, no
logfile management; and less ways that it could break.

   --dkg

Attachment: signature.asc
Description:


Current thread: