COMMAND

    getopt

SYSTEMS AFFECTED

    SunOS versions 5.5.1, 5.5.1_x86,  5.5, 5.5_x86, 5.4, 5.4_x86,  and
    5.3.

PROBLEM

    This text is L0pht copyright and  by his author mudge.  Text  used
    here is taken mostly from L0pht Advisory.

    The getopt(3)  function parses  options from  a program's  command
    list.   Due  to  insufficient  bounds  checking by getopt(3) while
    processing command  line arguments,  it is  possible to  overwrite
    the internal stack space of programs that use getopt(3). This  may
    allow  users  to  cause   programs  using  getopt(3)  to   execute
    arbitrary  commands  by  supplying  carefully crafted arguments to
    these programs.   If these  programs are  setuid or  setgid,  then
    these commands may be run with those privileges.

    Any  dynamically  or  statically  linked  setuid or setgid program
    that uses getopt(3) may be vulnerable.

    A buffer overflow  condition exists in  the getopt(3) routine.  By
    supplying  an  invalid  option  and  replacing  argv[0]  of a SUID
    program  that  uses  the  getopt(3)  function with the appropriate
    address  and  machine  code   instructions,  it  is  possible   to
    overwrite  the  saved  stack  frame  and  upon return(s) force the
    processor  to  execute  user  supplied  instructions with elevated
    permissions.

    While  evaluating  programs  in   the  Solaris  Operating   System
    environment it became apparent  that changing many programs  trust
    argv[0] to never  exceed a certain  length. In addition  it seemed
    as though  getopt was  simply copying  argv[0] into  a fixed  size
    character array.

        ./test >>& ccc
        Illegal instruction (core dumped)

    Knowing that the code in  ./test was overflow free it  seemed that
    the problem must exist in one of the functions dynamically  linked
    in at runtime through ld.so.  A quick gander through the  namelist
    showed a very  limited range of  choices for the  problem to exist
    in.

        00020890 B _end
        0002088c B _environ
        00010782 R _etext
                 U _exit
        00010760 ? _fini
        0001074c ? _init
        00010778 R _lib_version
        000105ac T _start
                 U atexit
        0002088c W environ
                 U exit
        0001067c t fini_dummy
        0002087c d force_to_data
        0002087c d force_to_data
        000106e4 t gcc2_compiled.
        00010620 t gcc2_compiled.
                 U getopt
        00010740 t init_dummy
        00010688 T main

    Next we checked out getopt() -  as it looked like the most  likely
    suspect.

        #include <stdio.h>

        main(int argc, char **argv)
        {
          int opt;

          while ((opt = getopt(argc, argv, "a")) != EOF) {
            switch (opt) {
            }
         }
        }

        >gcc -o test test.c
        >./test -z
        ./test: illegal option -- z

    Note the name it threw back at the beggining of the error message.
    It was quite obvious that they are just yanking argv[0].  Changing
    argv[0] in the test program confirms this.

        for (i=0; i<4096; i++)
          buffer[i] = 0x41;

         argv[0] = buffer;

    With the above in place we see the following result:

        >./test -z
        [lot's of A's removed]AAAAAAAAA: illegal option -- z
        Bus error (core dumped)

    By yanking out the object  file from the static archive  libc that
    is  supplied  with  Solaris  our  culprit  was  spotted [note - we
    assumed  that  libc.a  was  built  from  the  same  code base that
    libc.so was].

        > nm getopt.o
                 U _dgettext
        00000000 T _getopt
        00000000 D _sp
                 U _write
        00000000 W getopt
                 U optarg
                 U opterr
                 U optind
                 U optopt
                 U sprintf
                 U strchr
                 U strcmp
                 U strlen

    Here  we  see  one  of  the infamous non-bounds-checking routines:
    sprintf();  More  than  likely  the  code  inside  getopt.c  looks
    something like the following:

        getopt.c:
          char opterr[SOMESIZE];
          ...
          sprintf(opterr, argv[0]...);

    Thus, whenever  you pass  in a  non-existant option  to a  program
    that uses getopt you run into the potential problem with  trusting
    that argv[0]  is smaller  than the  space that  has been allocated
    for opterr[].

    This  is  interesting  on  the  Sparc  architecture as getopt() is
    usually called  out of  main() and  you need  two returns  [note -
    there are certain situations  in code on Sparc  architectures that
    allow you  to switch  execution to  your own  code without needing
    two returns.  Take a look  at the TBR for some enjoyable  hacking]
    due to the sliding register  windows. Some quick analysis of  SUID
    programs on  a standard  Solaris 2.5  box show  that most of these
    programs exit() or  more likely call  some form of  usage()-exit()
    in  the  default  case  for  getopt  and thus are not exploitable.
    However,  at  least  two  of  these programs provide the necessary
    returns to throw your address into the PC :  passwd(1) login(1)

    On Solaris X86  you do not  need these double  returns and thus  a
    whole world  of SUID  programs allow  unpriveledged users  to gain
    root access:   (list of programs  vulnerable too big  to put here.
    sigh.)

    Exploit:

        $./exploit "/bin/passwd" 4375 2> foo
        # id
        uid=0(root) gid=1(other)

    Note: the source  code for the  exploit will be  made available on
    the  www.l0pht.com/advisories.html  page  in  a  couple  of  days.
    As soon as this exploit will  be exposed to public, I will  putted
    here (crv).

    Special thanks go out to  ][ceman for his co-work on  this project
    and mudge, the author.

SOLUTION

    The vulnerability relating to getopt(3) is fixed by the  following
    patches:

        OS version      Patch ID
        ----------      --------
        SunOS 5.5.1     103612-23
        SunOS 5.5.1_x86 103613-23
        SunOS 5.5       103187-25
        SunOS 5.5_x86   103188-25
        SunOS 5.4       101945-49
        SunOS 5.4_x86   101946-43
        SunOS 5.3       101318-87

    Patches listed are available to  all Sun customers via World  Wide
    Web at:

        ftp://sunsolve1.sun.com/pub/patches/patches.html