[LWN Logo]
[Timeline]
Date:         Tue, 19 Sep 2000 09:06:30 +0400
From: Solar Designer <solar@FALSE.COM>
Subject:      Re: [RHSA-2000:061-02] syslog format vulnerability in klogd
To: BUGTRAQ@SECURITYFOCUS.COM

> Various vulnerabilities exist in syslogd/klogd. By exploiting these
> vulnerabilities, it could be possible for local users to gain root
> access. No remote exploit exists at this time, but it remains
> theoretically possible that this vulnerability could be exploited
> remotely under certain rare circumstances.

The Linux vendor updates and sysklogd 1.4 should be fixing three
kinds of vulnerabilities:

1. klogd "format bugs" found by Jouko Pynnönen.  (Note that the
"obvious" fix for this would break the decoding of priorities.)

2. A Linux/sysklogd-specific syslogd bug reported to Debian and fixed
by Daniel Jacobowitz:

"There was a silly logic error in printchopped() that caused it to zero out
the wrong portion of the buffer, leaving a message of "<", which had
priority 0.  That caused a kern.emerg log, which is by default walled to all
users.  Oops."

see http://bugs.debian.org/32580 for the full story.

3. Two syslogd printline() bugs found by me, see below.

Patches for just these security bugs can be obtained here:

ftp://ftp.openwall.com/pvt/sysklogd-1.3-31-security-patches.tar.gz

The printline() problems are not specific to the sysklogd package.
Fortunately, they're relatively minor.

Below are some quotes from the message I sent to the vendor-sec list,
*BSD security contacts, and the major commercial Unix vendors.

(Is there an e-mail security contact for SCO?)

| This is to report several bugs found in the printline() function in
| implementations of syslogd derived from Eric Allman's.
|
| I don't know if all of the vendors I'm sending this to are affected.
| Please, treat this as an opportunity to check your operating system
| for this kind of problems.

Some of the vendors have replied (even though I didn't explicitly ask
for a reply), so I'll summarize the responses I've got:

Jeff Polk of BSDI replied saying that "BSDI fixed this in the BSD/OS
tree in May of 1996.  No supported releases of BSD/OS are vulnerable."

Compaq has replied that the problem "has not been found to affect the
as shipped, Compaq Tru64/UNIX Operating Systems Software".

SGI replied that they've filed a bug id and are investigating IRIX.

[...]

| Below is information on the (free) operating systems that I was able
| to check:
|
| * OpenBSD, NetBSD, FreeBSD-stable, CORE-SDI's ssyslog 1.22:
|
| Two bugs in printline():
|
| 1. Single-byte buffer overflow (may be caused to write a NUL beyond
| the end of line[]).
|
| 2. Will truncate lines at the first '\200' character.

OpenBSD and NetBSD have now fixed the bugs.

CORE-SDI's new msyslog v1.0 has the buffer overflow fixed, but not
the '\200' bug, yet.

The '\200' bug may allow an attacker to partially avoid logging in
certain cases, such as with a syslog(3) call like this:

syslog(LOG_INFO, "%s of %s tried to login as %s",
	remote_user, remote_host, target_user);

| * sysklogd 1.3-31, as used on most current Linux distributions:
|
| Two bugs in printline():
|
| 1. Single-byte buffer overflow.
|
| 2. Does not escape control characters '\177' through '\237'.

These have now been fixed.

| * FreeBSD-current:
|
| One bug in printline(), which results in:
|
| 1. Single-byte buffer overflow (which was fixed and re-introduced
| with the new bug).
|
| 2. Control characters '\200' through '\237' are escaped differently
| than intended (as a sequence of 4 characters rather than 3).
|
| In addition to this, the ctype macros used in some implementations of
| printline() may produce undefined results for 8-bit characters on some
| older platforms (with non-ANSI C libraries).
|
| Fortunately, the buffer overflow is very likely non-exploitable.  The
| line[] buffer is declared to be 1025 bytes large (this is MAXLINE + 1)
| and other variables local to the function are either unimportant or
| require at least a 32-bit alignment.  Thus, in my testing line[1025]
| was pointing to unused stack space needed to achieve the alignment.
| Of course, a compiler may not guarantee to align the buffer itself
| (it is only the integer and pointer variables that are guaranteed to
| be aligned on most, but not all, platforms), in which case it may be
| placed right below one of the other variables allowing for attacks.
|
| Finally, the proposed patch for printline(), as found in the sysklogd
| package.  The patch may easily be ported to other syslogd clones.  It
| is important to pay attention to the variable declarations: "c" should
| be an "unsigned char".

diff -rU 27 sysklogd-1.3-31.orig/syslogd.c sysklogd-1.3-31/syslogd.c
--- sysklogd-1.3-31.orig/syslogd.c	Tue Jan 19 14:31:58 1999
+++ sysklogd-1.3-31/syslogd.c	Wed Sep 13 00:45:55 2000
@@ -1419,60 +1419,65 @@
 void printline(hname, msg)
 	const char *hname;
 	char *msg;
 {
 	register char *p, *q;
 	register unsigned char c;
 	char line[MAXLINE + 1];
 	int pri;

 	/* test for special codes */
 	pri = DEFUPRI;
 	p = msg;

 	if (*p == '<') {
 		pri = 0;
 		while (isdigit(*++p))
 		{
 		   pri = 10 * pri + (*p - '0');
 		}
 		if (*p == '>')
 			++p;
 	}
 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
 		pri = DEFUPRI;

 	memset (line, 0, sizeof(line));
 	q = line;
-	while ((c = *p++) && q < &line[sizeof(line) - 1]) {
+	while ((c = *p++) && q < &line[sizeof(line) - 4]) {
 		if (c == '\n')
 			*q++ = ' ';
-		else if (iscntrl(c)&&(c<0177)) {
+		else if (c < 040) {
 			*q++ = '^';
 			*q++ = c ^ 0100;
+		} else if (c == 0177 || (c & 0177) < 040) {
+			*q++ = '\\';
+			*q++ = '0' + ((c & 0300) >> 6);
+			*q++ = '0' + ((c & 0070) >> 3);
+			*q++ = '0' + (c & 0007);
 		} else
 			*q++ = c;
 	}
 	*q = '\0';

 	logmsg(pri, line, hname, SYNC_FILE);
 	return;
 }



 /*
  * Take a raw input line from /dev/klog, split and format similar to syslog().
  */

 void printsys(msg)
 	char *msg;
 {
 	register char *p, *q;
 	register int c;
 	char line[MAXLINE + 1];
 	int pri, flags;
 	char *lp;

 	(void) snprintf(line, sizeof(line), "vmunix: ");
 	lp = line + strlen(line);
 	for (p = msg; *p != '\0'; ) {