/* CC - Covert Client a demonstration on covert chanels for educational purposes Copyright (C) 2008 ithilgore - ithilgore.ryu.L@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include // linux ip header struct #include // linux tcp header struct /* DEFINES */ #define VERSION "1.0" #define NAME "CovertClient" #define TRUE 1 #define FALSE 0 /* FUNCTION PROTOTYPES */ void print_usage(void); void raw_packet_init(void); uint16_t checksum_comp(uint16_t *, int); void clean_exit(char *, int); /* DECLARATIONS */ /* option stuct - idea taken by nmap */ typedef struct options { char src[16]; /* 16 = 4*3 digits + 3 dots + 1'\0' */ char dst[16]; unsigned long int port; char *file; unsigned short int mode; unsigned short int verbose; char opt_ex; /* bitwise boolean - if an option exists corresponding bit will be 1 or else 0 -the setting takes place inside the getopt loop */ #ifdef DEBUG unsigned short int debug; #endif } options; /* this struct contains both the ip header * and tcp header - if we reversed the * order of the headers would it work ??? */ typedef struct raw_pack { struct iphdr ip; struct tcphdr tcp; } raw_pack; /* pseudo header used for checksuming */ /* this header never reaches the wire */ typedef struct pseudo_hdr { u_int32_t src; u_int32_t dst; u_char mbz; u_char proto; u_int16_t len; } pseudo_hdr; /* GLOBAL VARIABLES */ options o; /* generic exit printing func * it prints a perror msg additionally * if err is 1 */ void clean_exit(char *msg, int err) { if (err == 1) perror(msg); else fprintf(stderr, "%s \n", msg); exit(EXIT_FAILURE); } void print_usage(void) { fprintf(stdout, "%s client by ithilgore\n", NAME); fprintf(stdout, "Options: \n" "-d: destination ip \n" "-s: source ip \n" "-p: destination port \n" "-f: file name \n" "-v: verbose mode \n" "-h: help \n" "\n"); exit(EXIT_SUCCESS); } uint16_t checksum_comp(uint16_t *addr, int len) { /* compute TCP header checksum */ /* with the usual algorithm a bit changed */ /* for byte ordering problem resolving */ /* see RFC 1071 for more info */ /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ register long sum = 0; int count = len; uint16_t temp; while (count > 1) { temp = htons(*addr++); // in this line:added -> htons sum += temp; count -= 2; } /* Add left-over byte, if any */ if (count > 0) sum += *(unsigned char *) addr; /* Fold 32-bit sum to 16 bits */ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); uint16_t checksum = ~sum; return checksum; } void raw_packet_init(void) { /* this code experiments with making raw structs by using * the struct raw_pack (a combination of tcphdr and iphdr) and * using pointers which is faster compared to using plain structs * and moving them around - this technique is also * used in the syn scanner Creeper (by ithilgore) * the only new idea is using the combination of the 2 headers * into a struct called raw_pack which makes the code more readable */ /* The logic is simple: we create a buffer datagram into which the * tcp and ip headers will be stored. We take into account the additional * storage needed by the pseudo header used for the tcp checksumming process. * The raw_pack pointer points to the beginning of the datagram * and the pseudo_header will point to the end of the raw_pack (still inside * the buffer datagram). * The sendto() function will send only the portion of the datagram which * contains the raw_pack (tcp and ip headers ) - the pseudo_header never reaches * the wire */ char datagram[sizeof(raw_pack) + sizeof(pseudo_hdr)]; /* buffer for the headers */ raw_pack *raw = (struct raw_pack *) datagram; /* point the raw_pack to the datagram */ pseudo_hdr *phdr; /* pseudo header pointer */ FILE *input; /* file pointer */ int ch; /* buffer storing input from file */ int sockfd; /* raw socket descriptor */ unsigned int dst, src; /* integers used for filling in the addresses with inet_pton() */ struct sockaddr_in sin; /* struct used for the raw socket info */ if ((input = fopen(o.file, "rb")) == NULL) { fprintf(stderr, "file %s cannot be opened for reading\n", o.file); exit(EXIT_FAILURE); } memset(datagram, 0, sizeof(datagram)); /* bzero the datagram */ /* convert strings to network ints */ if (inet_pton(AF_INET, o.dst, (unsigned int *) &dst) < 0) clean_exit("invalid source addr", 0); if (inet_pton(AF_INET, o.src, (unsigned int *) &src) < 0) clean_exit("invalid dest addr", 0); /* main raw packet building loop */ while ((ch = fgetc(input)) != EOF) { sleep(1); // TODO: optimal time needed here /* raw packet creation */ /* ip header construction */ /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identification |Flags| Fragment Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time to Live | Protocol | Header Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /* all the values that are over 1 octer need to be network byte ordered */ raw->ip.ihl = 5; raw->ip.version = 4; raw->ip.tos = 0; raw->ip.tot_len = htons(40); /* 16 byte value */ raw->ip.frag_off = 0; /* no fragment */ raw->ip.ttl = 64; /* default value */ raw->ip.protocol = IPPROTO_TCP; /* protocol at L4 */ raw->ip.check = 0; /* ??not needed in iphdr */ raw->ip.saddr = (src); raw->ip.daddr = (dst); /* There was a confusion with using the htonl function on the * ip addresses: if the addresses are already converted to network- * byte-order (which they are because of the inet_pton() called before) * then calling htonl on them will bring the opposite results, which * means that the address will be converted to host byte order causing * havoc. In addition if htonl() is called twice on the same network- * byte-order address the addr won't be converted back to a network byte addr * as seemingly expected ( following a 2 negatives make 1 positive logic ). * Now i am beginning to understand Hobbit's ranting about the bsd sockets * api ..... raw->ip.saddr = htonl(src); raw->ip.daddr = htonl(dst); ^ IT WONT WORK if already in network byte order */ /* tcp header construction */ /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ raw->tcp.source = htons( 1 + (int) (10000.0 * rand() / (RAND_MAX + 1.0)) ); raw->tcp.dest = htons(o.port); raw->tcp.seq = ch; /* we encode the data in the seq */ raw->tcp.ack_seq = 0; raw->tcp.res1 = 0; /* reserved bits */ raw->tcp.doff = 5; /* header length (counted in 32 bit words) */ raw->tcp.fin = 0; raw->tcp.syn = 1; raw->tcp.rst = 0; raw->tcp.psh = 0; raw->tcp.ack = 0; raw->tcp.urg = 0; raw->tcp.window = htons(512); raw->tcp.check = 0; raw->tcp.urg_ptr = 0; /* fill the socket struct */ sin.sin_family = AF_INET; sin.sin_port = raw->tcp.source; sin.sin_addr.s_addr = raw->ip.daddr; /* make a raw socket */ if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) clean_exit("cannot open socket", 1); /* ip header checksum */ raw->ip.check = htons (checksum_comp((unsigned short *) &(raw->ip), 20)); // TODO: some hosts respond with incorrect checksum ??? /* pseudo header used for checksumming */ phdr = (struct pseudo_hdr *) (datagram + sizeof(raw_pack)); phdr->src = raw->ip.saddr; phdr->dst = raw->ip.daddr; phdr->mbz = 0; phdr->proto = IPPROTO_TCP; phdr->len = ntohs(0x14); /* tcp checksum */ raw->tcp.check = htons (checksum_comp( (unsigned short *) &(raw->tcp), sizeof(raw->tcp) + sizeof(pseudo_hdr) ) ); /* do u like the above indendentation ?? either way, i don't care */ /* send the raw packet */ int err = sendto(sockfd, datagram, sizeof(raw_pack), 0, (struct sockaddr *)&sin, sizeof(sin)); if (err < 0) clean_exit("sendto error: ", 1); #ifdef DEBUG if (o.debug) fprintf(stderr, "bytes send by sendto(): %d \n", err); #endif fprintf(stdout, "Sending Data: %c\n", ch); close(sockfd); } fclose(input); } /****************** MAIN PROGRAM **************************/ int main(int argc, char **argv) { if (argc == 1) { print_usage(); exit(EXIT_SUCCESS); } /* option parsing */ int opt; while ((opt = getopt(argc, argv, "d:s:p:f:vhD")) != -1) { switch (opt) { case 'd': /* destination address */ strncpy(o.dst, optarg, sizeof(o.dst)); // if the address is less than 15 chars strncpy pads the dest with nulls o.opt_ex |= (1 << 0); break; case 's': /* source address */ strncpy(o.src, optarg, sizeof(o.dst)); o.opt_ex |= (1 << 1); break; case 'p': /* destination port */ o.port = atoi(optarg); o.opt_ex |= (1 << 2); break; case 'f': /* input file */ o.file = (char *)malloc(sizeof (optarg)); strcpy(o.file, optarg); o.opt_ex |= (1 << 3); break; case 'v': /* verbose mode */ o.verbose = TRUE; break; case 'h': /* help - usage */ print_usage(); break; #ifdef DEBUG case 'D': /* debug mode */ o.debug = TRUE; break; #endif case '?': /* error */ fprintf(stderr, "option inconsistency : -%c \n" "see usage(no arguments)\n", optopt ); exit(EXIT_FAILURE); } } /* some option restrictions */ if ((o.opt_ex & 0x0F) != 0x0F) clean_exit("need to provide all -d -s -p -f arguments", 0); /* check if u r r00t */ if (getuid() && geteuid()) clean_exit("need to be root", 0); /* create the raw packet and send it */ raw_packet_init(); return(EXIT_SUCCESS); }