Cover V12, I06

Article
Listing 1
Listing 2

jun2003.tar

Port Knocking -- Network Authentication Across Closed Ports

Martin Krzywinski

Two thousand years ago, information security was already a concern to Julius Caesar, who is said to have been one of the first people to use cryptography to secure his dispatches. To encrypt his messages, he applied a circular shift to each letter moving it three places further in the alphabet. In the day when few were literate, the Caesar cipher was probably sufficient. In fact, evidence exists to indicate that cryptography was used by Hebrew scribes as early as 600-500 BC in the form of a reverse-alphabet cipher named ATBASH. Long before the inception of the first computer network, information security was a preoccupation for those responsible for safeguarding data. Today, with the exponential growth of information systems and of the Internet population, effectively protecting information requires increasingly sophisticated methods. How do we keep our data protected from unauthorized access while keeping it accessible online to ourselves and to those we trust?

Keeping network applications updated and having a conservatively configured firewall can serve as the first line of defense. Equally important are intrusion detection systems (IDS), which are designed to spot known, derivative, or anticipated attacks. While no connected system can be rendered safe, short of severing the network cable (sometimes the right solution), a properly configured firewall/IDS combination is an effective bulwark. However, packet-filtering firewalls (e.g., the Linux kernel-based IPCHAINS/IPTABLES) filter on the basis of IP address and ports and do not contain an authentication mechanism to discriminate users. As a result, applications behind open ports remain vulnerable to attacks by malevolent individuals from unfiltered IP addresses.

Port knocking is a firewall-based user authentication system that uses closed ports for authentication, making it possible to connect to a networked computer that has no open ports. Communication across closed ports is mediated through the firewall log, in which all connection attempts are recorded. The log is monitored for specific port sequences that encode information used to modify firewall rules, which are changed to open or close ports for a specific IP address. The system can be extended to implement the transfer of arbitrary information across closed ports. Port knocking is inherently stealthy because it is not possible to determine whether a host is listening to port knocks and because the authentication information flows in the form of connection attempts.

Authentication Mechanism

The application of the port knocking mechanism is illustrated in this section. A number of examples are used to show how knock sequences can be used to encode information.

In the simplest case, a small number of ports are used to encode short knock sequences that map to specific modifications of the firewall rules. For example, a host with IP address FIREWALL may have all privileged ports closed, while ports 100-109 are monitored for connection attempts. The IPCHAINS command to achieve this is:

ipchains -A input -p tcp -s 0/0 -d FIREWALL/32 100:109 -j DENY -l
ipchains -A input -p tcp -s 0/0 -d FIREWALL/32 1:1024 -j DENY
Any connections to ports 100-109 will be silently ignored, but logged. The client will not receive ICMP error packets and therefore cannot determine whether an authentication process is taking place. Now suppose a client from IP address IPCLIENT makes a series of connection attempts to FIREWALL's ports 102,100,100,103, in that order. The firewall log will contain entries like:

Feb 12 00:13:26 ... input DENY eth1 PROTO=6 IPCLIENT:64137 FIREWALL:102 ...
Feb 12 00:13:27 ... input DENY eth1 PROTO=6 IPCLIENT:64138 FIREWALL:100 ...
Feb 12 00:13:27 ... input DENY eth1 PROTO=6 IPCLIENT:64139 FIREWALL:100 ...
Feb 12 00:13:28 ... input DENY eth1 PROTO=6 IPCLIENT:64140 FIREWALL:103 ...
Using a service daemon that monitors the firewall log, the port sequence 102,100,100,103 can be extracted and associated with the incoming IP address. This specific port sequence could act as a trigger to manipulate the firewall rules. For example, it could set off the following rule:

ipchains -I input -p tcp -s IPCLIENT/32 -d FIREWALL/32 22 -j ACCEPT
which would have the effect of opening port ssh/22 to TCP traffic from IPCLIENT. The client that performed the knock sequence would now be able to connect to the ssh application and carry out a session. After the session ends, another knock sequence would be sent to close the port.

The manner in which information is encoded in the port sequence is arbitrary. In the example above, a discrete mapping between a sequence and an action was illustrated. The sequence could also contain the port and the length of time it is held open. For example, the sequence could be:

