[5] in Best-of-Security

home help back first fref pref prev next nref lref last post

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?


home help back first fref pref prev next nref lref last post