Port Knocking

Introduction

What is Port Knocking?

Port Knocking involves using firewall rules to restrict access to an access port, say ssh or telnet, and open it briefly only to someone who tries to connect to a certain combination of ports first. Sound interesting? It's basically a combination lock that opens a time window for connections to a port on your server, and is implemented with firewall logic.

Why Would I Want To Use It?

"I've using the latest version of ssh which is secure, why would I want to use this?" This is by no means suggested as an alternative to secure shell or other security measures you already have in place, but rather has one primary benefit (other than being really cool): it prevents other people from knowing you run ssh, telnet, whatever else. This is good for a couple of reasons: As an example, here is a log summary of ssh failed logins to a new server on the Internet within the first six hours:
 sshd:
    Authentication Failures:
       unknown (202.222.28.85): 2539 Time(s)
       adm (202.222.28.85): 6 Time(s)
       apache (202.222.28.85): 6 Time(s)
       bin (202.222.28.85): 6 Time(s)
       daemon (202.222.28.85): 3 Time(s)
       ftp (202.222.28.85): 3 Time(s)
       games (202.222.28.85): 3 Time(s)
       lp (202.222.28.85): 3 Time(s)
       unknown (mbl-82-54-33.dsl.net.pk): 3 Time(s)
       mail (202.222.28.85): 2 Time(s)
       unknown (211.20.54.196): 2 Time(s)
       mailnull (202.222.28.85): 1 Time(s)
If a vulnerabililty was discovered in ssh or a password easily guessible, there's a possibility of a remote attacker gaining access.

Requirements

This particular method uses netfilter (iptables) on Linux. Not all versions of netfilter have the required match modules however. You will need: You can see if you have these match modules by looking in your kernel modules:
<nat@saturn>/home/nat% ls -l /lib/modules/2.6.5-1.358/kernel/net/ipv4/netfilter/ipt_{state,recent}.ko
-rwxr--r--  1 root root 11360 May  8 08:46 /lib/modules/2.6.5-1.358/kernel/net/ipv4/netfilter/ipt_recent.ko*
-rwxr--r--  1 root root  2988 May  8 08:46 /lib/modules/2.6.5-1.358/kernel/net/ipv4/netfilter/ipt_state.ko*
I am using Fedora Core 2, which has this all already. Earlier versions of Linux on the 2.4 kernel (Red Hat 9, for example) may not have the ipt_recent.ko module, so RH9 users will have to recompile the kernel with these match modules configured. More information on this can be found on the netfilter site, specifically patch-o-matic.

Implementation

This will demonstrate port knocking between two of my servers, "uranus" running Red Hat 7.3 as a connecting client and "saturn" running Fedora Core 2 as the server. For most purposes you will implement this on your ssh port, but I am going to do it on telnet in this example because it displays a login prompt, making it more suitable for demonstration.

Before...

As you can see, both a simple telnet and nmap (port scan) show that telnet is alive and well on saturn, and I can login from uranus and pretty much anywhere else.
[root@uranus nat]# nmap -sT saturn

Starting nmap V. 2.54BETA31 ( www.insecure.org/nmap/ )
Interesting ports on saturn.guyton.net (10.1.1.4):
(The 1545 ports scanned but not shown below are in state: closed)
Port       State       Service
22/tcp     open        ssh
23/tcp     open        telnet
53/tcp     open        domain
80/tcp     open        http


Nmap run completed -- 1 IP address (1 host up) scanned in 1 second
[root@uranus nat]# telnet saturn
Trying 10.1.1.4...
Connected to saturn.
Escape character is '^]'.
Fedora Core release 2 (Tettnang)
Kernel 2.6.5-1.358 on an i686
login:

Implement