103,101,107  100+a,100+b,100+c,100+d  100+e  100+(a+b+c+d+e mod 10)  107,101,103
header       payload                  time   checksum                footer
The header and footer indicate to the monitoring daemon that a sequence is about to start and end. Four numbers in the sequence payload encode the port to be opened. A time value indicates whether to (1) open the port, (2) open the port and then close it after some time has passed, or (3) close the port. In this example, 105 could mean open port abcd and keep it open for 5*10=50 minutes. A final checksum field validates the authenticity of the sequence.

A more sophisticated sequence would be encrypted and would contain the IP address for which ports are opened. This type of knock sequence provides a two-fold advantage. First, anyone intercepting the sequence cannot directly use it unless they manage to decrypt it, replace the client IP with their own, and then re-encrypt it. Second, ports can be opened for a given IP address by using a different host, since the IP address of the client performing the knock is no longer used. This type of knock sequence could encrypt the following data, stored in seven unsigned chars, with the range of the port limited to 0-255 for simplicity:

IPbyte1 IPbyte2 IPbyte3 IPbyte4 port time checksum
For example, if port 22 were to be opened to the client from IP address 142.103.205.1 for 15 minutes, the decrypted sequence would be:

142 103 205 1 22 15 233
where the checksum is (142+103+205+1+22+15) mod 255 = 233. Using Perl, the encryption can be carried out using the cipher block chaining module Crypt::CBC and, to choose one algorithm, Crypt::Blowfish. In this example, the cipher object is created without using an initialization vector (IV). The IV is random data used to seed the algorithm to ensure that each encryption of the same data results in a different cipher text. Removing the IV reduces the length of the cipher text, and therefore of the encrypted knock sequence. In practice, an IV would be used, but in this example it is skipped:

use constant KEY => "knock";
use constant CIPHER => "Blowfish";
$cipher = Crypt::CBC->new({key=>KEY,cipher=>CIPHER,
                      iv=>"01234567",prepend_iv => 0});
The knock data is stored as an array, with the checksum as the last element. The sum() function from Math::VecStat is used to calculate the checksum:

@data = (142,103,205,1,22,15)
push(@data,sum(@data) % 255); # 142, 103, 205, 1, 22, 15, 233
The data is packed into a string, encrypted, then encoded into a list of unsigned chars:

$ciphertext = $cipher->encrypt(pack("C*",@data));
@cipherpack = unpack("C*",$ciphertext); # 221, 169, 50, 219, 86, 117, 62, 187
Finally, the unsigned chars are mapped onto actual port values. For example, ports 745-1000 could be used:

use constant PORTMIN => 745;
@knocks = map {PORTMIN+$_} @cipherpack; # 966, 914, 795, 964, 831, 862, 807, 932
The result is an encrypted 8-port knock sequence that contains the IP address, port, time flag, and checksum. Once the knock sequence is received by the daemon monitoring the log, it is decrypted. Malformed sequences can be detected because the decryption step does not produce the expected data structure.

Benefits of Port Knocking

The primary feature of port knocking is that it allows for stealthy authentication into a networked host that has no open ports. The method is stealthy because it is not possible to directly determine whether the host is listening for port knocks. While port probes could be employed to try to guess which ports are being monitored and the format of the knock sequence, success is highly improbable and such brute-force attempts can be easily discovered with IDS tools.

Since information is flowing in the form of connection attempts, rather than typical packet data payload, it is unlikely that this authentication method would be detected by packet sniffing methods. To minimize the risk of a functional knock sequence being reconstructed by a third party, the sequence should contain the client IP address and be encrypted.

The system is flexible because the authentication scheme is built into the knock sequence. Existing applications, such as ssh, which perform their own authentication do not need to be changed. One-time port knocks can be implemented by adjusting the way particular sequences are interpreted. For example, a sequence could correspond to a request that a port be opened for only a specific length of time and then never opened again to the same IP. Furthermore, a one-time pad could be used to encrypt the sequence, making it undecipherable by those without the pad.

Disadvantages of Port Knocking

As with any security system, the disadvantages present themselves as inconveniences, preferably small, which must be endured while the system is in use. These inconveniences should ideally pose less of a burden than the risk associated with not using the system. Port knocking requires a client script to perform the knock. This client and any associated data should be considered a secret and kept on removable media, such as a USB key. The use of the client imposes an overhead for each connection and users would require instruction on its appropriate use.

