exfiltration via receive timing
Another similar way to create a backchannel but without transmitting anything is to introduce delays in the receiver and measure throughput as observed by the sender. All we need is a protocol with transmission control. Hmmm.
Actually, it’s easier (and more reliable) to code this up using a plain pipe, but the same principle applies to networked transmissions.
First the reader code. We’ll assume an input string of decimal digits, 1-9.
void
reader(char *data, int fd)
{
int delay;
char buf[32 * 1024];
while (*data) {
sleep(*data - '0');
data++;
read(fd, buf, sizeof buf);
}
exit(0);
}
For every digit we want to “send” back, we sleep a few seconds, then drain the pipe. We don’t care about the data, although if this were a video file or an OS update, we could probably do something useful with it.
Now the writer side.
void
writer(int fd)
{
char buf[1024];
time_t before, after;
int rv;
while (1) {
before = time(NULL);
rv = write(fd, buf, sizeof buf);
after = time(NULL);
if (rv == -1)
break;
if (after - before)
printf("%c\n", '0' + after - before);
}
exit(0);
}
Continuously fill the pipe with junk data. If (when) we block, calculate the difference between before and after. This is a our secret backchannel data. (The reader and writer use different buffer sizes because on OpenBSD at least, a writer will stay blocked even after a read depending on the space that opens up. Even simple demos have real world considerations.)
The main driver is just pipe and fork.
int
main(int argc, char **argv)
{
int fds[2];
pipe(fds);
if (fork()) {
close(fds[0]);
writer(fds[1]);
} else {
close(fds[1]);
reader(argv[1], fds[0]);
}
}
In this simple example, the secret data (argv) is shared by the processes, but we can see that the writer isn’t printing them from its own address space. Nevertheless, it works.
./a.out 321856
3
2
1
8
5
6
Time to add random delays and buffering to firewalls? Probably not.