Decred behind Portknocking
1. Introduction
Portknocking is a technique used to hide the existence of a service running on a server. Portknocking requires that a specific port combination receive traffic in order for the service port to be opened.
Using portknocking makes it virtually impossible for an attacker to discover the service running on that port. For example, for portknocking that requires the user to hit 3 different ports in 5 seconds, it would be necessary to test 65535^3 ports.
WARNING! If your Internet access is being monitored, the censor may notice this type of network traffic. A partial solution would be to open ports from another IP address.
To learn more about knockd, read https://linux.die.net/man/1/knockd.
2. Installation
The following steps were performed on a Debian 9 64-bit.
a) Install knockd from the default repository.
$ sudo apt-get install knockd
b) Edit /etc/default/knockd.conf
file and set parameter START_KNOCKD=1
so that the service will start automatically at the next operating system startup.
3. Simple mode
3.1. Configuration
The next example shows knockd configuration on file /etc/knockd.conf
for sshd service.
$ cat /etc/knockd.conf
[options]
UseSyslog
[openSSH]
sequence = 7532,2357,7235
seq_timeout = 5
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -m comment --comment "Knockd: SSH" -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 7235,2357,7532
seq_timeout = 5
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -m comment --comment "Knockd: SSH" -j ACCEPT
tcpflags = syn
If there are any rules in iptables that allow or deny traffic that will be controlled by Portknocking this rule must be deleted. To block all traffic that is not explicitly allowed use the iptables chain policy:
$ sudo iptables -P INPUT DROP
If you have made any changes to iptables rules or policies, the ‘iptables-save’ command should be used to write the rules permanently.
Start the service manually.
$ sudo service knockd start
Whenever the portknocking rules change, you must restart the service.
$ sudo service knockd restart
3.2. Execution
Create the following scripts on the client device that will be used to open services ports protected by Portknocking. In the following examples telnet was used for knocking, but knockd comes with a knock
application that allows you to choose the port and protocol in the port: protocol
format. The project site contains clients for other platforms.
$ cat dcrd_ssh_open.sh
telnet $DCRD_IP 7532
telnet $DCRD_IP 2357
telnet $DCRD_IP 7235
$ cat dcrd_ssh_close.sh
telnet $DCRD_IP 7235
telnet $DCRD_IP 2357
telnet $DCRD_IP 7532
At the end, run the script and connect to the port that was opened.
$ ./dcrd_ssh_open.sh
4. Advanced mode: decoy
As explained in section 1, if the censor monitors traffic, one option is to open the port from another IP address. In this case, the iptables command cannot include the source address (-s% IP%
) as a traffic restriction or will have to keep a fixed IP address in the configuration. But there are more practical alternatives to try to bypass the censor, such as setting up other sequences with common ports, for example.
The following examples hide the SSH TCP/22 port, but could be used to hide the VPN or TCP port of Decred components.
Interface
: The default interface where knockd expects packets is “eth0”, but can be changed with this directive.seq_timeout
: Determines the maximum wait time for all packets in the sequence.tcpflags
: Determines the flags that should be marked on TCP packets. knockd will ignore packets that do not have flags selected, but will not invalidate the entire sequence forcing the client to start over. Multiple flags can be separated with commas (egtcpflags = syn, ack, urg
) and can be explicitly negated with “!” (eg:tcpflags = syn,! ack
).command
: the iptables command that will be executed when the client hits the correct sequence of knocks.
Example 1
$ cat /etc/knockd.conf
[options]
UseSyslog
Interface = ens33
[openSSH]
sequence = 80,443,21
seq_timeout = 29
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
tcpflags = syn
Example 2
$ cat /etc/knockd.conf
[options]
UseSyslog
Interface = ens33
[openSSH]
sequence = /etc/knockd_ssh_sequences
seq_timeout = 29
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
tcpflags = syn
Example 3
In this example other directives are also used:
start_command
: the iptables command that will be executed when the client hits the correct sequence of knocks. The%IP%
variable will be replaced with the knocker IP IP address.cmd_timeout
: Timeout between execution ofstart_command
andstop_command
. It is optional and only required ifstop_command
is used.stop_command
: the iptables command that will be executed when the seconds ofcmd_timeout
are over. The%IP%
variable will be replaced with the knocker IP IP address. It is optional.
$ cat /etc/knockd.conf
[options]
UseSyslog
Interface = ens33
[opencloseSSH]
one_time_sequences = /opt/knockd_ssh_sequences
seq_timeout = 29
tcpflags = syn
start_command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
cmd_timeout = 25
stop_command = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
[openclosedcrwallet]
one_time_sequences = /opt/knockd_dcrwallet_sequences
seq_timeout = 29
tcpflags = syn
start_command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 9109 --syn -m comment --comment "Knockd: dcrwallet" -j ACCEPT
cmd_timeout = 25
stop_command = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 9109 --syn -m comment --comment "Knockd: dcrwallet" -j ACCEPT
The third example uses predefined port sequences, which prevents the censor from easily guessing this behavior.
File /opt/knockd_ssh_sequences
contains the port sequences that will be used. Each time a string is used, it will be invalidated with a comment (#) at the beginning of the line. This comment will replace the first character of the line, so you must leave a blank space at the beginning of each line.
$ cat /opt/knockd_ssh_sequences
80:tcp,443:tcp,53:udp
21:tcp,123:udp,995:tcp
53:udp,23:tcp,135:tcp
Example 4: run knocking from ‘Example 3’ with app knock
This example assumes that the same knockd_ssh_sequences
file also exists on the client.
$ cat dcrd_open.sh
# Author: Marcelo Martins (stakey.club)
#! /bin/sh -
# Example: dcrd_open.sh [line number] [server IP address] [seq_file]
KNOCK_BIN=knock
SEQ_FILE="knockd_ssh_sequences"
SLEEP_MAX=14
SRV_IP="11.12.13.14"
if [ -z $1 ]; then
echo "Line number must be provided."
exit 1
fi
if [[ $(( $1 % 1 )) -eq $1 ]]; then
echo "Argument \"$1\" is not a number."
exit 1
fi
if [ ! -z $2 ]; then
SRV_IP=$2
fi
if [ ! -z $3 ]; then
SEQ_FILE=$3
fi
SEQ=`head -"$1" $SEQ_FILE | tail -1`
PORT1=`echo $SEQ | awk -F "," '{print $1}'`
PORT2=`echo $SEQ | awk -F "," '{print $2}'`
PORT3=`echo $SEQ | awk -F "," '{print $3}'`
if [[ -z $PORT1 || -z $PORT2 || -z $PORT3 ]]; then
echo "Unable to extract port:protocol from file $SEQ_FILE, line was [$SEQ]."
exit 1
fi
SLEEP1=$(( RANDOM % $SLEEP_MAX )) # do not use RANDOM for simulations
SLEEP2=$(( RANDOM % $SLEEP_MAX )) # or crypto, it's flawed
echo "Knocking on server's door... at $SRV_IP, ports $PORT1 $PORT2 $PORT3"
$KNOCK_BIN $SRV_IP $PORT1
echo "Sleeping for $SLEEP1 seconds."
sleep $SLEEP1
echo "Knock, knocking on server's dooor... at $SRV_IP, ports $PORT1 $PORT2 $PORT3"
$KNOCK_BIN $SRV_IP $PORT2
echo "Sleeping for $SLEEP2 seconds."
sleep $SLEEP2
echo "Knock, knock, knocking on server's dooooor... at $SRV_IP, ports $PORT1 $PORT2 $PORT3"
$KNOCK_BIN $SRV_IP $PORT3
echo "We're done knocking on server's door. The door must be open."
5. Filters in the firewall
To prevent attackers from trying to portscan or guess the Portknocking sequence, you can configure firewall rules to temporarily block IP addresses that show this behavior.
a) First is created the blacklist that will keep for 180 seconds, for example, the addresses sent to it. This rule drops all packets coming from addresses that are in the list.
$ sudo iptables -A INPUT -m recent --name blacklist_180 --rcheck --seconds 180 -m comment --comment "IP Blacklisted for 180 sec" -j DROP
b) Next we create the chain TCP_SYN on the first line. The second line directs the TCP_SYN chain to all TCP packets with this flag checked. It is in the TCP_SYN chain that it will be checked if, after 3 packets with the SYN flag marked per second, we continue to receive a number of packets exceeding the limit (1/s), as shown in the third line. If this happens, the chain follows processing and sends the IP address to the blacklist as determined by the fourth line. If not, the third rule returns processing to the INPUT chain.
$sudo iptables -N TCP_SYN
$sudo iptables -A INPUT -p tcp --syn -j TCP_SYN
$sudo iptables -A TCP_SYN -m limit --limit 1/s --limit-burst 3 -m comment --comment "Limit TCP SYN rate" -j RETURN
$ sudo iptables -A TCP_SYN -m recent --name blacklist_180 --set -m comment --comment "Blacklist source IP address" -j DROP