For obvious reasons, port knocking cannot be used to protect ports of public applications, such as Web or mail services. It is not reasonable to expect that each mail or web client will perform a port knock. This would require everyone to know the secret knock, or how to construct one. If port knocking is used on a host to protect some ports (e.g., ssh, telnet, ftp) while others remain open for the public (e.g., Web, mail), two options remain. Public services should preferably be delegated to a bastion host in the DMZ and isolated from the host on which sensitive information is stored. If the delegation is not possible, all public services must be regularly updated or patched to minimize vulnerability. Though some applications remain open to the public, the protection of others with port knocking would limit the number of possible types of attack.

The implementation of any system that manipulates firewall rules in an automated fashion must be robust to prevent legitimate users from being locked out. If knocks cannot be interpreted properly or if the service daemon crashes, the networked host may become isolated. Appropriate safeguards should be implemented to avoid such a scenario.

Implementation

Both the client and server scripts are available at:

http://mkweb.bcgsc.ca/portknocking
knockclient

A prototype client is shown in Listing 1. Lincoln Stein's Crypt::CBC module is used as proxy to Crypt::Blowfish to carry out encryption. The unencrypted knock sequence comprises seven values: four IP bytes, a port (limited to the range 0-255 in this implementation), a time flag, and a checksum (mod 255). The time flag determines how the daemon will react: 0 to open the port, 255 to close the port, and any other value in the range 1-254 to open the port and then close it after that many minutes. The knock on the firewall (IP=FIREWALL) to open port ssh/22 on IP=IPCLIENT and have the port closed after 15 minutes would be executed by calling the client as follows:

knockclient -c IPCLIENT -r FIREWALL -p 22 -t 15
The client packs the list of seven integers, performs the encryption, and unpacks the string into unsigned chars (0-255). These values are then mapped onto a sequence of ports in the range 745-1000.

knockdaemon

The accompanying knock daemon is shown in Listing 2. This script will work with any firewall compatible with IPCHAINS syntax. The File::Tail module is used to continually look for new lines in the firewall log. Lines corresponding to connection attempts to ports 745-1000 are parsed for the remote IP and port number. Because the client produces encrypted sequences of 8 digits, a queue of this length is used to store the ports. Each connecting IP has its own queue. Every time the queue size reaches 8 an attempt is made to decrypt it. If the decryption is successful, and the checksum is correct, appropriate action is taken and the queue is cleared. If the decryption fails, the oldest queue port element is removed.

The firewall rules are manipulated by a system call to the IPCHAINS binary, although the IPChains Perl module by Jonathan Schatz can also be used. The "at" queue system provides a simple way to trigger closing a port after a specified amount of time. The interface to this is provided by Jose Rodrigues' Schedule::At module.

Conclusion

Port knocking is a stealthy network authentication system that uses closed ports to carry out identification of trusted users. This method provides the means of establishing a connection to an application running on a completely isolated system on which no ports are initially open. The use of port knocking to safeguard information makes it possible to dramatically increase the level of protection without sacrificing accessibility.

Resources

Port Knocking Page: http://mkweb.bcgsc.ca/portknocking

Randomness Recommendations for Security (RFC 1750): ftp://ftp.rfc-editor.org/in-notes/rfc1750.txt

Codes and Ciphers: Julius Caesar, the Enigma and the Internet, Robert Churchhouse. Cambridge University Press, 2002.

"Crypto 101", Kurt Seifreid. Sys Admin, May 2002.

"Secure by Design", William Kramp. Sys Admin, May 1999.

Registered Ports List (RFC 3232): ftp://ftp.rfc-editor.org/in-notes/rfc3232.txt http://www.iana.org

IPCHAINS/IPTABLES http://www.netfilter.org

"An Introduction to Using Linux as a Multipurpose Firewall", Jeff Regan. Linux Journal, March 2000.

Martin Krzywinski is a bioinformatics research scientist at the Genome Sciences Centre in Vancouver, British Columbia, Canada. He spends his time applying Perl to problems in physical mapping and data processing automation. In his spare time he can be found encouraging his cat to stick to her diet. He can be reached at: martink@bcgsc.ca.