If we do not wish to fight, we can prevent the enemy from engaging us even though the lines of our encampment be merely traced out on the ground. All we need do is to throw something odd and unaccountable in his way. --Sun Tzu, The Art of War
OS fingerprinting is a process for determining the operating system a remote host computer is running, based on characteristics of the data returned from the remote host. This can be as simple as connecting up to the host and reading a service banner or as complex as statistical analysis of TCP initial sequence numbers and flags. An outsider has the capability to discover general information, such as which operating system a host is running, by searching for OS-specific differences in implementation of the TCP stack. In some cases, these differences can reveal information as detailed as the OS version number and processor architecture.
By pinpointing the exact OS of a host, an attacker can launch an educated and precise attack against a target machine. In a world of buffer overflows, knowing the exact flavor of an OS and architecture could be all the opportunity an attacker needs. By using software such as Netfilter for Linux, an administrator can evade accurate OS-fingerprinting methods and in some cases even manipulate the results gathered by the external force. While these practices should never be considered a sound security solution, sometimes they can deter and even confuse a would-be attacker if the host poses as an obscure network entity.
While fingerprint evasion does offer a nice layer of obscurity as to the actual OS a host is running, it does not in any way secure the host from various vulnerabilities. Security practices and policies attempt to raise the level of skill required to compromise the system, obscurity only attempts to mask the actual target. Even if your system appears to be running Microsoft IIS5 to the world, this won't protect you if you are running a vulnerable version of let's say sendmail, and some script kiddy's automated scanner attempts to exploit you. Fingerprint evasion is meant to deter attacks, not prevent them.
Before attempting to dissuade a potential attacker through OS deception, one must familiarize themselves with the tools and methods used in fingerprinting an OS. The term “attacker” here is used loosely and encompasses those who would attempt to fingerprint a host or those who might have the intentions of doing the system harm. Security has been, and in the writer's mind always will be, a sequence of measure and counter-measure scenarios. By becoming accustomed to the tools and methodologies available for this type of attack you cannot only prepare and plan for current engagements, but also gain insight into what the future may have in store.
Several publicly available tools exist for attempting to fingerprint an OS. Of these tools, one seems to be the popular choice: nmap (www.insecure.org/nmap/index.html) by Fyodor of Insecure.org. nmap uses several techniques for attempting to determine the host operating system from a network level, some of them primitive in their approach and others more complex, requiring a good understanding of the TCP/IP protocol and protocol standards. Of the methods nmap incorporates, some of the most notable are:
FIN Probing—by sending a packet to an open port on a host with nothing more than the FIN flag set in the packet, an attacker can glean information from certain hosts that respond to the requests. This behavior is non-RFC-compliant so it is a good indicator of OS.
TCP ISN Sampling—TCP packet ISN (initial sequence number) sampling can be a valuable way to determine and categorize the remote host. By watching the ISNs for patterns an attacker can make an educated guess as to the host OS.
ICMP Error Messaging—through the use of ICMP (internet control message protocol) error messages, an attacker can find useful information based on the host responses. Particular areas of interest are the checksums, error message echo integrity and TOS (type of service) fields in the reply packets.
TCP Options—perhaps the most revealing aspect of any TCP stack is how it handles optional TCP flags and data. By making specific requests to a host and varying window scales and segment sizes, one can determine which operating system a host is running based on its willingness to accept or respond with these optional parameters.
While all of these methods of OS fingerprinting are at the packet level, great care should be taken to understand your system at a service level. An attacker could sort and compare packet structures but will often simply query a web server for the “Server” field in the HTTP response header. Knowing which services readily identify themselves, and more importantly the operating system architecture, will show us other avenues that can be used for remote information retrieval.
Client modesty (or lack thereof) can be an excellent way to glean information from a host as well. Unlike the other options, this process can be completely passive. By watching how a client application presents itself to a service, you can make a reasonable guess at the operating system and possibly the architecture. Of these clients, web browsers, e-mail clients and IRC (internet relay chat) clients are most often the biggest offenders. If we were on IRC and requested a CTCP version from a user, and received the reply of “mIRC32 v5.81 K.Mardam-Bey”, we could make an educated guess at this point that the host is running some form of the Windows operating system.
Finally, there is exploit testing. While less tactful, it can nevertheless be useful in discovering the operating system of a host. By initiating a series of OS-specific denial-of-service attacks an outsider can test to see if a host is vulnerable. This can determine which rating system a host is running, usually down to the patch level. The Windows community should be grateful that Fyodor and the other fingerprinting-tool authors didn't decide to incorporate this method into their usual slew of scanning techniques.
If you have read up to this point, you are at least no doubt a little curious as to why one would go to the trouble of OS fingerprint evasion. Good question! I think the logic here varies from person to person. Everyone has his or her own reasons for wanting, or not wanting for that matter, operating system obscurity.
For some, the extra layer of obscurity helps them feel fuzzy and warm inside. Like the people who feel the need to remove the issue banner from their Telnet login screens, but resort to Telnet rather than SSH for remote access security (obscure, but technically less secure). For others, the notion of operating system obscurity at the network level allows them to fine-tune and tweak their IDS (intrusion detection system) since they have a fairly good idea not only of what should be coming into their network, but also of what data should be leaving it (obscure, cautious and hopefully secure). Some might even have a need for security, where every network they plug in to is a potential hostile network; and the more obscure their operating system is, the bigger the window of opportunity they have to complete whatever the task is at hand without being noticed (obscure, cautious, secure and probably reading your e-mail). Finally, there are those of us who do it for fun, because we can and because we get some small kick out of being able to fool the unknown individuals around us who persist on launching scans in our direction (yes, guilty as charged).
Now it's time to try our luck at fingerprint evasion. Familiar with some of the common techniques used in determining a host's operating system, we can reverse engineer these concepts to aid us in hiding our operating identities.
First, we need to make sure that all patches are in place and the system is secured. As I stated before, obscurity should only be entertained after security has been implemented. I'm sure some would disagree here, relying solely upon obscurity for their means to a secured system, but what good is obscurity if that script kid33's automated script gains root on your machine tonight? I'm willing to bet once he has root access, what flavor of Linux you are running isn't on his or her list of things to figure out.
Second, we need to observe our services. Do they match up with the operating system we are hoping to pass ourselves off as using? In most cases this isn't as much of a concern since most UNIX environments share similar if not the same services. But if you are hoping to present yourself as a Windows machine, or even a Cisco router for that matter, it may not be to your advantage to show up having IRCd running. Make an effort in matching up your services with a suitable decoy host.
While we're on the subject of services, it is also a good idea to begin greping through the source code of these services looking for banners or common identifiers of the services. Some subtle identifiers could be the supporting of ASP pages or web content that is served compressed in gzip format. For most people this will be a lot of work. Again, it's up to you to gauge what level of obscurity and conformity with your new decoy host you are trying to achieve.
Next, we need to look at how our host appears on a network vs. how our decoy host should look on a network. To make this a little bit easier I suggest studying already documented materials, namely the current fingerprint files used by the tools themselves. Time should be taken to note not just how your decoy host responds to usual queries, but also what special flags it supports in TCP. TCP flags are useful information for outsiders to determine what OS you are running. The fingerprint files don't include all possible responses a host might give, just simple techniques that work reproducibly. Depending on what level of obscurity you hope to achieve, it may be worth looking into fingerprint information not used by nmap (OSPF, OOB, IPv6, etc.). Or the joy of thoroughness could be outweighed by the sleepless nights you would spend gathering this information.
Finally, a decision needs to be made. Are you crafty, or are you paranoid? If you answer to the latter, then you most likely want to continue by obfuscating your client software. As mentioned above, a host's client software tends to give out all kinds of information regarding the system, either directly or indirectly. In our previous example an IRC client lists itself as being for Win32, but there are also more subtle ways of determining a host, such as reading the e-mail headers of outgoing mail. Once again, it all comes down to how many sleepless nights you are willing to spend before your system meets your criteria.
OS fingerprint evasion is like any other aspect of security; it takes planning, proper execution and most importantly, understanding. If security policies are not properly implemented, the system could be more vulnerable than if these policies were not implemented at all.
Popularity gives way to recognition. In most realms of software, popularity is a great thing; it brings attention to all your hard work and determination. In the case of OS fingerprint evasion, recognition works against you. If you are using a popular tool or package, eventually vulnerabilities and particulars will be discovered; this is inevitable. These same software-specific identifiers will allow others to fingerprint your counter-measure accurately rather than the operating system itself.
Most every OS attempts to make its TCP ISN sequencing random, in attempt to thwart TCP hijacking and more complex attacks on the system. If your chosen implementation of evasion attempts to alter TCP initial sequence numbers, great care should be taken to ensure you do not downgrade this functionality and put your host at risk to these types of attacks.
As with any software package that makes it onto your system, application security should be a primary concern. Part of the evasion process is masking existing services; the other comes in the form of code, which is meant to filter your traffic and mask what you put on the wire. Great care should be taken to ensure that the application produced for this task is secure through good programming practices and rigorous testing. All it takes is one poorly thought-out strcpy() to turn this asset quickly into a liability.
One of the evasion tactics previously listed is to alter the service banners of software that identifies itself. Be careful because some add-on software packages actually use these same banners to determine compatibility with the current system software.
Having established that evasion does not mean security, we need to look at another aspect of this process, namely performance. Since a good evasion setup filters your traffic en masse, it is feasible that system performance will suffer. Obviously if you have a site that hosts web pages for 10,000 clients, performance is a bigger issue than if you simply have a Linux box set up somewhere for you and your friends to check e-mail and IRC. As an administrator, you need to decide which is the bigger reward for you (and your users), performance or privacy.
To illustrate the feasibility and relative ease of fingerprint evasion I have included a small sample user-space application (OSFPE) for Linux, which makes use of the Netfilter kernel modules [see Listing 1 at ftp.linuxjournal.com/pub/lj/listings/issue89/4750.tgz]. Through the use of such software as Netfilter in Linux, OS fingerprint evasion is becoming increasingly more efficient. Similar modifications and applications are sprouting up all over the place; in BSD it is possible to accomplish this task via ipfilter and a moderate amount of code (during the time of this writing ipfilter has been removed from the BSD CVS tree, sorry guys). Windows users (who are by far at the biggest disadvantage in this arena) are discovering ways to shim their TCP/IP communications, and with the inception of Libpcap for Win32, capture and forge their own packet responses.
Netfilter, as stated by its author, is “a framework for packet mangling”. Sounds fun, eh? Netfilter interfaces with the Linux kernel (kernels 2.4.x and above to be exact) and registers hooks for each protocol. If the proper rules are in place, these hooks capture incoming or outgoing network traffic that match specified rules. These packets are then processed and marked for either NF_DROP to have the packet dropped, NF_ACCEPT to accept the packet for normal processing on the stack or NF_QUEUE to have the packet queued for manipulation in user space. If the packet gets queued for manipulation in user space, the ip_queue driver places it in a queue; it is then handled asynchronously by any applications running in user space that have registered themselves for these types of packets. When these applications pull the packets from the queue they have the ability to manipulate, accept and reject the packets. If the packets are accepted, they are handed off to the next application running that has registered for such a packet. If the packet is flagged for NF_DROP, the packet is dropped and processing of that particular packet ceases. Through the use of Netfilter, applications in user space essentially have kernel-level control of network traffic.
iptables is an application used to interface with Netfilter to set, view and remove a system's current network filtering rules. I make mention of iptables here because in developing the proof-of-concept code we felt it was a better idea to introduce users to the iptables program for rule administration rather than having the application handle them. This will allow people to better understand what is going on with the packet queuing.
By taking advantage of the Netfilter modules and iptables rule administration program we were able to set up rules to capture incoming UDP, TCP and ICMP packets. Based on the incoming packets and the source host we either allow them to access the system normally or craft responses to appear as a Windows host, as defined in one of nmap's OS fingerprint entries. Here is the fingerprint we were attempting to match, and a brief walk-through on how we accomplished this goal:
TSeq(Class=TD|RI%gcd=1|2|3|4|5|8|A|14|1E|28|5A%SI=<1F4) T1(DF=Y%W=2017|16D0|860|869F%ACK=S++%Flags =AS%Ops=M|MNWNNT) T2(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) T3(Resp=Y%DF=Y%W=0%ACK=O%Flags=AR%Ops=) T4(DF=N%W=0%ACK=S++|O%Flags=R%Ops=) T5(DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(DF=N%W=0%ACK=S++|O%Flags=R%Ops=) T7(DF=N%W=0%ACK=S++%Flags=AR%Ops=) PU(DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=E%RIPCK=E%UCK =E%ULEN=134%DAT=E)
The first line states that we need to build a time dependent (TD) TCP sequencing or one that has random increments (RI) equal to, but not greater than, 0x1F4 (500). This was actually pretty easy to accomplish, or match up I should say. First we grabbed the incoming packet, took the TCP sequence number, generated a psuedo-random number between 1 and 500 and added the values together. This met both the random increment and greatest common denominator (gcd) requirements for the fingerprint.
Next we broke down all the various packet tests (T1-T7) and created cases for them in our TCP handler. All of these are pretty straightforward and simply dictate how the host should respond to different types of packets to open and closed ports, the exact tests and their parameters are covered more in-depth in Fyodor's paper on remote OS detection.
Next we matched up our response for a UDP port-unreachable query. What nmap does here is send a UDP packet to a closed port on the host and wait for a response in the form of an ICMP port-unreachable packet. ICMP port-unreachable packets simply tell the querying host that the port to which they attempt to deliver a UDP message failed because there is no listening UDP service on that port. On some networks these messages never get sent back and are dropped at the router. In order to conform to the fingerprint we made an effort to send back what they were expecting.
Finally, as an extra little bonus we sent back Syn-Ack packets to the host for specific ports on our host if they happen to scan for these TCP ports as being open. Similarly, we sent back no response for particular UDP ports that we want to appear to be open on our host (as stated above, only closed UDP ports send back a port-unreachable message). When the scan of our host is complete, it should appear as though TCP ports 135 and 139 and UDP ports 135, 137 and 138 are open. If we attempt to fingerprint our host we should match up with the above-listed fingerprint and get the listing as “Windows NT4 / Windows 95 / Windows 98”.
As a final note, proof-of-concept code is just that, a little piece of programming used to prove a point. Do yourself a favor and don't run this on a critical device. Open it up, learn from it, modify it, exploit it, but don't depend on it. I've made an attempt to keep the code safe and somewhat readable (arguable), but I can't promise anything.
Thanks to Rex Warren for all his hard work aiding me with this paper and the sample code, Fyodor for allowing me to reference all his hard work and for such a great security tool, Dan Kurc for reading over my code and calling that baby ugly (Hey! It's my first C program), Sir Dystic for C-isms and Courtnee.