Timeshifter

Transmissions of data through time based covert channels across a network

Introduction

Overview

We created a system in order to transmit and receive data by modifying the time intervals between packets.

packets

Why?

Steganography provides a way to hide data in plain sight, in this case as time intervals of packets. As the data isn’t immediately obvious it is less likely to arouse suspicion.

Tweaking

There are two variables which can be adjusted as parameters of the program.

The first being the threshold, that intervals between packets below or equal to, will be classed as a zero bit.

Another variable represents the ammount of time delay that is added between two packets, to generate a one bit.

By making these value lower, transmission speed is increased and the noticability of data transmission will be lower, however the potential for errors will increase.

Improvements

By measuring the time intervals between many packets, before first attempting to manipulate them, we can attempt to minimise the delay we add – as we would be able to work out the minimum time interval we can add, to result in a noticable output at the receiver end.

We could make use of hamming codes to aid error correction in case we miss-detect bits.

Additionally we could add stop bits to the raw binary data in order to aid better synchronisation in case one byte is lost.

We could also make use of a block mode such as CTR/OFB using a cipher such as AES, which will allow part of the plaintext to still be recovered in case some bytes are damaged.

Setting up iptables

In order to manipulate the timing delays for packets we first need to ensure that we have the iptables NFQUEUE module loaded or compiled into our Linux kernel.

To start with we first flush the iptables rules.

iptables -F
iptables -A OUTPUT -p icmp --icmp-type echo-request -j NFQUEUE --queue-num 0
iptables -A INPUT -p icmp --icmp-type echo-reply -j NFQUEUE --queue-num 1

N.B. once you’ve activated these iptables rules, then packets will not be sent out from the computer until you run the program below.

Sourcecode

Libraries required

You need the libnetfilter_queue library which can be obtained from your Linux distribution’s repository or http://www.netfilter.org/projects/libnetfilter_queue/

This library allows you to manipulate queued packets, we use it to manipulate the time delays between them.

sudo apt-get install libnetfilter-queue-dev

Compilation and execution

Compile using:

gcc timeshifter.c -o timeshifter -lnetfilter_queue -lnfnetlink -lm

As we are performing this test on a single computer, we are going to use the intervals between ping replies as the message we are going to decode.

Run the following as root:

Run the transmitter using: echo "helloworld" | ./timeshifter 0 2000 3000
Run the reciever using:                        ./timeshifter 1 2000 3000

Then on the same computer use: ping google.com

It is important to ensure in this case, that the only ICMP packets that are being sent, are those sent by the ping command we have just run.

timeshifter

Repository

I’ve put the code here on github, and am going to start working on adding additional functionality to it, such as fountain codes.

Actual code

// https://www.anfractuosity.com/projects/timeshifter/

#include <stdint.h>
#include <time.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h>
#include <arpa/inet.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <time.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <sys/timex.h>
#include <math.h>

#define NF_IP_PRE_ROUTING   0
#define NF_IP_LOCAL_IN      1
#define NF_IP_FORWARD       2
#define NF_IP_LOCAL_OUT     3
#define NF_IP_POST_ROUTING  4
#define NF_IP_NUMHOOKS      5
#define ERR_INIT            -1

int first = 1;
struct timeval start, elapsed;

int startp = -1;
char buff[1];

int zerothreshold = -1;
long long onesleep = -1;

unsigned char getbit(unsigned char *bits, unsigned long n) {
	return (bits[n / 8] & (unsigned char)pow(2, n % 8)) >> n % 8;
}

void setbit(unsigned char *bits, unsigned long n, unsigned char val) {
	bits[n / 8] =
	    (bits[n / 8] & ~(unsigned char)pow(2, n % 8)) | ((unsigned char)
							     pow(2,
								 n % 8) * val);
}

static int difference_micro(struct timeval *before, struct timeval *after) {
	return (signed long long)after->tv_sec * 1000000ll +
	    (signed long long)after->tv_usec -
	    (signed long long)before->tv_sec * 1000000ll -
	    (signed long long)before->tv_usec;
}

static uint32_t nfqueue_packet_get_id(struct nfq_data *packet) {
	uint32_t id = -1;
	struct nfqnl_msg_packet_hdr *packetHeader;

	if ((packetHeader = nfq_get_msg_packet_hdr(packet)) != NULL)
		id = ntohl(packetHeader->packet_id);

	return id;
}

static uint32_t nfqueue_packet_get_hook(struct nfq_data *packet) {

	uint32_t hook = -1;
	struct nfqnl_msg_packet_hdr *packetHeader;

	if ((packetHeader = nfq_get_msg_packet_hdr(packet)) != NULL)
		hook = packetHeader->hook;

	return hook;
}

