home *** CD-ROM | disk | FTP | other *** search
- L0pht Security Advisory
- Advisory released Jan 27 1997
-
- Application: Solaris libc getopt(3)
-
- Vulnerability Scope: Solaris 2.5 distributions
-
- Severity: Non-priveledged users can exploit a vulnerability
- in the getopt(3) routine inside libc. As most SUID programs
- in Solaris are dynamically linked, users can gain root
- priveledges.
-
- Author: mudge@l0pht.com
-
- Overview:
-
- 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.
-
-
- Description:
-
- 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. Hey, we have
- day jobs and sometimes spare time is impossible to come by. ]
-
- Fixes:
- For those with source:
- If you are one of the few people who have a source code license the fix
- should be fairly simple. Replace the sprintf() routine in getopt.c with
- snprintf() and rebuld libc.
-
- 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.
-
- Thanks:
- Special thanks go out to ][ceman for his co-work on this project.
-
- mudge@l0pht.com
-
- ---
- Check out http://www.l0pht.com/advisories.html for other l0pht advisories
- ---
-
-