A critique of port knocking - author's response
Send comments to firstname.lastname@example.org.
Recently, an article appeared on NewsForge about port knocking. Titled "A critique of port knocking" and written by Arvind Narayanan, the article raised some justifiable criticisms. However, it also contained common misconceptions about port knocking.
Here, I’d like to present another view point of the topics Arvind discusses and argue that port knocking, a method never presented as a security "silver-bullet", is not as bad as Arvind makes it out to be. Arvind’s article concludes that "there are several security lessons we can take from port knocking." While I agree with this point, I don’t agree about the nature of the lessons.
The article starts off with a motivation of why you might want to use port knocking.
Suppose you want to be able to retrieve files from your Linux system remotely. The "standard" method of running the SSH server on port 22 is notoriously inadequate. OpenSSH, which is the SSH server on the majority of Linux installations, suffers from regular exploits of buffer overflow and other vulnerabilities, and you neither have the time to keep up with the patches nor want to make the effort -- you'd rather put up with not being able to access your files. This is where port knocking might seem to help -- but don't count on it.
I disagree that SSH is "notoriously inadequate" and that another method is needed. Although vulnerabilities have been found in SSH, this fact does not make it inadequate. Vulnerabilities make an unpatched version of SSH susceptible to exploit, but not inadequate. The vulnerabilities in a given implementation of the SSH framework may render it completely insecure or even useless, depending on the nature of the vulnerability, but they are not necessarily indicative of a fault in the approach. OpenSSH offers secure, encrypted communication based on publicly validated strong encryption. This isn’t just semantics. Arvind's article appears to interchange the notion of an algorithm with the notion of an implementation of the algorithm.
SSH provides a way to mediate communication across other ports through a secure tunnel, thus centralizing system security at a single point. Therefore, keeping SSH patched is critical. A hole in SSH can undermine the confidence of using it as a means of secure communication. Your communication may, in fact, be transparent to others, or worse, your system may be vulnerable to root exploits.
Arvind’s point about not having time to keep up with patches or not wanting to make the effort is misplaced. Patching is not merely an option, it is a requirement of keeping a system secure.
I’d like to draw an analogy to driving a car but not wanting to make the effort of putting in gas, or making repairs. When you operate a car, you admit to the need for gas or maintenance, and thus make scheduling both part of the driving experience. The analogy runs out of gas (sorry) when making a directly comparison to software, which does not degrade like mechanical parts. However, software may be compromised if someone sufficiently clever (or a large number of semi-clever individuals) use the software long enough, or stare at the code.
port knocking as a layer
Port knocking protection is an extra layer, in addition to other authentication systems behind a firewall.
Successful authentication with the port knocking layer permits attempt at authentication with the protected application.
Disabling the port knocking layer results in an off-line (thus secure) system. Even if some ports remain open, the underlying authentication agent (e.g. SSH) remains intact.
Expecting that no more vulnerabilities in OpenSSH will be discovered is a bit like expecting your car to never break (you know it will, but you just don’t want to believe it). At this point, I want to say that I am not making any statements about the capability of the programmers of any SSH implementation. OpenSSH is an excellent project.
It’s also important to realize that port knocking is not an alternative to SSH, or any other authenticating system. I have never proposed that it should replace SSH nor that disgruntled SSH users should turn to port knocking as a form of reprieve. Port knocking provides an auxiliary layer of security on top of an existing authentication scheme, but it cannot exist as a layer on its own. More about this later.
Port knocking is a method of "message transmission across closed ports." It works like this: initially a firewall blocks all ports on the server. The client issues a series of connection requests (knocks) to different ports; these are, of course, dropped since the ports are blocked. However, there is a daemon that monitors the log files for connection requests, and the sequence of requests serves as an encrypted code. If the code makes sense to the daemon, it enables SSH or another service (for a particular IP address and on a particular port encoded by the knock sequence).
Arvind provides a good description of port knocking here.
Port knocking acts as an extra layer of security. It is also claimed to have the advantage of stealth: an attacker can't detect whether you're using port knocking or not.
Yes, the claim is that an attacker cannot directly deduce whether you are using port knocking. What I believe to be true is that an attacker cannot use methods like port scans to determine whether you are running a port knocking daemon. An attacker may sniff traffic and, over time, deduce by the profile of traffic that port knocking is used. For example, if encrypted sessions are always preceeded by a series of SYN packets (the knocks).
Let's dispose of the stealth argument first. Suppose that port knocking becomes widely used (and surely that's the point of its existence?). Say it is running on 10% of all servers. Then an attacker simply assumes that the target machine is using port knocking and proceeds to attack it. The attack succeeds with a probability of one in 10 (of what it would have been if stealth were not employed), but those odds are worthwhile to the attacker, considering that the cost of the attack is far smaller than the expected gains from a successful attack. This kind of technique is quite common in port scanning -- crackers use multiple automated methods, each of which has only a small probability of success, but which taken together prove very effective.
Arvind’s analysis that 10% of the attacks succeed is incorrect. It is impossible to determine this fraction without more information. 10% is not only extremely high, it is unreasonably high — likely by many orders of magnitude.
The purpose of port knocking is to fill a specific niche in security — to provide security in depth and to harvest low-hanging fruit. Its ultimate adoption by the community, which may or may not happen, will be indicative of the method’s usefulness but adoption is not of the reason for its existence. In fact, I’d like to argue that the largest benefit of port knocking is realized by early adopters. Those who implement port knocking on their systems before any organized schemes to penetrate such systems are formulated receive a security bonus.
At this point I want to point out that attempting to attack a system protected by port knocking is an extremely loud process. Port knocking forces the attacker to perform easily detectable actions, which in turn give away their presence. Attacking a port knocking system may take the form of denial of service aggression or attempt to guess a valid knock sequence.
The potential of success of the latter is extremely unlikely, far less likely than guessing someone’s 10-character password (compare 25516 possibilities for a 16 port knock to ~7010 possibilities for a 10-character password). Nevertheless, both attacks require that the aggressor attempt to connect to closed ports. Assuming that the attack develops over a short period of time, it’s easy to detect unauthorized connection attempts with an intrusion detection kit, like snort. A smart attacker may polling a port once per hour or once per day. The chances of detecting this are lower, and if someone has this kind of patience, you may have bigger problems.
The stealthiness of port knocking is limited in scope — your machine appears uninteresting to someone who's scanning a whole range of IPs for open ports. A more dedicated attacker has a multitude of methods to infer that the target machine could be using port knocking, such as sniffing the (highly distinctive!) stream of packets in the knock sequence, observing that a machine known to host a service appears to have no ports open, social engineering, or even something as silly as finding a entry like "I got port knocking working today! w00t!" buried deep in the user's Weblog.
The stealthiness of port knocking is limited in scope, by design. As with other security systems, steps can be taken to broaden the scope. Arvind’s first statement that a machine protected by a port knocking system "appears uninteresting" to someone who is performing a port scan is true. Therein lies part of the "concealement" provided by port knocking. In order to mount an attack, an attack vector needs to be determined — this may be achieved by port scans and tickling services to expose underlying details of implementations (vendor, version, patch level etc). Without such interrogation, a potential attacker moves on.
The point to make here is that the initial investigation is probably done by an automated process (on a single compromised machine, a network of such machines, or from the attacker’s home base) which returns potential targets for attack for further investigation (by more automated tools). If you don’t make the list of potential targets, you are safe. You are not completely safe, you are simply safe in this particular case. However, the majority of attacks are instigated by unskilled individuals in possession of such automated tools who do not wield a vendetta against a specific machine. Thus, port knocking harvests the low-hanging fruit — elimination of risk posed by the majority of attacks.
Even more, knowing that a machine is protected by port knocking does not necessarily bring one closer to mounting a successful attack. The knowledge that a machine is running port knocking, if no unprotected services exist, is "almost" equivalent to the knowledge that a machine exists and has no open ports (though it may be running some unreachable services). I say "almost" because of the extremely unlikely chance of successfully guessing a knock sequence.
I won’t address Arvind’s point about social engineering. The strength of a computer-based security system cannot be evaluated on the basis of the gullibility of the individuals using it. If you manage to persuade me to give you my password, it is no indication that my password is a weak one.
Security through obscurity is bad when it is the sole defense. This is true in a general theoretical sense, and there is always a cost associated with making the system more complicated in order to add an extra obscurity layer. In the case of port knocking, the hoops you need to jump through to achieve stealth far outweight the marginal benefits. Furthermore, there are much cleaner ways to achieve stealth, as we will see in a moment.
Arvind did not motivate the fact that port knocking is a form of obscure security. In fact, it is not. Port knocking does not create an obscurity layer, although it may appear to be the case because one benefit of port knocking is concealment. Port knocking has natural access control (connection attempt log files) and attacks on the system are loud and can be detected.
Moreover, port knocking is never meant to be the sole defense. Port knocking is an extra security screen. If you’ve seen the remake of the Italian Job, you may remember the safe which contained a thin pane of glass to foil a safe cracker. Why put a delicate pane of glass in an otherwise robust, steel safe? So that when someone drills the safe mechanism, the brittle glass will crack and trigger an essentially irreversible locking mechanism that cannot be breached without cutting the safe open. Port knocking plays a similar role to this brittle security screen — as a security layer it exposes attacks and, if it fails, it results in the system going off-line. More on this later.
Adding port knocking does increase the complexity of your system. Ultimately, you need to make a decision whether such an increase warrants any realized increase in security. In some cases, it does. Port knocking is well-suited for administrative back-doors on machines that don’t receive a lot of traffic, for example. Having an SSH service listening 24/7 on a machine which receives one or two logins per month may be unnecessary — port knocking can be effectively used to hide the SSH service.
Once stealth is taken out of the equation, it becomes clear what port scanning really is. A port is not a physical object but merely a 16-bit integer. A sequence of knocks is therefore just a sequence of bits that lets you in -- in other words, a passphrase. So port knocking is simply a method to authenticate yourself with a passphrase. The obvious question then is, in what way is this convoluted and inefficient method of sending a passphrase across a network better than the straightforward method of putting it in a packet and sending it? The obvious answer is, it is not. (The second obvious question is, how is it different from authenticating yourself with a passphrase using SSH? But we'll get to that shortly.)
Arvind correctly points out that port knocking is a method to authenticate yourself with a passphrase. This is how port knocking was presented in the original Sysadmin Magazine publication, Port Knocking: Network Authentication Across Closed Ports. The "port knock" terminology isn’t mean to hide the fact that the method involves authentication.
There are benefits of this kind of authentication. The method uses the server’s firewall to act as an authentication agent. With anti-DoS measures, it is extremely difficult for a single, casual attacker to exploit a port knocking layer by overwhelming IPTABLES in an effort to escalate their privileges on system. By using the log file as a "messaging system" by which the port knocking daemon would detect the knocks, I was able to create a proof-of-concept implementation very quickly. The implementation is neither elegant nor meant for production systems — it’s a sandbox. Although it’s functional and (I’m going out on a limb here) can be used to protect small systems, there are other implementations to choose from.
Port knocking subverts the notion of closed ports and uses them for the transfer of information. The fact that the information flows across not inside a packet data payload but rather in the port numbers themselves is not just an academic curio. This kind of "subversion" of information transfer is beneficial in cases where the packet payload is being snooped. Of course, eventually, the snooper may realize that it is the port number that carries the information.
Given that port knocking is an additional authentication system, layered on top of a network service like SSH, what is the justification of having an additional system to validate a user’s identity. As Arvind asks, how is this different than using the SSH-based authentication?
The major difference is the agent of authentication. When authenticating with SSH, the user is interacting with a component of the SSH network service. The user can attempt to manipulate the interaction with SSH during the login phase, find an exploit and escalate privileges. If the SSH port is open to all incoming hosts, anyone has the opportunity to try to identify the SSH vendor and release and try an attack.
Port knocking offers authentication that is removed from a classical network service daemon. The authentication is mediated by the firewall (if a log file is used), or by monitoring traffic directly with pcaplib. Because of this, the authenticating agent cannot be exploited, in the traditional sense. The user is not communicating directly with the agent. The agent, in this case, is not analyzing packet payload but port numbers. Even if the agent were to somehow fail, or jump to root, the remote user is not connected to the agent and therefore cannot use the agent’s malfunction for their benefit.
This brings me to the point of what happens if a port knocking daemon is disabled by a remote attacker. This is possible if the implementation contains bugs which cause unexpected behaviour for some combinations of port numbers, for example. If the daemon dies, then no further authentication is possible and the system is effectively knocked offline. The down side: nobody can connect. The up side: nobody can successfully attack, either.
The passive nature of the protection means that a failure renders the system inoperable, not insecure. If on the other hand, the daemon malfunctions and opens a port and then dies, then we are merely where we started before port knocking was implemented: a port is open and now the attacker has to interact with the underlying network service, like SSH, and attempt authentication.
Therefore, the consequence of having the port knocking layer penetrated is not equivalent to penetration of the system. The attacker still has to penetrate the network service that was protected by port knocking.
Think of security as an onion. If properly designed and implemented, each layer presents a new hurdle to the attacker. This is called "defense in depth." But the onion structure comes at a cost: it becomes much more difficult to analyze the security of the system. All too often, the layers serve as obfuscation and give you an illusion of security while leaving a single weak point open to an attacker.
If the layers are independent of one another, then the difficulty of analyzing a security system scales with the number of layers. If the interaction between layers is well documented and understood and the number of layers is kept low then maintaining the system is still tractable. Arvind’s point that layers serve as obfuscation presents an overly cynical view of both the ability of system administrators and security in general. I don’t doubt that there are users of security systems who deploy and forget, but I hope that most do not.
Establish security perimeters and maintaining awareness is not simple. While there are automated tools that help slogging through the reams of data (acid front-end to snort, for example), it is ultimately the responsibility of the user to interpret and understand these data. The evaluation of a security system cannot be based on the worst-case scenario — a distracted system administrator who places too much faith in a security layer without understanding its limitations.
To avoid falling into this trap, build your onion from standard components and to put them together in standard ways. On the other hand, everything about port knocking is about implementing crypto primitives in an obscure manner. If you're using a key, you call it a key. You gain nothing by calling it a sequence of port numbers; in fact, you lose a lot. When you think of a key as a key, it's clear that you shouldn't store it in any public location. On the other hand, log files (which contain the key in the form of port sequences) can lie around for ages. This is a typical example of how nonstandard terminology and implementation complicates analyzing the security of a system.
Here Arvind brings up a very strong point: the port knock is stored in the firewall log file, which is potentially readable by anyone on the system. Although the log file may be readable only by root, it is still unwise to keep the clear knock stored on the system.
This is a failing of my implementation, which used to require a log file. It is not a failing of the underlying port knocking method. Since v0.30 of the Perl prototype, the pcaplib library is used (other implementations have long since done this) to listen to the packets without relying on a log file.
Let’s suppose for a moment what would happen if someone discovered a valid knock sequence. The knock sequence typically contains the remote user’s IP address, the port number that they wish to have opened, and other information, such as the length of time to keep the port opened for. Recall that the knock is encrypted using a strong cipher, like Blowfish, and sent to the daemon by the knocking client. If an unauthorized party discovered the knock sequence, either by reading the log file or by sniffing traffic, they would still have to do quite a bit of work to make use of the sequence. Feeding the sequence back to the daemon would permit access to the remote IP and local port combination stored in the knock. Spoofing their IP, the attacker may therefore gain access to connection to a port, but no more than that. Decrypting the knock sequence would also be quite difficult.
The point is this: if the knock is discovered and replayed, and no anti-replay measures are in place, the consequence is not a full system breach. Rather, a bypass of the outer layer of security.
There are two reasons why a port knocking attacker may be thwarted: one is that only some ports are allowed in knock sequences (customized by the user) and the second is that the knock sequence can be encrypted with a password. The first defense is an extremely poor one. Security should reside only in knowledge of the key, and not in bits and pieces sprinkled throughout the system. That's Kerchoffs' law, often used as a rallying cry against security through obscurity.
Limiting which ports are used in the knock, supported in the Perl prototype, is not presented as a feature that increases security. I coded this feature to offer flexibility to the user. Instead of assuming that a set of contiguous ports is available, I leave the user to define which ports to dedicate to port knocking.
The second defense is much better, but amazingly, it is presented as optional in the port knocking overview page. There is in fact little or no security in port knocking without encrypting knock sequences. Even with it, there are problems.
The document at this URL walks the reader through the basics of port knocking. I start with a very simple example and go from there. In a number of places I stress the need to encrypt the sequence.
Firstly, the unnecessary only-some-ports-valid "protection layer" complicates things. Suppose you decide on a list of 32 valid ports (the current implementation allows up to 256). How long does the port knock sequence need to be? You might think that since each port is a 16-bit integer, you need 8 knocks, so that you get 8*16 bits or 128 bits of security (virtually unbreakable). But since each port has only 32 possible values (5 bits), what you actually get is only 8*5=40 bits of security (trivially breakable)! Granted, this is a problem for the implementer and not for the end user, and the current (proof-of-concept) implementation appears to take care of the length issue. But it is easy to go wrong. This is a particularly serious problem, since there is bound to be pressure on the implementer to make the knock sequence as short as possible, since it can easily take a minute to complete a port knock. When trying to reach a compromise between the two conflicting goals, security flaws result.
The argument is hard to follow here. The problem either is with the implementation, or it is not. Regardless of how much information the user may think is stored in the knock, there is a correct answer and any confusion on the part of the user cannot be taken as a fault of the system. It is incorrect to generalize that 40-bit security is trivially breakable. It is breakable, given enough time and resources, but an attacker attempting to guess 40 bits would be rapidly spotted.
Secondly, there's the matter of what you're going to do about replay attacks. A replay attack is one where a router sitting between the client and the server passively sniffs what the client is sending, and sends the same bitstream after a while to the server, pretending to be the client. The standard defense is a challenge-response protocol, but that is of course ruled out because we don't want the server to send anything until the client authenticates itself. The documentation says that a flag field is added to the knock sequence and incremented for each port knock, preventing replay attacks. But this is nonstandard and dubious. What happens when the server is rebooted? What if the counter resets? As far as I can figure out, only a single-byte field is allocated for the flag, which makes resetting a real threat.
Port knocking precludes a challenge-response phase by its very design — the authentication method is performed by a "blind client". This is an essential part of port knocking. The client has no opportunity to determine whether it is (a) knocking on a system running a port knocking daemon (b) knocking on a system not running a port knocking daemon, or (c) knocking on a non-existent system. Given this fact, the client cannot easily manipulate the authentication process.
Preventing replay attacks can be accomplished by varying the definition of a valid knock over time, using some mutually agreed method by the client and server. Though fussy, even one time pads may be used.
On the other hand, I propose a more controversial answer. So what if a port knocking daemon suffers a successful replay attack? What does the attacker gain? The answer: the opportunity to authenticate themselves with the underlying network service, like SSH which is replay-proof. If the outer layer is not replay proof, but the inner player is replay proof, then protection against this kind of attack is still in place. Although this may not be a very satisfying justification, from a theoretical point of view, it fits into the context of port knocking, as a pragmatic tool to increase the security of a system. An implementation of port knocking creatively strengthened against replay attacks is needed.
In general, port knocking has too many points of potential attack. In particular, anyone with non-root access to the server should be able to trivially break the system. This does not seem to be of concern to the developers.
Umm, I think I’m missing the point here completely. Port knocking is meant to protect the systems from unauthorized external access. Internal security, to protect the system from those who already possess limited access, is a completely different problem. It is also a very hard problem. If you don't want non-root attackers on your system, focus on keeping them from obtaining non-root access.
There are several security lessons we can take from port knocking. There is a demand for barebones authentication system without bells and whistles that doesn't require maintenance.
Call me cynical, but I doubt such as system exists. The nature of operating systems, networks and communication is complex and any tool that restricts and monitors communication for the purpose of security in an maintenance-free manner has to be at least (a) as complex as the system it is trying to protect and (b) as smart as the attackers. Software will have bugs and attackers will continue to gain knowledge and resources. The corollary is that eventually a system will become obsolete and eventually useless. It’s my experience that the more automated and hands-free something is, the less flexible it is and less likely to accomodate security policies that change over time.
The problem with OpenSSH is that it tries to do everything. When designing a barebones system, it is essential to keep the target audience in mind and to decide the scope in terms of what features it will offer and what attacks it is designed to resist.
I interpret this statement as a direct contradiction of the previous one, in which a call is made for a single, bare-bones authentication system. The reason why OpenSSH tries to do everything, as Arvind puts it, is because it is a single system and needs to be both flexible and secure — a difficult balance. If you ask for a monolithic solution, be prepared to put up with either (or both) (a) changing your policies to be compatible with the system, or (b) patching the system instantly when required.
I like the fact that the port knocking implementation is not written in C, a language which is subject to buffer overflow bugs. The approach of layering port knocking over SSH instead of replacing it is certainly sound. On the other hand, reinventing cryptography is definitely unsound.
The implementation Arvind mentions is the Perl prototype. There are many others written in a variety of languages like C, Python and even BASH. I chose Perl because I am most comfortable with it and it is a good prototyping platform. I don’t think I tried to reinvent cryptography — any attempts on my part to do this would end in abysmal failure, I am sure. The Perl implementation uses standard strong cryptographic algorithms like IDEA and Blowfish, via the chaining module Crypt::CBC.
Let us therefore take the port knocking out of port knocking and see what we get. It is a simple system for manipulating firewall rules based on standard password-based authentication and symmetric key encryption. By implementing this hypothetical daemon in a high level language and zealously resisting the urge to add any other features, we can greatly reduce the bugginess and possibly achieve a reasonable approximation to zero-maintenance. Stealth can be added: have the service run on a UDP rather than a TCP port. Since UDP is connectionless, the concept of closed and open ports is much weaker than for TCP. By having the application send out an ICMP_PORT_UNREACH message whenever a packet is received on that port (but nevertheless processing the packet), it can be made to look like a closed port to anyone who doesn't know the password. The extra obscurity might be useful if the overall system is simple enough. On the other hand we might forgo stealth and implement a challenge-response protocol instead to reliably stop replay attacks.
Arvind’s suggestions are at the level of implementation. Manipulating firewall rules across closed ports using encrypted authentication is a valuable way to enhance security. It sounds like Arvind agrees, but differs in opinion about how such a system should be implemented. I certainly don’t think that the Perl prototype is the best way to do it — it’s one way that I was able to do it. I hope that the underlying basic method is sufficiently useful to enough people to give rise to more implementations, some of which will stand the test of time and critical evaluation.
In conclusion, even though port knocking addresses a problem that many face, the solution is needlessly complicated and far from optimal.
I agree, but from a different perspective. Port knocking is not the ultimate solution, but in its essence it presents a way of enhancing the overall security of a system by using a combination of concealment, personalized triggers and access control. It renders attacks extremely "loud". It’s a utilitarian solution to a pressing problem.
Arvind’s approach to criticizing port knocking lacks pragmatism. Yes, from a theoretical perspective port knocking lacks some of the features that one looks for in security system. But when it is used to complement an existing security system that has these missing features, a system is much more difficult to breach. And isn’t that the point? A security system that works and fits in with what already exists?
The concerns in the world of computer security change rapidly as new vulnerabilities are found and forms of attack appear. It is a world in which one does not seek panaceas, I think everyone has learned that such things do not exist. A 6 or 12 month respite against a majority of attacks is probably good enough. That is, until something better comes along.