static int manage_packet(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
			 struct nfq_data *nfa, void *data2) {

	uint32_t hook = nfqueue_packet_get_hook(nfa);
	uint32_t id = nfqueue_packet_get_id(nfa);

	switch (hook) {
		case NF_IP_LOCAL_IN:

			if (first) {
				printf("Reciever...\n");
				gettimeofday(&start, 0x0);
				first = 0;
				startp = 0;
			} else {
				gettimeofday(&elapsed, 0x0);

				long ms =
				    difference_micro(&start, &elapsed) / 1000;

				if (startp == 0)
					printf("BYTE: ");
				
				if (ms <= zerothreshold) {
					printf("0");
					setbit(buff, startp, 0);
				} else {
					printf("1");
					setbit(buff, startp, 1);

				}

				fflush(stdout);
				startp++;

				if (startp % 8 == 0) {
					printf(" (%c) \n", buff[0]);
					startp = 0;
				}

				start.tv_sec = elapsed.tv_sec;
				start.tv_usec = elapsed.tv_usec;
			}

			return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);

		case NF_IP_FORWARD:

			puts("capturing packet from FORWARD iptables hook");
			return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);

		case NF_IP_LOCAL_OUT:{

				if (startp == -1) {
					printf("Transmitter...\n");

					int b = fgetc(stdin);
					if (b == EOF)
						b = 0;

					buff[0] = b;
					startp = 0;

					return nfq_set_verdict(qh, id,
							       NF_ACCEPT, 0,
							       NULL);
				}

				unsigned char bit = getbit(buff, startp);

				if (startp == 0) {
					printf("BYTE: ");
				}

				if (bit == 0) {
					printf("0");
				} else if (bit == 1) {
					printf("1");
					int seconds = onesleep / 1000;
					long long milliseconds =
					    (((double)onesleep / (double)1000) -
					     (double)seconds) * 1000 * 1000000;
					nanosleep((struct timespec[]) { {
						  seconds, milliseconds}},
						  NULL);
				}

				fflush(stdout);

				startp++;

				if (startp % 8 == 0) {
					printf(" (%c)\n", buff[0]);

					int b = fgetc(stdin);
					if (b == EOF)
						b = 0;

					buff[0] = b;
					startp = 0;
				}

				return nfq_set_verdict(qh, id, NF_ACCEPT, 0,
						       NULL);
			}

		default:
			puts("error: capturing packet from an iptables hook we shouldn't");
			return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);

	}

}

int main(int argc, char **argv) {
	struct nfq_handle *handle;
	struct nfq_q_handle *queue;
	struct nfnl_handle *netlink_handle;
	uint32_t nfqueue_fd;

	if (argc != 4) {
		printf("Argument: queue_number zerothreshold onesleep\n");
		printf("Times are to be represented in milliseconds.\n");
		return -1;
	}

	int16_t queuenum = atoi(argv[1]);
	zerothreshold = atoi(argv[2]);
	onesleep = atoi(argv[3]);

	handle = nfq_open();

	if (!handle) {
		perror("Error: during nfq_open()");
		return ERR_INIT;
	}

	if (nfq_unbind_pf(handle, AF_INET) < 0) {
		perror("Error: during nfq_unbind_pf()");
		return ERR_INIT;
	}

	if (nfq_bind_pf(handle, AF_INET) < 0) {
		perror("Error: during nfq_bind_pf()");
		return ERR_INIT;
	}

	queue = nfq_create_queue(handle, queuenum, &manage_packet, NULL);

	if (!queue) {
		perror("Error: during nfq_create_queue()");
		return ERR_INIT;
	}

	if (nfq_set_mode(queue, NFQNL_COPY_PACKET, 0xffff) < 0) {
		perror("Error: can't set packet_copy mode");
		return ERR_INIT;
	}

	netlink_handle = nfq_nfnlh(handle);
	nfqueue_fd = nfnl_fd(netlink_handle);

	char buf[4096] __attribute__ ((aligned));
	int received;

	while (1) {
		if ((received = recv(nfqueue_fd, buf, sizeof(buf), 0)) >= 0) {
			nfq_handle_packet(handle, buf, received);
		}
	}

	if (!queue)
		nfq_destroy_queue(queue);

	if (!handle)
		nfq_close(handle);

	return 0;
}


6 Comments
  • Unum
    May 30, 2015 Reply

    root@zeus:/home/unum/ts# echo “helloworld” | ./timeshifter 0 2000 3000

    root@zeus:/home/unum/ts# ./timeshifter 1 2000 3000

    —-

    Both of these return no output. Any advice?

    • admin
      May 30, 2015 Reply

      Odd, did you use the iptables commands first?

      And use the ping command?

  • Duister
    June 19, 2015 Reply

    Nice, works fine on an Ubuntu – had lib troubles on centos.

    Works on the local host as mentioned; any change to create an ip to ip version?

    Grts!

  • jdc
    October 17, 2016 Reply

    Are you able to send files using this? I’ve been able to send the contents of text files via cat but how about img files etc?

    • admin
      October 17, 2016 Reply

      I’ve not tried that actually, it might work without modification, thinking about it, if you just cat’d the file to it.

  • neelmanojshimpi
    May 31, 2019 Reply

    do “sudo -i” to run the commands otherwise just with “sudo” you get ‘operation not permitted’ error. Also ping to remote host to get the receiver working

Leave Comment

Click here to cancel reply

Error Please check your entries!