TCP Stealth hides open ports
TCP Camouflage
Finding open UDP or TCP ports on Linux is easy. Hardcore hackers use Netcat [1]. If you prefer an easier approach, you can use Nmap [2]. In addition to identifying active services, you can even profile the underlying operating system in many cases. In fact, a port scan can be a useful troubleshooting tool for an administrator's bottom line.
There is a dark side to port scanning, too, however. Unfriendly people can use the same methods to spy on IT systems. After identifying what are basically open doors, an attacker can start on other investigations. In a worst-case scenario, the attacker learns which version of which program is keeping a port open. A short search on the Internet for potential vulnerabilities or exploits is quickly accomplished.
Administrators can effectively block this attack vector by obfuscating the open ports. For an outsider, it looks as if there were no easy targets; only experts know how to gain access. Probably the best-known technology in this context is port knocking, and there are several implementations [3].
What Happened Thus Far
Figure 1 shows a typical setup with port knocking. The components involved here are the client and the server application, with a firewall to keep out uninvited guests and another process that waits for the agreed upon knock signal. This process is often known as the port knock daemon. The daemon and the clients have a shared secret. This can be a knock sequence or network packets with specific content.
The connection is established in four steps. The client attempts to open a connection to the server, and the firewall blocks this. At the next step, the pre-shared key referred to previously – that is, a specific order in which the client attempts to address other ports or a specific packet content that is sent by the client – is used. The port knock daemon detects this and tells the firewall to let the client request through. The final step is then establishment of a totally normal connection.
If you want to try this out yourself, you can check out previous articles in Linux Magazine [4]-[6]. Alternatively, you can start with the Knockd [7] project. The TCP Stealth approach described here is also implemented in recent versions of Knoppix.
The various Internet protocols will behave differently. As a connectionless protocol, UDP needs to be treated differently as early as the scanning stage. Additionally, there are several scan type variants for identifying open ports on the TCP side (Table 1).
Table 1
Known Port Scan Types for TCP
Name | Description |
---|---|
Connect | Performs the TCP handshake to complete the connection |
SYN | Sends only one packet with the TCP SYN flag set
|
FIN | Sends only one packet with the TCP FIN flag set
|
Xmas | Sends only one packet with all TCP flags set |
The known port knocking setup has two inherent vulnerabilities, the first being that the lock mechanism is detectable if the attacker can sniff the network. The additional steps required to open a connection are obvious, and the original rejection of the connection followed by the connection then being accepted is a giveaway.
The second vulnerability relates to man-in-the-middle attacks. After successfully completing port knocking, the port knock daemon and the firewall leave the playing field. They cannot prevent the attacker from retroactively hijacking the established connection. In the rest of this article, I will just be looking at TCP as the transport protocol.
Initial Stealthy Steps
TCP Stealth is an extension of the initial three-way handshake for establishing a connection. A request for comment (RFC) [8] has already been submitted to the Internet Engineering Task Force (IETF) [9]. The method, also known as silent port knocking, was created by Julian Kirsch as a student project at the Technical University in Munich in the summer of 2013 [10]. Its original name was Knock, and the project website [11] still uses this moniker. Kirsch submitted his master's thesis on the subject of TCP Stealth in 2014 [12].
If you have been following the port knocking scenario for a few years, you might remember SilentKnock [13]. The project uses the same seven-year-old idea [14] as TCP Stealth. In 1997, Craig H. Rowland described how information can be transmitted camouflaged as the standard TCP header.
Listing 1 shows the TCP header. Most of its parts are predetermined by their function and are not suitable for embedding data. One exception to this is the initial sequence number (ISN) of the TCP connection. To a certain extent, this also applies to the acknowledgement number (ACK). The use of the timestamp as a carrier is also conceivable.
Listing 1
TCP Header
01 $ cat rfc793.txt 02 ... 03 0 1 2 3 04 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 05 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 06 | Source Port | Destination Port | 07 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 08 | Sequence Number | 09 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 10 | Acknowledgment Number | 11 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 12 | Data | |U|A|P|R|S|F| | 13 | Offset| Reserved |R|C|S|S|Y|I| Window | 14 | | |G|K|H|T|N|N| | 15 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | Checksum | Urgent Pointer | 17 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 18 | Options | Padding | 19 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 20 | data | 21 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 22 ...
The RFC specifies a maximum size of 32 bits for the ISN. This limits the space for payload data. The input data for the random ISN is the shared secret, the IP address and port on the target, and possibly also the timestamp. The secret is known to both the client and server and is thus a pre-shared key (PSK). In the current implementation, this key is a simple string, and there is precisely one string per port.
To generate the ISN, I will use the well-known MD5 [15] hashing method. If you take a closer look, you will see that timestamps play an important role. They are in fact the only variables in the computations – the ISN, PSK, target IP, and port are constants. The precise approach is described elsewhere [8] [12].
Stealth Socks
How does TCP Stealth work in practical terms, though? Targeted manipulation of the ISN requires modifications to the TCP stack. On Linux, the stack is implemented in the kernel. When I was writing this article, the plain vanilla kernel knew nothing about TCP Stealth. In other words, users wanting to try this have no alternative to patching the Linux kernel. For more details, see the box "TCP Stealth in Daily Linux Use."
TCP Stealth in Daily Linux Use
The plain vanilla kernel is not familiar with TCP Stealth. Right now, it doesn't look as if the situation is going to change any time soon [16]. On the project website, you can download the patch for kernel versions 3.18 and 3.19.x [17]. If you are uncertain as to how to proceed, you can either look for help on the Internet or check out Julian Kirsch's [12] master's thesis. You will find an entry for TCP Stealth in the kernel configuration below Networking options .
You should check to see whether the loaded Linux kernel actually supports stealth TCP. When you are taking your first steps, in particular, you have no way of knowing whether the problem is caused by the application or the underlying kernel. It is definitely worthwhile to look at the sample programs for your first experiments; they are also available on the TCP Stealth project website [11].
You can also pick up a patch for OpenSSH on the project website. This is interesting because it is clear here that the application ensures confidentiality of the data you transfer. The patch extends OpenSSH, adding a TCPStealthSecret
option – both for the server and for the client. The approach for patching is described online [11] [12]; however, it was only partially successful in our lab. I have posted a description of a slightly different approach online [17].
In any case, the result will be an SSH client and a matching daemon that use the TCP Stealth functionality. For test purposes, you can use the new -z tcp_stealth_secret
command-line parameter. In daily use, however, you will probably prefer to keep the key in the configuration of sshd_config
and ssh_config
, or $HOME/.ssh/config
. Otherwise, a simple ps
command could reveal the shared secret.
A short movie that I posted on YouTube [18] demonstrates how TCP Stealth and OpenSSH cooperate. For example, the TCPStealthSecret
option enables TCP Stealth in OpenSSH and configures the PSK at the same time:
$ grep -i stealth /etc/sshd_config $HOME/.ssh/config /etc/sshd_config:TCPStealthSecret "Too many secrets :-)" /home/user/.ssh/config:TCPStealthSecret "Too many secrets :-)"
The patch only changes 11 files all told and only a handful of functions in those files. These include changing the way the client part of TCP opens connections (e.g., tcp_v4_connect()
and tcp_v6_connect()
) and their counterparts on the server side, of course; this mainly means tcp_v4_do_rcv()
and tcp_v6_do_rcv()
.
You need to enable TCP Stealth in your network stack before opening the connection. This means the required configurations start with the sockets. On Linux, a system call to setsockopt()
takes care of this. Finally, the kernel has a new configuration option (Figure 2) and an extended TCP socket structure.
A Linux kernel that supports TCP Stealth is only one part of the preparations that you need to make. In the second step you need to prepare your choice of application for accessing menu functions. A sample implementation of both the client and the server side is available online [11] [12]. The decisive part is shown in Listing 2.
Listing 2
Source Code Snippet
01 #define TCP_STEALTH 26 02 [...] 03 # Password: 04 char secret[64] = "This is my magic ID."; 05 [...] 06 # Setsocket-call: 07 if (setsockopt(sock, IPPROTO_TCP, TCP_STEALTH, secret, sizeof(secret))) { 08 printf("setsockopt() failed, %s\n", strerror(errno)); 09 return 1; 10 } 11 [...]$
The new system call setsockopt()
can also be traced using analysis tools such as strace
:
$ grep setsockopt tcp_stealth_server.strace 29392 setsockopt(3, SOL_TCP, 0x1a /* TCP_??? */, "This is mymagic \ ID.\0\0 \0\0\0\0\0\0\0 ...\0\0\0\0\0\0\0\0\0\ 0\0", 64) = 0
If you trace setsockopt()
with Strace (Listing 2), you can easily see that TCP Stealth does not perform any encryption.
Despite this, TCP Stealth can ensure data integrity (more on that later). At this point, it is important to note that a local attacker can sniff certain information because they are simply available in the clear. Additionally, authentication only works in one direction. The server checks whether the client is authorized to open a connection, and it checks the PSK as a credential. However, the client cannot check whether it is really talking to the right server.
Buy this article as PDF
(incl. VAT)