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