[5] in Best-of-Security
BoS: Re: L0pht Advisory: Solaris libc - getopt(3)
daemon@ATHENA.MIT.EDU (Adam Morrison)
Tue Jan 28 16:50:07 1997
Date: Tue, 28 Jan 1997 19:17:32 +0200
Reply-To: Adam Morrison <adam@MATH.TAU.AC.IL>
From: Adam Morrison <adam@MATH.TAU.AC.IL>
In-Reply-To: <Pine.BSI.3.95.970126221614.9974D-100000@l0pht.com> from
"owner-bugtraq@NETSPACE.ORG" at Jan 26, 97 10:17:26 pm
Errors-To: best-of-security-request@suburbia.net
To: best-of-security@suburbia.net
Resent-From: best-of-security@suburbia.net
> Super Ugly kludge fix:
> If you don't have the source code available (like most of us), one solution
> is to use adb to change the name for getopt with something like getopz,
> yank a publicly available getopt.c, and put it in place of getopt.
> If anyone can tell me how to yank the object files out of dynamically
> linked libraries it would be appreciated as you suffer performance
> hits among larger problems by doing this from the static library Sun
> provides as, of course, it is not PIC code.
>
If you want to rebuild libc, a PIC version exists in /usr/lib/pics.
$ ar x /usr/lib/pics/libc_pic.a
$ rm getopt.o
$ cc -K pic -c getopt.c
$ cc -o libc.so.1 -h libc.so.1 -G \
-Wl,-f,'/usr/platform/$PLATFORM/lib/libc_psr.so.1' \
`lorder *.o | tsort 2>/dev/null` -ldl
$ su
Password:
# /usr/sbin/static/mv /usr/lib/libc.so.1 /usr/lib/libinsecure-c.so.1
# /usr/sbin/static/cp libc.so.1 /usr/lib
Alternatively, here's a trick I've been using on other library holes where
source replacements weren't so easy to come by. The usual disclaimers of how
this might render your system unbootable apply. Also note the example below
uses the Sun cc compiler, not gcc.
Thanks to the zany Sun linker, it's possible to interpose on insecure library
functions. You can easily see how this example applies to similar holes,
ie the gethostbyname() resolver hole. (I should also note that as with all
oh-so-technically advanced buffer overflows, the exploit for, say, passwd(1)
is a short diff to the exploit Jeremy Elson posted here recently.)
PS: I would love to hear of a less fidgety way of doing this.
First, create a stub libc.
# whoami
root
# >stub.c
# ls -l stub.c
-rw-r--r-- 1 root wheel 0 Jan 28 15:04 stub.c
# mkdir /usr/lib/insecure
# cc -o stub -K pic -G -h /usr/lib/insecure/libc.so.1 stub.c
"c.c", line 1: warning: empty translation unit
# dump -Lv stub | grep SONAME
[3] SONAME /usr/lib/insecure/libc.so.1
# cp stub /usr/lib/insecure/libc.so.1
Plug the hole.
# cat getopt.c
#include <stdlib.h>
#include <dlfcn.h>
int
getopt(int argc, char *const *argv, const char *args)
{
int (*real_getopt)(int, char *const *, const char *) = dlsym(RTLD_NEXT, "getopt");
int av0len;
if (argv && argv[0]) {
av0len = strlen(argv[0]);
if (av0len > 256)
argv[0][255] = '\0';
}
if (real_getopt)
return ((*real_getopt)(argc, argv, args));
else
return (-1);
}
Replace the insecure libc; if you mess this up you may need to reboot from
CDROM or the network and restore the damage.
# cc -K pic -G -h libc.so.1 getopt.c /usr/lib/insecure/libc.so.1
"getopt.c", line 10: warning: assignment type mismatch:
pointer to function(int, pointer to const pointer to char,
pointer to const char) returning int "=" pointer to void
# ls -l a.out
-rwxr-xr-x 1 root wheel 4928 Jan 28 15:06 a.out
# dump -Lv a.out | grep NEEDED
[1] NEEDED /usr/lib/insecure/libc.so.1
# /usr/sbin/static/mv /usr/lib/libc.so.1 /usr/lib/insecure
# /usr/sbin/static/cp a.out /usr/lib/libc.so.1
# rm /usr/lib/libc.so
# ln -s /usr/lib/insecure/libc.so.1 /usr/lib/libc.so
If you ever want to back this out, use the static mv to move
/usr/lib/insecure/libc.so.1 to /usr/lib and make the symlink /usr/lib/libc.so
point to it.
Verify that dynamically linked executables work and that the dependencies
are in order.
# pwd
/tmp/foo
# ldd /usr/lib/libc.so.1
/usr/lib/insecure/libc.so.1
libdl.so.1 => /usr/lib/libdl.so.1
# ldd /bin/ls
libw.so.1 => /usr/lib/libw.so.1
libintl.so.1 => /usr/lib/libintl.so.1
libc.so.1 => /usr/lib/libc.so.1
libdl.so.1 => /usr/lib/libdl.so.1
/usr/lib/insecure/libc.so.1
Et voila.
% cat getopt-test.c
#include <stdio.h>
main(int argc, char **argv)
{
char buf[4096];
memset(buf, 'A', 4096);
buf[4095] = '\0';
argv[0] = buf;
(void) getopt(argc, argv, "a");
}
% cc getopt-test.c
% ./a.out -x
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: illegal option --
x
% ./a.out -x |& sed 's/:.*$//' | wc -c
256
adam?