PATCH to BIND-8.2.3 to get rid of the, unnecessary, and potentially dangerous fchown() calls

From: Greg A. Woods (woodsat_private)
Date: Sat Sep 08 2001 - 11:56:24 PDT

  • Next message: SeungHyun Seo: "Digital Unix 4.0x msgchk multiple vulnerabilities"

    [[ note this posting is CC'ed to BUGTRAQ.  I know of no current exploits
    in BIND-8.2.3, but even so since I'm also enclosing a fix there may be
    quite a few people who might want to be able to fix their own versions. ]]
    
    The so-called "support" fix in change 999 of BIND-8.2.3 introduces some
    unnecessary, and potentially very dangerous fchown() calls to named.
    
    The worst one is the one that leaves the pid-file writable by the
    supposedly untrusted user named has been asked to run as.
    
    The pid-file does not need to be owned, or indeed accessible in any way,
    by the user named is running as.  If named is to be restarted through
    any means but exec(), when it is running with low privileges, then it
    must be restarted by root anyway!
    
    One of the problems with leaving the pid-file writable by the
    unprivileged user is of course that on many systems there are vendor
    supplied (and sometimes home-grown) administrative control scripts
    commonly used to manage daemon processes.  These scripts use the pid
    stored in the pid-file to stop the daemon, trigger it to reload its
    config, etc.  If the pid-file is writable by the user named is running
    as then it is vulnerable should any remote exploit be found for named.
    This means a successful remote exploit of named could be used to trick
    the sys-admin into killing arbitrary processes, and indeed maybe even
    crashing the system.  Some of these scripts naively put at least the
    first whole line, and sometimes even the entire contents, from the
    pid-file on the command-line they use to invoke kill.  The potential for
    danger is probably even higher for these scripts.  [Note that asking the
    vendor to fix their scripts is silly and pointless -- there's no way for
    the script to know intuitively whether a PID has the "right" value
    because if they did then they wouldn't need the pid-file in the first
    place, and indeed these scripts rightfully assume that only a privileged
    user could have created the pid-file in the first place!]
    
    Another problem is that the pid-file may be located on a very important
    file system, and even with low privileges a vulnerability in named could
    allow the attacker to fill that file system.
    
    There's also a call to change the ownership of any logging file (which
    would only occur if named is explicitly configured to write to its own
    log files instead of using syslog).  I don't know yet whether this
    happens before named drops its privileges or not, but if so then the
    fchown() results in leaving the log file writable by any successful
    attacker!
    
    The other calls to fchown() are probably just unnecessary and pointless
    since they seem occur only after the file has just been newly created,
    and if my quick analysis is correct only ever after named has
    permanently given up root privileges too (though my analysis of this
    aspect so far is rather incomplete).
    
    If named is intentionally running as root (though I can't imagine why
    anyone would still be doing that, except out of laziness) then these
    calls will do nothing if the file did not previously exist, but if the
    file did exist then these calls will change its ownership to be root,
    which is potentially dangerous because although fchown() is used no
    checks are made to ensure the file that was opened is indeed freshly
    created and/or not tricked into pointing at some other existing file by
    a symlink.  Yes normally anyone with any concern about security will
    have pointed named into a private directory that's intended to be
    writable only by the unprivileged user named is normally expected to run
    as.  However that very same user is the one which would be compromised
    by a remote vulnerability, and as such there still no protection against
    a rogue symlink being created by a successful attacker.
    
    If named is not running as root (as it should not ever be!) then these
    calls cannot likely ever do anything (since unlike the pid-file call,
    all happen, as far as I can tell with a quick analysis, after named has
    relinquished its privileges, so are pointless and unnecessary (which is
    perhaps indicated implicitly by the way they are not checked for
    failures).
    
    Note the same problem occurs, but in a much more insidious way, in
    BIND-9.  I do not yet have a patch to fix BIND-9 [I'm not running it in
    production anywhere yet].
    
    -- 
    							Greg A. Woods
    
    +1 416 218-0098      VE3TCP      <gwoodsat_private>     <woodsat_private>
    Planix, Inc. <woodsat_private>;   Secrets of the Weird <woodsat_private>
    
    
    Index: src/bin/named/ns_config.c
    ===================================================================
    RCS file: /cvs/misc/bind8/src/bin/named/ns_config.c,v
    retrieving revision 1.1.1.5
    diff -c -r1.1.1.5 ns_config.c
    *** src/bin/named/ns_config.c	31 Jan 2001 21:03:33 -0000	1.1.1.5
    --- src/bin/named/ns_config.c	8 Sep 2001 03:40:31 -0000
    ***************
    *** 1454,1460 ****
    --- 1454,1462 ----
      		  S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
      	if (fd < 0)
      		return (NULL);
    + #if 0 /* ARGH!!!!  Making the pid file writable by user_id is a major security hole!!!! */
      	(void) fchown(fd, user_id, group_id);
    + #endif
      	stream = fdopen(fd, "w");
      	if (stream == NULL)
      		(void)close(fd);
    Index: src/bin/named/ns_main.c
    ===================================================================
    RCS file: /cvs/misc/bind8/src/bin/named/ns_main.c,v
    retrieving revision 1.1.1.5
    diff -c -r1.1.1.5 ns_main.c
    *** src/bin/named/ns_main.c	31 Jan 2001 21:03:37 -0000	1.1.1.5
    --- src/bin/named/ns_main.c	8 Sep 2001 03:29:07 -0000
    ***************
    *** 621,627 ****
    --- 621,629 ----
      			return;
      		case EBADF:
      		case ENOTSOCK:
    + #if 1	/* Note I didn't have this one listed for some reason....  -GAW */
      		case EFAULT:
    + #endif
      			/*
      			 * If one these happens, we're broken.
      			 */
    Index: src/bin/named/ns_maint.c
    ===================================================================
    RCS file: /cvs/misc/bind8/src/bin/named/ns_maint.c,v
    retrieving revision 1.1.1.4
    diff -c -r1.1.1.4 ns_maint.c
    *** src/bin/named/ns_maint.c	31 Jan 2001 21:03:37 -0000	1.1.1.4
    --- src/bin/named/ns_maint.c	8 Sep 2001 18:30:52 -0000
    ***************
    *** 667,673 ****
    --- 667,675 ----
      				   name);
      			return(-1);
      		}
    + #if 0 /* ARGH!!!  this one's totally unnecessary given the file is guaranteed brand new!!! */
      		(void) fchown(tsig_fd, user_id, group_id);
    + #endif
      	}
      
      	memset(secret_buf, 0, sizeof(secret_buf));
    Index: src/bin/named/ns_stats.c
    ===================================================================
    RCS file: /cvs/misc/bind8/src/bin/named/ns_stats.c,v
    retrieving revision 1.1.1.4
    diff -c -r1.1.1.4 ns_stats.c
    *** src/bin/named/ns_stats.c	31 Jan 2001 21:03:41 -0000	1.1.1.4
    --- src/bin/named/ns_stats.c	8 Sep 2001 03:49:45 -0000
    ***************
    *** 122,128 ****
    --- 122,130 ----
      			  server_options->stats_filename);
      	}
      	if (f != NULL) {
    + #if 0 /* ARGH!!!! */
      		(void) fchown(fileno(f), user_id, group_id);
    + #endif
      		fprintf(f, "+++ Host Statistics Cleared +++ (%ld) %s",
      			(long)timenow, checked_ctime(&timenow));
      		(void) my_fclose(f);
    ***************
    *** 143,149 ****
    --- 145,153 ----
      			  server_options->stats_filename);
      		return;
      	}
    + #if 0 /* ARGH!!!! */
      	(void) fchown(fileno(f), user_id, group_id);
    + #endif
      
      	fprintf(f, "+++ Statistics Dump +++ (%ld) %s",
      		(long)timenow, checked_ctime(&timenow));
    ***************
    *** 170,176 ****
    --- 174,182 ----
      			  server_options->memstats_filename);
      		return;
      	}
    + #if 0 /* ARGH!!!! */
      	(void) fchown(fileno(f), user_id, group_id);
    + #endif
      
      	fprintf(f, "+++ Memory Statistics Dump +++ (%ld) %s",
      		(long)timenow, checked_ctime(&timenow));
    Index: src/bin/named/ns_update.c
    ===================================================================
    RCS file: /cvs/misc/bind8/src/bin/named/ns_update.c,v
    retrieving revision 1.1.1.4
    diff -c -r1.1.1.4 ns_update.c
    *** src/bin/named/ns_update.c	31 Jan 2001 21:03:41 -0000	1.1.1.4
    --- src/bin/named/ns_update.c	8 Sep 2001 03:50:10 -0000
    ***************
    *** 145,151 ****
    --- 145,153 ----
      			 strerror(errno));
      		return (NULL);
      	}
    + #if 0 /* ARGH!!!! */
      	(void) fchown(fileno(fp), user_id, group_id);
    + #endif
      	if (fseek(fp, 0L, SEEK_END) != 0) {
      		ns_error(ns_log_update, "can't fseek(%s, 0, SEEK_END)",
      			 zp->z_updatelog);
    ***************
    *** 170,176 ****
    --- 172,180 ----
      			 strerror(errno));
      		return (NULL);
      	}
    + #if 0 /* ARGH!!!! */
      	(void) fchown(fileno(fp), user_id, group_id);
    + #endif
      	if (fseek(fp, 0L, SEEK_END) != 0) {
      		ns_error(ns_log_update, "can't fseek(%s, 0, SEEK_END)",
      			 zp->z_ixfr_base);
    Index: src/lib/isc/logging.c
    ===================================================================
    RCS file: /cvs/misc/bind8/src/lib/isc/logging.c,v
    retrieving revision 1.1.1.4
    diff -c -r1.1.1.4 logging.c
    *** src/lib/isc/logging.c	31 Jan 2001 21:04:45 -0000	1.1.1.4
    --- src/lib/isc/logging.c	8 Sep 2001 18:32:45 -0000
    ***************
    *** 156,162 ****
    --- 156,164 ----
      		chan->flags |= LOG_CHANNEL_BROKEN;
      		return (NULL);
      	}
    + #if 0 /* ARGH!!!  Don't leave the audit trail writable by the attacker!!! */
      	(void) fchown(fd, chan->out.file.owner, chan->out.file.group);
    + #endif
      
      	chan->out.file.stream = stream;
      	return (stream);
    



    This archive was generated by hypermail 2b30 : Sun Sep 09 2001 - 23:10:31 PDT