OK, so to protect telnet from people who don't know the port-knock sequence, we issue the following rules as root on saturn:
# We only want to block new connections to telnet, not choke off accepted ones 30 seconds after acceptance!
iptables -A INPUT -p tcp --dport 23 -m state --state ESTABLISHED -j ACCEPT
# As each key port is hit, clear records that we've hit the subsequent ports
iptables -A INPUT -p tcp --dport 902 -m recent --remove --name PART2
iptables -A INPUT -p tcp --dport 902 -m recent --remove --name PART3
iptables -A INPUT -p tcp --dport 902 -m recent --remove --name PART4
iptables -A INPUT -p tcp --dport 425 -m recent --remove --name PART3
iptables -A INPUT -p tcp --dport 425 -m recent --remove --name PART4
iptables -A INPUT -p tcp --dport 721 -m recent --remove --name PART4
# Record that we hit a port
iptables -A INPUT -p tcp --dport 902 -m recent --set --name PART1
iptables -A INPUT -p tcp --dport 425 -m recent --set --name PART2
iptables -A INPUT -p tcp --dport 721 -m recent --set --name PART3
iptables -A INPUT -p tcp --dport 813 -m recent --set --name PART4
# Allow in if we hit all 4 ports (in order as enforced above) in the last 30 seconds
iptables -A INPUT -p tcp --dport 23 -m recent --rcheck --seconds 30 --name PART1 --name PART2 --name PART3 --name PART4 -j ACCEPT
# Block all other requests
iptables -A INPUT -p tcp --dport 23 -j REJECT

... and After

[root@uranus nat]# nmap -sT saturn

Starting nmap V. 2.54BETA31 ( www.insecure.org/nmap/ )
Interesting ports on saturn.guyton.net (10.1.1.4):
(The 1546 ports scanned but not shown below are in state: closed)
Port       State       Service
22/tcp     open        ssh
53/tcp     open        domain   Look!  No telnet!
80/tcp     open        http


Nmap run completed -- 1 IP address (1 host up) scanned in 0 seconds
[root@uranus nat]# telnet saturn
Trying 10.1.1.4...
telnet: connect to address 10.1.1.4: Connection refused   Yep, no telnet
[root@uranus nat]# telnet saturn 902                      Start combination: 902
Trying 10.1.1.4...
telnet: connect to address 10.1.1.4: Connection refused
[root@uranus nat]# telnet saturn 425                      425
Trying 10.1.1.4...
telnet: connect to address 10.1.1.4: Connection refused
[root@uranus nat]# telnet saturn 721                      721
Trying 10.1.1.4...
telnet: connect to address 10.1.1.4: Connection refused
[root@uranus nat]# telnet saturn 813                      813
Trying 10.1.1.4...
telnet: connect to address 10.1.1.4: Connection refused
[root@uranus nat]# telnet saturn                          HEY!  TELNET!
Trying 10.1.1.4...
Connected to saturn.
Escape character is '^]'.
Fedora Core release 2 (Tettnang)
Kernel 2.6.5-1.358 on an i686
login:
This opportunity lasts for 30 seconds. Within that window I also did another port scan:
[root@uranus nat]# nmap -sT saturn

Starting nmap V. 2.54BETA31 ( www.insecure.org/nmap/ )
Interesting ports on saturn.guyton.net (10.1.1.4):
(The 1545 ports scanned but not shown below are in state: closed)
Port       State       Service
22/tcp     open        ssh
23/tcp     open        telnet     Look!  Telnet!
53/tcp     open        domain
80/tcp     open        http


Nmap run completed -- 1 IP address (1 host up) scanned in 0 seconds
30 seconds after the first combination number was sent, all new access is closed (but established telnet windows are still valid).

Room For Improvement

This implementation works well, however a better approach would be to monitor the non-combination ports as well, and if they are hit by the foreign IP trying to unlock the site, then reset the sequence. ie: Currently if someone tries 902 - 425 - 323 - 721 - 345 - 813, the approach above still grants access; the 323 and 345 numbers not in my "combination" should invalidate the sequence. There are some multiport/port range match modules that come with the netfilter package that would do the trick - I just haven't looked at them yet.

Also note that no sane person would enable telnet across the Internet. I assume that the approach above would be to guard your ssh port or something else that is already encrypted.