IP Personality Gaël Roualland - Jean-Marc Saffroy $Id: ippersonality.sgml,v 1.27 2001/07/23 22:19:36 g_roual­ land Exp $ IP Personality Documentation (List : ippersonality-devel@lists.source­ forge.net) ______________________________________________________________________ Table of Contents 1. Introduction 2. Limitations 3. IP Personality 3.1 Features 3.2 The journey of a packet in PERS 3.2.1 TCP packets 3.2.2 UDP packets 3.2.3 Common part for IP packets 4. Configuration 4.1 Command line options 4.2 Configuration file 4.2.1 Identification 4.2.2 Generic TCP parameters 4.2.3 Sequence Numbers Generator Parameters 4.2.4 IP ID Generator Parameters 4.2.5 Options Reordering Parameters 4.2.6 TCP decoying Parameters 4.2.7 UDP Decoying Parameters 4.3 Language 5. Example 5.1 Configuration file 5.2 Test network 6. Pseudo Code 6.1 Overview 6.2 Instructions 6.2.1 TEST 6.2.2 JMP 6.2.3 PUT 6.2.4 SET 6.2.5 RET 6.3 TCP Options 7. Development Tools 7.1 Debugging 7.2 Osdet 7.3 Perscc 8. References ______________________________________________________________________ 11.. IInnttrroodduuccttiioonn Except for the regular behavior specified in RFCs, every IP stack has some specific ways (due to coding policies, bugs, optimizations...) of responding to incoming traffic, especially when handling abnormal packets that do not strictly follow the RFC. These specificities are used by network analysis software to guess what OS a remote host is running. They probe the OS by sending the host a bunch of abnormal packets (mangling parametres such as fragmentation, TCP flags, unused/reserved fields, size of packets, ...) and comparing the results with a signatures database of known operating systems. Such tools are used by system administrators to map an heterogenous network, but also by crackers trying to learn more information about a remote host or a subnet, in order to use appropriate attacks and improve their chance of compromising the systems. _I_P _P_e_r_s_o_n_a_l_i_t_y is a _n_e_t_f_i_l_t_e_r module designed to be able to have different 'personalities' network wise, that is to change some characteristics of the network traffic, depending on different parameters. This especially enables fooling such tools in thinking a remote host is running a specific system when it is actually running another one, so as to hide or protect hosts that would otherwise vulnerable, or to build "honey pots". 22.. LLiimmiittaattiioonnss By looking at the tests nmap performs, one can notice that they are all based on abnormal or at least weird packets, hence easy to detect, so as to send counter-measures. This way, it seems possible to change replies from a local machine when we receive such packets. However, these changes have some drawbacks: · some characteristics of OS are related to the host architecture (for instance page sizes on various CPU) which could lead to performance issues; · some of these changes are more "political" choices of the IP stack (initial sequence numbers, window sizes, TCP options available...). Tweaking those allow to fool a scanner but might break regular connectivity by changing network parameters. It could also make the system weaker if the emulated IP stack is not as strong as the initial one. However, such modifications are possible in most cases for the local host. But it is not so easy when it comes to routed hosts: · since the local host doesn't know the exact state of a remote IP stack, not all tests can be done there, making it hard to guess a valid response for a remote host; · the behavior of routed hosts can hardly be changed "live" because there's no way for the gateway to tell routed hosts how it changed their packets; · any piece of information discarded on the remote hosts cannot be "restored" on the gateway (except by keeping the whole traffic...); · the gateway shouldn't "create" information. That is, if we consider a test to which the remote hosts would not reply, if the gateway was replying to it, then it would also reply for hosts that are down. 33.. IIPP PPeerrssoonnaalliittyy Considering the above restrictions, we have chosen a solution based on _n_e_t_f_i_l_t_e_r and _i_p_t_a_b_l_e_s: the latter provides the mangle table, designed to enable modifications on packets (contrary to the filter and nat tables, designed for filtering and address translation). Consequently, we created the new PERS (as IP Personality) target, which can rewrite packets in several ways. The rules let _i_p_t_a_b_l_e_s select IP packets according to their source and destination address and port, and the parameters passed to the PERS target alter its behaviour, which can be tuned by the administrator to define how a class of packets should be rewritten. 33..11.. FFeeaattuurreess Once installed and configured properly, IP Personality makes it possible to fool nmap into believing that the host runs an operating system freely specified by the administrator. Most test packets sent by nmap are abnormal, and the others are sent to closed ports, therefore they have no influence on the local TCP/IP stack: consequently we can divert them without worry, and send the replies that suit to our needs. The PERS configuration covers a large set of possible answers, thus we can send back to nmap packets which are specific to any kind of system described in nmap's OS fingerprint list. Some of the operations intended to deceive nmap (not all of them, though) can also be applied to packets that are routed by the host. Even though we lose the capacity to completeley lure nmap, our modifications are efficient enough to prevent it from detecting the OS running on its target. The operations we can apply to routed packets are TCP sequence numbers and options rewriting. By the way, some rewriting operations can improve robustness in some cases. In particular, the hosts with weak ISN generators are vulnerable to sequence number prediction attacks, and they can be protected by our target, which offers a truly random ISN. Furthermore, thanks to the versatility offered by the syntax of the configuration file, the possibilities for emulation are not limited to existing network fingerprinting tools: it becomes very easy to fool, or at least disturb any tool that relies on the same tricks as nmap, since we can control the elements that make a packet typical. In order to handle all the possible behaviors of an IP stack, the configuration is done through an external config file describing values for several parameters. This file is parsed and loaded in kernel space with an extension to _n_e_t_f_i_l_t_e_r's configuration tool, _i_p_t_a_b_l_e_s. In particular, for cases difficult to emulate, that configuration file contains two "code" sections that are interpreted in the kernel (as pseudo-code) in order to analyse packets more precisely with algorithms similar to those of the emulated operating systems. 33..22.. TThhee jjoouurrnneeyy ooff aa ppaacckkeett iinn PPEERRSS +----->---->---+----+--->---->-----+ | +---<----<---| VM |---<----<---+ | | | +----+ | | +--+-+--+ | | +->-| Decoy |->-+ | | | +-------+ | +-----+ +-----+ +-+-+-+ +-->--+->--->--->--->-+-| SEQ |->-| WIN |->-| OPT |-+ +-----------+ | TCP +-----+ +-----+ +-----+ | | IP Tables |->-+ |--+ +-----------+ | UDP +---------+ | | | +-->---->-----| Unreach |------>------>-------------+ | | +---------+ | +-------<---------<--------<---------<----------<----------<------+ <==================== IP Personality ====================> The PERS target can modifiy the packets it receives from the _n_e_t_f_i_l_t_e_r architecture. Therefore it is used in the mangle table, which is meant to enable packet modification. This table has access to two of _n_e_t_f_i_l_t_e_r's hooks, PRE_ROUTING and LOCAL_OUT. So as to be able to rewrite connections correctly, the PERS module needs to track each connection's packets in both directions (we will explain why later). To achieve this, we use a pair of rules configured identically, except that their source and destination criteria are symmetric. For routed packets, both rules must be on the PRE_ROUTING hook, since packets of both directions come from hosts other than the local machine. By contrast, even though the traffic sent to the local machine uses the PRE_ROUTING hook, responses are sent via LOCAL_OUT. In every rule used to rewrite a class of traffic, there is an option to tell the module whether it should protect the source or the destination of the packet, since some rewriting operations are done differently depending on the packet's direction. 33..22..11.. TTCCPP ppaacckkeettss If the packet is sent to the local machine (this is an option of our target), it is first sent to the decoy generation code: here the pseudo-code of the tcp_decoy section of the configuration file decides whether the packet can continue as is, or (if the packet has been found to be abnormal) whether we have to send back a decoy based on the packet. If the packet continues, it can be modified in different ways. In particular, the direction, which can be determined with information provided by _i_p_t_a_b_l_e's conntrack module and with parameters of the current rule, defines how the packet is rewritten. The possible modifications are: · sseeqquueennccee nnuummbbeerrss rreewwrriittiinngg:: we want to be able to simulate initial sequence number generators, and at the same time we want that steps following the establishment of a connection work properly. Therefore we need to rewrite the sequence and acknowledgement numbers in all packets of a connection for which the ISN has been modified. The first rewriting is done when the ISN is chosen by one of the generators in PERS (the configuration file defines a generator and its parameters); at this time, the difference between the original ISN and the one generated by PERS is saved. As this difference between the sequence numbers used by both sides remains constant, we can simply add it to the sequence numbers in one way and subtract it from acknowledgement numbers in the other way; · wwiinnddooww ssiizzee rreewwrriittiinngg:: since the initial window size is a characteristic element, we want to be able to control it. But as for sequence numbers, once we have set a limit, we must enforce it for all the packets of the connection; · ooppttiioonnss rreewwrriittiinngg:: when a connection is established, both IP stacks exchange useful information by the use of options: they are optionnal fields in the TCP header, that lie between the regular header and the payload of the packet. The supported options and the order in which they appear is a characteristic we can tweak: this is done by interpreting the pseudo-code from the tcp_options subsection of the configuration file. This code tests the type and values of options and the flags of the TCP header, and then builds a new option buffer to replace the original one. 33..22..22.. UUDDPP ppaacckkeettss UDP packets that are simply routed are ignored. However, packets for the local machine are analysed to check if their destination port is listening: if that's the case, they are left as is; otherwise, they are dropped and PERS is then responsible for emitting an ICMP Port Unreachable message packet because nmap will check it. This kind of message is made of an IP packet with an ICMP header, followed by the beginning of the original packet that generated the message. The configuration file allows one to control each part of the generated reply packet that nmap uses to identify the OS. 33..22..33.. CCoommmmoonn ppaarrtt ffoorr IIPP ppaacckkeettss After the potential changes in UDP/TCP packets, all IP packets can also be modified. Right now, only one change can be done : tweaking the IP ID number using a value generated with a defined method, just like for the TCP ISN. 44.. CCoonnffiigguurraattiioonn The configuration of the PERS target is done in userspace with the _i_p_t_a_b_l_e_s command and an associated dynamic library for specific parameters. This library adds new options for setting up the PERS target; one of the options allows the user to specify a configuration file containing all the parameters needed to emulate a particular operating system. Hence by using different configuration files for each different netfilter rule, one can easily choose to look like a particular OS for some sources or destination addresses, for a specific interface, and/or for other matching criterias. 44..11.. CCoommmmaanndd lliinnee ooppttiioonnss Command line options are passed to the target when adding a rule using it, for instance with a syntax like the following one: iptables -A -s -d -j PERS [Refer to _i_p_t_a_b_l_e_s documentation for more information on the global syntax] The following options are recognized by the library: · _-_t_w_e_a_k _{_s_r_c_|_d_s_t_} : This option sets the way packets should be rewritten for the corresponding rule. If its value is "src", then it means one wishes to protect the source of the matching packets (for instance, rewriting ISNs). If it is set do "dst", then the destination is protected (in that case acks would be rewritten). · _-_l_o_c_a_l : This option specifies that either the source or the destination (depending on the value of tweak) is local, and that the "decoy" and "udp" modules should be enabled for it (if available in the configuration used) in order to completely fool tools suchs as nmap. · _-_c_o_n_f _<_f_i_l_e_>: This option sets the configuration file to use for the emulated system in this rule (see below). 44..22.. CCoonnffiigguurraattiioonn ffiillee The parameters for emulation of a particular operating system are specified in the configuration file. This file has a syntax similar to named.conf, inspired from C. Options are grouped together in logical blocks (delimited with { and }), each block corresponding to a different kind of packet rewriting operation. Each option is composed of an identifier followed by one or more arguments, and ended by a ;. Options and blocks can be specified in any order. 44..22..11.. IIddeennttiiffiiccaattiioonn The first item of the configuration file is an identification for the system being described. It is a string at most 20 characters long. Syntax is as follows: id "FakeOS"; 44..22..22.. GGeenneerriicc TTCCPP ppaarraammeetteerrss These parameters are grouped together in a block named _t_c_p. Example: tcp { incoming yes; outgoing no; max-window 65536; } The _i_n_c_o_m_i_n_g parameter sets whether you wish to enable TCP connections modifications (ISN, window size, options) for incoming connections to the protected zone. It can either be set to _y_e_s or _n_o. The _o_u_t_g_o_i_n_g parameter has the same meaning but for outgoing connections The _m_a_x_-_w_i_n_d_o_w parameter controls window size rewriting for TCP packets from connections matching the previous options. If it is set to a non-null value, then for every new connection with a window size greater than the given value, an offset is computed and applied to every packet to set the window size below the specified value for the length of the connection. 44..22..33.. SSeeqquueennccee NNuummbbeerrss GGeenneerraattoorr PPaarraammeetteerrss These parameters are grouped together in a block named _t_c_p___i_s_n. Example: tcp_isn { type random-inc 10000; initial-value 2600; } The _t_y_p_e parameter sets the type of generator to emulate and possible options for it. The following types are supported: · _f_i_x_e_d_-_i_n_c _<_n_u_m_b_e_r_> : That's the simplest generator. The initial sequence number is simply increased of a fixed value (specified as argument) at each new connection. Using 0 as the increment value allows one to emulated systems using fixed initial sequence numbers. · _r_a_n_d_o_m_-_i_n_c _<_n_u_m_b_e_r_> : That's a pseudo-random generator. For each new connection, the initial sequence number is incremented by a random value chosen between 0 and the specified number. This is the kind of generator used on systems such as Linux, FreeBSD, ... The strength of such a generator is determined by its random range. · _t_r_u_e_-_r_a_n_d_o_m : This is a truly random generator. For each new connection, the initial sequence number is randomly chosen using the kernel's internal entropy based random generator. · _b_u_i_l_t_i_n : This is the host system builtin generator. Hence under linux, it is a random incremented generator. · _t_i_m_e_-_d_e_p _<_n_u_m_b_e_r_> : This is a time dependant generator. The passes number specifies the frequency of the generator (in Hz). For instance, using 25000 for the value allows one to implement the generator recommended in RFC 793: the ISN is then incremented by 1 every 4 micro-seconds. (however, the generator granularity depends on the host system ticks precision, 100 Hz by default on linux/x86) The _i_n_i_t_i_a_l_-_v_a_l_u_e sets the initial value to use for the emulated generator. A numeric value can be specified or _r_a_n_d_o_m which will pick this number randomly when loading the rule. This parameter is of little importance on strong generators. 44..22..44.. IIPP IIDD GGeenneerraattoorr PPaarraammeetteerrss These parameters are grouped together in a block named _i_p___i_d. Example: ip_id { type broken-inc 1; initial-value 2600; } The _t_y_p_e parameter sets the type of generator to emulate and possible options for it. The same types as for the ISN generator are available, with an additional one, _b_r_o_k_e_n_-_i_n_c _n_u_m_b_e_r: it is an incremented counter of the specified value, but the result is saved in the packet in little endian order instead of network order. 44..22..55.. OOppttiioonnss RReeoorrddeerriinngg PPaarraammeetteerrss These parameters are grouped together in a block named _t_c_p___o_p_t_i_o_n_s. Example: tcp_options { keep-unknown yes; keep-unused no; isolated-packets yes; timestamp-scale 100; code { } } This block defines how TCP options of a packet should be rewritten. The _c_o_d_e subsection contains a simple program written in a langage close to C (see below), which is compiled by the _l_i_b_i_p_t___P_E_R_S_._s_o module. This code is passed to the virtual machine that fills an option buffer (part of its state) as it runs it. When the execution is over, the new options buffer is used to replace the original options buffer of the packet. The _k_e_e_p_-_u_n_k_n_o_w_n parameter specifies if "unknown" options in the original packet (hence that can't be handled in the code) should be added at the end of the new options buffer so they are kept. It can be set to either _y_e_s or _n_o. The _k_e_e_p_-_u_n_u_s_e_d parameter specifies if options from the original packet that haven't been used (probed or copied) by the code should be added at the end of the new options buffer so they are kept. It can be set to either _y_e_s or _n_o. This allows one to use a very simple code to reorder a few options while keeping the other ones functionnal. The _i_s_o_l_a_t_e_d_-_p_a_c_k_e_t_s parameter specifies if options reordering should be performed for packets that do not belong to any known connection. It can be set to either _y_e_s or _n_o. (defaults to no). The _t_i_m_e_s_t_a_m_p_-_s_c_a_l_e parameter specifies if the timestamp options of TCP packets related to the local machine should be changed to a new frequency. Its argument is the new frequency to use. (if it is null or equal to the base frequency it is ignored). 44..22..66.. TTCCPP ddeeccooyyiinngg PPaarraammeetteerrss These parameters are grouped together in a block named _t_c_p___d_e_c_o_y. Example: tcp_decoy { code { } } This block only contains a _c_o_d_e subsection like the previous one, that defines tests to perform on packets in order to recognize pathological packets from analysis tools and decide the way to handle them. The language used is the same as before (see below). 44..22..77.. UUDDPP DDeeccooyyiinngg PPaarraammeetteerrss These parameters are grouped together in a block named _u_d_p___u_n_r_e_a_c_h. Example: udp_unreach { reply yes; df no; max-len 56; tos 0; mangle-original { ip-len 21; ip-id same; ip-csum zero; udp-len 308; udp-csum zero; udp-data same; } } The _r_e_p_l_y parameter sets if you want an ICMP "port unreachable" message to be sent when receiving an UDP datagram for a port not listening. It can be set to either _y_e_s or _n_o. The other parametres of this block only apply if this is enabled. The _d_f parameters specifies whether the "Don't Fragment" bit should be set on generated ICMP messages. The _m_a_x_-_l_e_n parameter specifies the maximum length of the generated ICMP messages. The _t_o_s parameters specifies the value for the "Type Of service" field of the IP header of the generated ICMP messages. When sending an ICMP "port unreachable" message, part of the original packet is sent back along. The _m_a_n_g_l_e_-_o_r_i_g_i_n_a_l subsection specify how this part should be handled and mangled. The following parameters are available: · _i_p_-_l_e_n _{_s_a_m_e_|_<_n_u_m_b_e_r_>_} : sets the changes to apply to the length field of the original packet IP header. It can be set to _s_a_m_e (in that case it is unchanged) or to any numeric value (in that case it is replaced). · _i_p_-_i_d _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : sets the changes to apply to the id field of the original packet IP header. It can be set to _s_a_m_e, _z_e_r_o (then it is set to zero), _m_a_n_g_l_e (it is changed for a different value). · _i_p_-_c_s_u_m _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : sets the changes to apply to the checksum of the original packet IP header. It can be set to _s_a_m_e, _z_e_r_o, _m_a_n_g_l_e. · _u_d_p_-_l_e_n _{_s_a_m_e_|_<_n_u_m_b_e_r_>_} : sets the changes to apply to the length field of the original packet UDP header. It can be set to _s_a_m_e or to any numeric value. · _u_d_p_-_c_s_u_m _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : sets the changes to apply to the checksum of the original packet UDP header. It can be set to _s_a_m_e, _z_e_r_o, _m_a_n_g_l_e. · _u_d_p_-_d_a_t_a _{_s_a_m_e_|_m_a_n_g_l_e_|_z_e_r_o_} : sets changes to apply to the first byte of the original UDP datagram payload. It can be set to _s_a_m_e, _z_e_r_o, _m_a_n_g_l_e. 44..33.. LLaanngguuaaggee The _t_c_p___o_p_t_i_o_n_s and _t_c_p___d_e_c_o_y blocks both have a _c_o_d_e subsection that can contain a program. As seen previously, this program is compiled by the dynamic library extending _i_p_t_a_b_l_e_s into pseudo-code that is interpreted in the kernel module by a simple virtual machine. It operates over a TCP packet and handles an internal state, composed of the following: · A TCP options buffer · Several "registers": _f_l_a_g_s, _m_s_s, _w_s_c_a_l_e, _w_i_n, _a_c_k and _d_f corresponding to the TCP header fields of the same name for a potential reply packet. Code from the _t_c_p___o_p_t_i_o_n_s subsection is applied to an incoming TCP packet, and after running the program the options buffer from the virtual machine state is used as the new options buffer for that packet. Code from the _t_c_p___d_e_c_o_y section is also applied to an incoming TCP packet, but the packet is not modifed. Depending on the result of running the program a new packet can be built from the state of the virtual machine and sent to the source of the original packet. The original packet can also be dropped, or passed as is to the next rules. These programs are written in a language close to C. Some conditionnal tests can be performed on the original packets in order to adjust behavior depending on its contents/status A test looks like: if (test) { } or if (test) { } else { } A test is composed of one or more conditions, separated by logical operators && and ||, and grouped together with parentheses where needed. The following conditions are available: · _o_p_t_i_o_n_(_o_p_t_) : True if option _o_p_t is found in the original packet. · _f_l_a_g_s_(_f_l_a_g_) : True if _f_l_a_g is enabled in the TCP header flags. · _f_l_a_g_s_(_f_l_a_g_1_&_f_l_a_g_2_&_._._._) : True if all the specified flags are enabled in the TCP header flags. · _f_l_a_g_s_(_f_l_a_g_1_|_f_l_a_g_2_|_._._._) : True if at least one of the specified flags is enabled in the TCP header flags. · _a_c_k_(_v_a_l_) : True if the TCP header ACK field has the value _v_a_l. · _l_i_s_t_e_n : True if the destination port is listening on the local host. The language has several instructions to handle the internal state of the virtual machine: · _c_o_p_y_(_o_p_t_) : This copies the _o_p_t option from the original packet to the options buffer of the state of the virtual machine, if such an option is found in the original packet. · _i_n_s_e_r_t_(_o_p_t_, _v_a_l_) This inserts the _o_p_t option in the options buffer, with the specified value. A numeric value can be passed, or an expression like _t_h_i_s _+ _<_n_u_m_b_e_r_> which will give the option its original value incremented of the specified value. Only the _m_s_s, _w_s_c_a_l_e and _t_i_m_e_s_t_a_m_p (in that case "this" means the current value usable as a local timestamp) options are supported by this instruction. · _i_n_s_e_r_t_(_o_p_t_) : same as _i_n_s_e_r_t_(_o_p_t_, _t_h_i_s_). · _s_e_t_(_a_r_g_, _v_a_l_) : This sets one of the internal registers of the virtual machine. The available registers are _f_l_a_g_s, _d_f, _w_i_n and _a_c_k. For _f_l_a_g_s, the argument must be a valid combination of TCP flags, like for the tests. The _d_f and _w_i_n registers can have their value defined relatively to the original packet value by using the _t_h_i_s _+ _<_n_u_m_b_e_r_> construct seen above. This is also available for the _a_c_k register but then the final value is relative to the original packet sequence number (and not to its _a_c_k value). · _d_r_o_p, _a_c_c_e_p_t, and _r_e_p_l_y: These instructions stop execution of the program by respectively dropping the packet, let it pass it to next rule, and build an answer from the virtual machine state and send it back. The default action is _a_c_c_e_p_t at the end of the code. Hence, such a language allows one to precisely define behavior to reorder options, and also to generate appropriate replies for pathological tests from network analysis tools. But we can notice the following points: · Since the code in the _t_c_p___o_p_t_i_o_n subsection only applies to options reordering, only the options buffer from the state of the virtual machine is used after running the program. Hence the _l_i_s_t_e_n and _a_c_k tests, and the _i_n_s_e_r_t, _s_e_t, _d_r_o_p, _r_e_p_l_y instructions have little interest in this case. · The options supported by the different tests and conditions were extracted from various RFCs specifiyng them; here are the names of the supported ones: _e_o_l, _n_o_p, _m_s_s, _w_s_c_a_l_e, _s_a_c_k_O_K, _s_a_c_k, _e_c_h_o, _e_c_h_o_r_e_p_l_y, _t_i_m_e_s_t_a_m_p, _p_o_c_O_K, _p_o_c_S_P, _C_C, _C_C_._N_E_W, _C_C_._E_C_H_O, _a_c_r_e_q, _a_c_d_a_t_a. · The TCP flags supported by the different tests and conditions span the whole 12 bits usable and are coded with the following names: (from the lowest bit to the highest one): _f_i_n, _s_y_n, _r_s_t, _p_u_s_h, _a_c_k, _u_r_g, _e_c_e, _c_w_r, _b_o_g_1, _b_o_g_2, _b_o_g_3, _b_o_g_4. 55.. EExxaammppllee 55..11.. CCoonnffiigguurraattiioonn ffiillee Suppose we want to create a configuration file to emulate an AmigaOS system. For this purpose we have the following nmap signature (refer to nmap documentation for more details): Fingerprint AmigaOS AmiTCP/IP 4.3 TSeq(Class=64K) T1(DF=N%W=1F0E%ACK=S++%Flags=AS%Ops=M) T2(Resp=N) T3(Resp=Y%DF=N%W=1F0E%ACK=O%Flags=A%Ops=) T4(DF=N%W=2000%ACK=O%Flags=R%Ops=) T5(DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(DF=N%W=0%ACK=O%Flags=R%Ops=) T7(DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) First we have to start the configuration file with an identification as follows: id "AmigaOS"; For a start, we want to rewrite only incoming TCP connections and not changing TCP windows sizes (only fool nmap), hence we use a simple _t_c_p section. tcp { incoming yes; outgoing no; max-window 32768; } The _T_S_e_q line in nmap signature defines the ISN generator to use. The important parameter is the _c_l_a_s_s one. The various possible classes are: · _C_l_a_s_s_=_C : Constant generator, modeled by _f_i_x_e_d_-_i_n_c _0. · _C_l_a_s_s_=_T_D : Time dependant generator. It can be emulated with a fixed-inc generator, with a small increment in order to satisfy the _g_c_d and _s_i fields. There are no rules to do that precisely, so you have to try several values. · _C_l_a_s_s_=_R_I : Random Increments Generator. It can be emulated with the random- inc type. The interval of generation of ISN is determined by nmap's "difficulty" (_g_c_d and _S_I). Here too it is better to try several values. · _C_l_a_s_s_=_T_R : True Random Generator, can be emulated by _t_r_u_e_-_r_a_n_d_o_m · _C_l_a_s_s_=_i_8_0_0, _C_l_a_s_s_=_6_4_K : Fixed incrementation of multiples of 800 and 64000 respectively. Hence here we will use: tcp_isn { type fixed-inc 64000; initial-value random; } Then follow the various TCP tests results on _T_x lines. Their syntax is always the same and describes a possible answer received by nmap for that test. Tx(Resp=Y%DF=Y%W=XXXX%ACK=S++%Flags=AS%Ops=M) The meaning of the various fields are: · _R_e_s_p : _Y if an answer was received, _N otherwise. · _D_F : Specify whether the "Don't Fragment" bit is enabled in the anwser · _W : Specify the window size or sizes (separated by "|") expected in the answer. · _A_C_K : Specify the expected acquittement value for the answer. Can be set to a numeric value, or _S to mean the test initial sequence number, or _S_+_+ for the test initial sequence number plus one. · _F_l_a_g_s : Contains the TCP flags that must be enabled in the answer, coded as their initials. (_A for _A_c_k, _S for _S_y_n, ...). · _O_p_s : Contains the list of options following their order in the anwser, as their respective initials. (_M for _M_S_S, _N for _N_O_P, ...) except for _E which means the previous option has the same value as in the test packet. If we want to emulate the system accurately, we need to guess the options reordering scheme from the various tests results and their matching tests packets. Here, only one option is supported, so the corresponding section is quite simple: tcp_options { keep-unknown yes; keep-unused no; isolated-packets yes; code { copy(mss); } } Now the system starts looking like the emulated one. However our answers will not fool nmap for really precise tests. In order to completely fool it locally, we can extract appropriate answers to return in _d_e_c_o_y mode from its TCP test results. For that we can use a code "squeleton" that fits its tests and fill it to get the expected answers: tcp_decoy { code { if (option(mss)) { if (listen) { if (flags(syn&ece)) { /* nmap test 1 */ } if (flags(null)) { /* nmap test 2 */ } if (flags(syn&fin&urg&push)) { /* nmap test 3 */ } if (ack(0) && flags(ack) && !flags(syn|push|urg|rst)) { /* nmap test 4 * } } else { if (flags(syn) && !flags(ack)) { /* nmap test 5 */ } if (ack(0) && flags(ack) && !flags(syn|push|urg|rst)) { /* nmap test 6 * } if (flags(fin&push&urg)) { /* nmap test 7 */ } } } } } And then we only need to write the code for each test, for instance for the first one: set(df, 0); set(win, 0x1F0E); set(ack, this + 1); set(flags, ack|syn); insert(mss, this+1); reply; or for the second one (no answer): drop; Finaly, we can also locally react (in the _u_d_p___d_e_c_o_y section) to the last nmap test, an UDP port-unreach probe (_P_U), which has the following syntax: PU(DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) The meaning of the various fields are: · _R_e_s_p : Like previously, equivalent to the _d_f option. · _T_O_S : Type Of Service, equivalent to the _t_o_s option. · _I_P_L_E_N : length of the ICMP packet. Can be set by using the _m_a_x_-_l_e_n option. The generated ICMP unreach includes the beginning of the original packet (as recommended by the RFC). Nmap tries to see if it was changed, so the following fields apply to the _m_a_n_g_l_e_-_o_r_i_g_i_n_a_l subsection. · _R_I_D_, _R_I_P_C_K_, _U_C_K_, _D_A_T : These fields describe the change of respectively the original IP ID, the original IP checksum, the original UDP checksum, the original data block. They can have one of 3 values: 0 (zeroed), F (fucked), E (equal). Those fields are "mapped" to the following ippersonality parameters (same order) : _i_p_-_i_d, _i_p_-_c_s_u_m, _u_d_p_-_c_s_u_m, _u_d_p_-_d_a_t_a which can have one of the three following values (same order too): _z_e_r_o, _m_a_n_g_l_e, _s_a_m_e. · _R_I_P_L_E_N_, _U_L_E_N : Those fields describe the original IP and UDP length and are equivalent to the _i_p_-_l_e_n and _u_d_p_-_l_e_n options. They can be set to a numeric value or to _s_a_m_e if they are to be kept as is. So here, the following could be used: udp_unreach { reply yes; df no; max-len 56; tos 0; mangle-original { ip-len 348; ip-id same; ip-csum zero; udp-len 308; udp-csum zero; udp-data same; } } Time to test! Such a file can later on be improved and optimized in order to be more reliable (options reordering and the ISN generator are a little tricky to "guess") and faster (group several tests, etc.). 55..22.. TTeesstt nneettwwoorrkk In order to check the capabilities of the IP Personality module, let's take two different networks (with only one host in each), linked together by a router running IP Personality. This gives us something like: +---------+ +---------+ +---------+ | suskind |<--------->| dse2 |<--------->| dse1 | +---------+ +---------+ +---------+ The operating systems used on each host are: · suskind : FreeBSD-2.2.8-RELEASE. · dse1 : Linux 2.2.14. · dse2 : Linux 2.3.99pre6 (ippersonality). We can first check that each OS can be remotely detected by nmap from any host in our test-bed (details were kept so we could see how they changed after): If we run nmap against dse2 from suskind: TCP Sequence Prediction: Class=random positive increments Difficulty=2119945 (Good luck!) Sequence numbers: 59CEAA9C 5987D082 59CC67D4 59598903 5983CC3D 5971B98C Remote OS guesses: Linux 2.3.49 x86, Linux 2.3.99-pre2 x86 OS Fingerprint: TSeq(Class=RI%gcd=1%SI=205909) T1(Resp=Y%DF=Y%W=7C70%ACK=S++%Flags=AS%Ops=MNNTNW) T2(Resp=N) T3(Resp=Y%DF=Y%W=7C70%ACK=S++%Flags=AS%Ops=MNNTNW) T4(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) PU(Resp=Y%DF=Y%TOS=C0%IPLEN=164%RIPTL=148%RID=E%RIPCK=E%UCK=E%ULEN=134%DAT=E) We obtain the same result when nmapping dse2 from dse1. If we run nmap against suskind from dse1: TCP Sequence Prediction: Class=random positive increments Difficulty=9819 (Worthy challenge) Sequence numbers: 3B1E1359 3B1F0409 3B1F9BAB 3B201E56 3B20B8D2 3B217357 Remote operating system guess: FreeBSD 2.2.1 - 3.2 OS Fingerprint: TSeq(Class=RI%gcd=1%SI=265B) T1(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNWNNT) T2(Resp=N) T3(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNWNNT) T4(Resp=Y%DF=N%W=4000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=F%RIPCK=F%UCK=0%ULEN=134%DAT=E) Now let's take 3 configuration files for 3 different operating systems : _a_m_i_g_a_o_s_._c_o_n_f, _l_i_n_u_x_._c_o_n_f, and _w_i_n_9_x_._c_o_n_f. We want to have dse2 look like a windows box from suskind. To do that, we simply enter the two following lines (on dse2): iptables -t mangle -A PREROUTING -s suskind -d dse2 -j PERS --tweak dst \ --local --conf win9x.conf iptables -t mangle -A OUTPUT -s dse2 -d suskind -j PERS --tweak src \ --local --conf win9x.conf Then, we decide to have dse2 look like an amiga from dse1. The following two lines are used: iptables -t mangle -A PREROUTING -s dse1 -d dse2 -j PERS --tweak dst \ --local --conf amigaos.conf iptables -t mangle -A OUTPUT -s dse2 -d dse1 -j PERS --tweak src \ --local --conf amigaos.conf In order to try the routing capabilities as well, we want to have suskind look a linux box from dse1. iptables -t mangle -A PREROUTING -s suskind -d dse1 -j PERS --tweak src \ --conf linux.conf iptables -t mangle -A PREROUTING -s dse1 -d suskind -j PERS --tweak dst \ --conf linux.conf Let's redo the previous nmap tests: If we run nmap against dse2 from suskind: TCP Sequence Prediction: Class=trivial time dependency Difficulty=0 (Trivial joke) Sequence numbers: A97ECB1D A97ECB1F A97ECB21 A97ECB23 A97ECB25 A97ECB27 Remote operating system guess: Windows NT4 / Win95 / Win98 OS Fingerprint: TSeq(Class=TD%gcd=2%SI=0) T1(Resp=Y%DF=Y%W=2017%ACK=S++%Flags=AS%Ops=M) T2(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) T3(Resp=Y%DF=Y%W=2017%ACK=S++%Flags=AS%Ops=M) T4(Resp=Y%DF=N%W=0%ACK=S++%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=S++%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) PU(Resp=N) If we run nmap against dse2 from dse1: TCP Sequence Prediction: Class=64K rule Difficulty=1 (Trivial joke) Sequence numbers: D997B378 D998AD78 D999A778 D99AA178 D99B9B78 D99C9578 Remote operating system guess: AmigaOS AmiTCP/IP 4.3 OS Fingerprint: TSeq(Class=64K) T1(Resp=Y%DF=N%W=1F0E%ACK=S++%Flags=AS%Ops=M) T2(Resp=N) T3(Resp=Y%DF=N%W=1F0E%ACK=O%Flags=A%Ops=) T4(Resp=Y%DF=N%W=2000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) If we run nmap against suskind from dse1: TCP Sequence Prediction: Class=random positive increments Difficulty=188907 (Good luck!) Sequence numbers: 32BD32 393D33 3B87EE 3FE6A3 4AC5E7 4F9533 No OS matches for host (If you know what OS is running on it, see http://www.insecure.org/cgi-bin/nmap-submit.cgi). TCP/IP fingerprint: TSeq(Class=RI%gcd=1%SI=2EF4C) TSeq(Class=RI%gcd=1%SI=2EF18) TSeq(Class=RI%gcd=1%SI=2E1EB) T1(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNNTNW) T2(Resp=N) T3(Resp=Y%DF=Y%W=403D%ACK=S++%Flags=AS%Ops=MNNTNW) T4(Resp=Y%DF=N%W=4000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=F%RIPCK=F%UCK=0%ULEN=134%DAT=E) We can notice how dse2 completely fools nmap locally. However, when trying to hide routed hosts, the changed parameters make it unable to recognize the real operating system but are not sufficient to completely fool it. 66.. PPsseeuuddoo CCooddee 66..11.. OOvveerrvviieeww We implement a simple virtual machine in the kernel module. This machine runs over a TCP packet and handles an internal state, composed of: · An instruction pointer in the code. · A TCP Options buffer. · Several "registers": _f_l_a_g_s, _m_s_s, _w_s_c_a_l_e, _w_i_n, _a_c_k and _d_f corresponding to TCP header fields of the same name for a potential reply packet. The code understood by the virtual machine is made of intruction on 32 bits (in the machine's endian) composed of a mnemonic (8 bits), an option (4 bits) and an operand (20 bits), like below: 0 7 8 11 12 31 +---------------+--------+---------------------------------------+ | Mnemonic | Option | Operand | +---------------+--------+---------------------------------------+ 66..22.. IInnssttrruuccttiioonnss 66..22..11.. TTEESSTT CCooddee :: 01 Test the object defined by the option. If the test is true, the instruction pointer goes from instruction _i to instruction _i_+_2. If the test is false, the program continues at instruction _i_+_1. The test options available are the following: · _T_C_P _O_p_t_i_o_n (0) : True if the passed TCP option in the operand is present in the original packet. · _A_n_y _T_C_P _F_l_a_g_s (1) : True if one of the TCP flags passed in the operand are enabled in the original packet. · _A_l_l _T_C_P _F_l_a_g_s (2) : True if all the TCP flags passed in the operand are enabled in the original packet. · _A_c_k (3) : True if the original packet ACK value is equal to the operand. · _L_i_s_t_e_n (4) : True if the destination port of the original packet is listening locally. 66..22..22.. JJMMPP CCooddee :: 02 Program continues running at the instruction which address is the operand. 66..22..33.. PPUUTT CCooddee :: 03 Adds a TCP option in the TCP options buffer. The inserted TCP option is in the operand and its source is determined by the instruction option. The following options are available: · _C_o_p_y (0) : The inserted TCP option is copied from the original packet if found. · _I_n_s_e_r_t (1) : The inserted TCP option is copied from the internal registers. This is only possible for the _m_s_s, _w_s_c_a_l_e and _t_i_m_e_s_t_a_m_p options. 66..22..44.. SSEETT CCooddee :: 04 Sets the value of an internal register in the virtual machine. The register and the type of operation are determined by the option. The value is in the operand. The following options are available: · _f_l_a_g_s (0) : Sets the _f_l_a_g registers to the operand value. · _a_c_k (1) : Sets the _a_c_k registers to the operand value. · _d_f (2) : Sets the _d_f ("Don't Fragment") registers to the operand value. · _w_i_n (3) : Sets the _w_i_n registers to the operand value. · _m_s_s (4) : Sets the _m_s_s registers to the operand value. · _w_s_c_a_l_e (5) : Sets the _w_s_c_a_l_e registers to the operand value. · _t_i_m_e_s_t_a_m_p (6) : Sets the _t_i_m_e_s_t_a_m_p (local timestamp value) register to the operand value. · _r_e_l_a_t_i_v_e _a_c_k (9) : Sets the _a_c_k registers to the operand value added to the original packet value. · _r_e_l_a_t_i_v_e _d_f (10) : Sets the _d_f ("Don't Fragment") registers to the operand value added to the original packet value. · _r_e_l_a_t_i_v_e _w_i_n (11) : Sets the _w_i_n registers to the operand value added to the original packet value. · _r_e_l_a_t_i_v_e _m_s_s (12) : Sets the _m_s_s registers to the operand value added to the original packet value. · _r_e_l_a_t_i_v_e _w_s_c_a_l_e (13) : Sets the _w_s_c_a_l_e registers to the operand value added to the original packet value. · _r_e_l_a_t_i_v_e _t_i_m_e_s_t_a_m_p (14) : Sets the _t_i_m_e_s_t_a_m_p (local timestamp value) register to the operand value added with the current usable value for the local timestamp. 66..22..55.. RREETT CCooddee :: 05 Terminates the program execution and returns the operand. The available operands are: · _A_c_c_e_p_t (1) : Terminates execution and make the packet continue its path. · _D_r_o_p (2) : Terminates execution and drop packet. · _R_e_p_l_y (3) : Terminates execution, build a reply TCP packet from the virtual machine state and send it. 66..33.. TTCCPP OOppttiioonnss For the various instructions that accept TCP options, the following TCP options are available: · _e_o_l (0) · _n_o_p (1) · _m_s_s (2) · _w_s_c_a_l_e (3) · _s_a_c_k_O_K (4) · _s_a_c_k (5) · _e_c_h_o (6) · _e_c_h_o_r_e_p_l_y (7) · _t_i_m_e_s_t_a_m_p (8) · _p_o_c_O_K (9) · _p_o_c_S_P (10) · _C_C (11) · _C_C_._N_E_W (12) · _C_C_._E_C_H_O (13) · _a_c_r_e_q (14) · _a_c_d_a_t_a (15) 77.. DDeevveellooppmmeenntt TToooollss 77..11.. DDeebbuuggggiinngg In order to track the module execution, some information can be printed by the module in the kernel message buffer while processing packets. By default, debug is disabled but it can be enabled with a sysctl, through the _/_p_r_o_c_/_s_y_s_/_n_e_t_/_i_p_v_4_/_i_p___p_e_r_s_o_n_a_l_i_t_y___d_e_b_u_g file. The debug level is defined by the value of this parameter: individual bits are associated to submodules, allowing to select precisely debugging messages by combining wanted bits as follows: · _1 : Target core · _2 : ISN rewriting · _4 : Options reordering · _8 : Window scaling · _1_6 : Local TCP Decoy · _3_2 : Virutal Machine · _6_4 : Local UDP Decoy · _1_2_8 : IP ID rewriting Example: echo 35 > /proc/sys/net/ipv4/ip_personality_debug 77..22.. OOssddeett Osdet is a test tool trying to guess the OS of a remote host. It is based on nmap sources and uses the same tests, but it performs them sequentially while displaying replies it receives (with code from tcpdump). This allows one to see how the reply was potentially changed. Sample usage: dse1:~# osdet -h usage: osdet [-t n[-N],...] [-p port] [-P port] [-S ip] [-h] host -p port Sets openport (defaults to 23 (telnet)). -P port Sets closedport (defaults to a random high port). -S ip Sets source Ip for scans if multihomed. -t ... Selects a subset of tests to perform. dse1:~# osdet -p 23 -P 234 dse2 OSDET v0.3 [using nmap backend version 2.53] Trying to detect remote os of dse2 [172.20.30.2]. (assuming port 23 is open and port 234 is closed) Using pcap filter: (icmp and dst host 172.20.30.1) or (tcp and src host 172.20.30.2 and dst host 172.20.30.1) * Test 1 (TCP to open port, SYN and BOGUS) Sending packet... ok: 172.20.30.1.50925 > 172.20.30.2.23: S 26F7D60A:26F7D60A(0) win 3072 (ttl 54, id 36252) Waiting for answer... ok: 172.20.30.2.23 > 172.20.30.1.50925: S 6ECE0057:6ECE0057(0) ack 26F7D60B win 7950 (ttl 255, id 59900) * Test 2 (TCP to open port, NULL) Sending packet... ok: 172.20.30.1.50926 > 172.20.30.2.23: . win 3072 (ttl 54, id 27188) Waiting for answer... no reply. * Test 3 (TCP to open port, SYN, FIN, URG and PUSH) Sending packet... ok: 172.20.30.1.50927 > 172.20.30.2.23: SFP 26F7D60A:26F7D60A(0) win 3072 urg 0 (ttl 54, id 28956) Waiting for answer... ok: 172.20.30.2.23 > 172.20.30.1.50927: . ack 2 win 7950 (ttl 255, id 60156) * Test 4 (TCP to open port, ACK 0) Sending packet... ok: 172.20.30.1.50928 > 172.20.30.2.23: . ack 0 win 3072 (ttl 54, id 7360) Waiting for answer... ok: 172.20.30.2.23 > 172.20.30.1.50928: R 0:0(0) win 8192 (ttl 255, id 60412) * Test 5 (TCP to closed port, SYN) Sending packet... ok: 172.20.30.1.50929 > 172.20.30.2.234: S 26F7D60A:26F7D60A(0) win 3072 (ttl 54, id 49268) Waiting for answer... ok: 172.20.30.2.234 > 172.20.30.1.50929: R 0:0(0) ack 26F7D60B win 0 (ttl 255, id 60668) * Test 6 (TCP to closed port, ACK 0) Sending packet... ok: 172.20.30.1.50930 > 172.20.30.2.234: . ack 0 win 3072 (ttl 54, id 53356) Waiting for answer... ok: 172.20.30.2.234 > 172.20.30.1.50930: R 0:0(0) win 0 (ttl 255, id 60924) * Test 7 (TCP to closed port, FIN, PUSH and URG) Sending packet... ok: 172.20.30.1.50931 > 172.20.30.2.234: FP 26F7D60A:26F7D60A(0) win 3072 urg 0 (ttl 54, id 60119) Waiting for answer... ok: 172.20.30.2.234 > 172.20.30.1.50931: R 0:0(0) ack 26F7D60A win 0 (ttl 255, id 61180) * Test 8 (UDP to closed port, expecting ICMP unreach) Sending packet... ok: 172.20.30.1.50932 > 172.20.30.2.234: udp 300 (ttl 60, id 36334) Waiting for answer... ok: 172.20.30.2 > 172.20.30.1: icmp: 172.20.30.2 udp port 234 unreachable (ttl 255, id 61436) * Test 9 (Initial Sequence Number) Sending paquets... 26F7D60B 26F7D60C 26F7D60D 26F7D60E 26F7D60F 26F7D610; last is: 172.20.30.1.50939 > 172.20.30.2.23: S 26F7D610:26F7D610(0) win 3072 (ttl 54, id 777) Waiting for answers... 9D128940[1] 9D138340[2] 9D147D40[3] 9D157740[4] 9D167140[5] 9D176B40[6]; last is: 172.20.30.2.23 > 172.20.30.1.50939: S 9D176B40:9D176B40(0) ack 26F7D611 win 32120 (DF) (ttl 64, id 0) * Nmap OS Fingerprint: TSeq(Class=64K) T1(Resp=Y%DF=N%W=1F0E%ACK=S++%Flags=AS%Ops=M) T2(Resp=N) T3(Resp=Y%DF=N%W=1F0E%ACK=O%Flags=A%Ops=) T4(Resp=Y%DF=N%W=2000%ACK=O%Flags=R%Ops=) T5(Resp=Y%DF=N%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=0%IPLEN=38%RIPTL=15C%RID=E%RIPCK=0%UCK=0%ULEN=134%DAT=E) TCP Sequence Prediction: Class=64K rule Difficulty=1 (Trivial joke) * Remote OS Guess: AmigaOS AmiTCP/IP 4.3 77..33.. PPeerrsscccc The iptables modules associated with IP Personality has to parse the config file and compile the pseudo code. In order to test its parsing and generated code, we developed a standalone config file parser/compiler/desassembler. This tool can also be used to check a config file before using it. Sample usage: dse2:~# percc example.conf === config === id: Example isn initialized: yes, value=877764155 isn type: true-random rewrite way: ingoing outgoing keep unknown options: yes keep unused options: yes max window: 0 change options for isolated packets: yes udp-unreach: reply: yes df: yes max-len: 500 tos: 0 ip-len: 0 ip-id: same ip-csum: same udp-len: 0 udp-csum: same udp-data: mangle === interpreted code #0 === if (flags(syn)) { if (option(sackOK)) { copy(sackOK); } else { copy(nop); copy(nop); } copy(timestamp); copy(mss); } else { if (option(sack)) { copy(sack); } else { copy(nop); copy(nop); } copy(timestamp); } code: 15 instructions. === compiled code #0 === 0000: [01100002] TEST tcp_flags, syn 0001: [0200000B] JMP 000B 0002: [01000004] TEST tcp_option, sackOK 0003: [02000006] JMP 0006 0004: [03000004] PUT sackOK (copy) 0005: [02000008] JMP 0008 0006: [03000001] PUT nop (copy) 0007: [03000001] PUT nop (copy) 0008: [03000008] PUT timestamp (copy) 0009: [03000002] PUT mss (copy) 000A: [02000012] JMP 0012 000B: [01000005] TEST tcp_option, sack 000C: [0200000F] JMP 000F 000D: [03000005] PUT sack (copy) 000E: [02000011] JMP 0011 000F: [03000001] PUT nop (copy) 0010: [03000001] PUT nop (copy) 0011: [03000008] PUT timestamp (copy) asm: 18 instructions. === interpreted code #1 === if (option(mss)) { set(df, 0); if (listen) { if (flags(syn&ece)) { set(win, 7950); set(ack, this + 1); set(flags, syn|ack); insert(mss, this + 1); reply; } if (flags(null)) { drop; } if (flags(fin&syn&urg&push)) { set(win, 7950); set(ack, 2); set(flags, ack); reply; } if ((ack(0) && flags(ack)) && !flags(syn|urg|push|rst)) { set(win, 8192); set(ack, 2); set(flags, rst); reply; } } else { set(win, 0); if (flags(syn) && !flags(ack)) { set(ack, this + 1); set(flags, ack|rst); reply; } if ((ack(0) && flags(ack)) && !flags(syn|urg|push|rst)) { set(ack, 2); set(flags, rst); reply; } if (flags(fin&urg&push)) { set(ack, this + 0); set(flags, ack|rst); reply; } } } code: 53 instructions. === compiled code #1 === 0000: [01000002] TEST tcp_option, mss 0001: [0200003A] JMP 003A 0002: [04200000] SET df, 0 0003: [01400000] TEST listen 0004: [02000022] JMP 0022 0005: [01200042] TEST tcp_flags, syn&ece 0006: [0200000D] JMP 000D 0007: [04301F0E] SET win, 7950 0008: [04900001] SET ack, this + 1 0009: [04000012] SET flags, syn|ack 000A: [04C00001] SET mss, this + 1 000B: [03100002] PUT mss (insert) 000C: [05000003] RET reply 000D: [01100000] TEST tcp_flags, null 000E: [02000010] JMP 0010 000F: [05000002] RET drop 0010: [0120002B] TEST tcp_flags, fin&syn&urg&push 0011: [02000016] JMP 0016 0012: [04301F0E] SET win, 7950 0013: [04100002] SET ack, 2 0014: [04000010] SET flags, ack 0015: [05000003] RET reply 0016: [01300000] TEST ack, 0 0017: [0200003A] JMP 003A 0018: [01100010] TEST tcp_flags, ack 0019: [0200003A] JMP 003A 001A: [0110002E] TEST tcp_flags, syn|urg|push|rst 001B: [0200001D] JMP 001D 001C: [0200003A] JMP 003A 001D: [04302000] SET win, 8192 001E: [04100002] SET ack, 2 001F: [04000004] SET flags, rst 0020: [05000003] RET reply 0021: [0200003A] JMP 003A 0022: [04300000] SET win, 0 0023: [01100002] TEST tcp_flags, syn 0024: [0200002B] JMP 002B 0025: [01100010] TEST tcp_flags, ack 0026: [02000028] JMP 0028 0027: [0200002B] JMP 002B 0028: [04900001] SET ack, this + 1 0029: [04000014] SET flags, ack|rst 002A: [05000003] RET reply 002B: [01300000] TEST ack, 0 002C: [02000035] JMP 0035 002D: [01100010] TEST tcp_flags, ack 002E: [02000035] JMP 0035 002F: [0110002E] TEST tcp_flags, syn|urg|push|rst 0030: [02000032] JMP 0032 0031: [02000035] JMP 0035 0032: [04100002] SET ack, 2 0033: [04000004] SET flags, rst 0034: [05000003] RET reply 0035: [01200029] TEST tcp_flags, fin&urg&push 0036: [0200003A] JMP 003A 0037: [04900000] SET ack, this + 0 0038: [04000014] SET flags, ack|rst 0039: [05000003] RET reply asm: 58 instructions. 88.. RReeffeerreenncceess · J. Postel. _I_n_t_e_r_n_e_t _P_r_o_t_o_c_o_l, Request for Comments 791. Network Working Group, 09/1981. · J. Postel. _I_n_t_e_r_n_e_t _C_o_n_t_r_o_l _M_e_s_s_a_g_e _P_r_o_t_o_c_o_l, Request for Comments 792. Network Working Group, 09/1981. · J. Postel. _T_r_a_n_s_m_i_s_s_i_o_n _C_o_n_t_r_o_l _P_r_o_t_o_c_o_l, Request for Comments 793. Network Working Group, 09/1981. · V. Jacobson, R. Braden. _T_C_P _E_x_t_e_n_s_i_o_n_s _f_o_r _L_o_n_g_-_D_e_l_a_y _P_a_t_h_s, Request for Comments 1072. Network Working Group, 10/1988. · J. Zweig, C. Partridge. _T_C_P _A_l_t_e_r_n_a_t_e _C_h_e_c_k_s_u_m _O_p_t_i_o_n_s, Request for Comments 1146. Network Working Group, 04/1990. · V. Jacobson, R. Braden, D. Borman. _T_C_P _E_x_t_e_n_s_i_o_n_s _f_o_r _H_i_g_h _P_e_r_f_o_r_m_a_n_c_e, Request for Comments 1323. Network Working Group, 05/1992. · T. Connolly, P. Amer, P. Conrad. _A_n _E_x_t_e_n_s_i_o_n _t_o _T_C_P _: _P_a_r_t_i_a_l _O_r_d_e_r _S_e_r_v_i_c_e, Request for Comments 1693. Network Working Group, 11/1994. · "Fyodor". _R_e_m_o_t_e _O_S _d_e_t_e_c_t_i_o_n _v_i_a _T_C_P_/_I_P _S_t_a_c_k _F_i_n_g_e_r_P_r_i_n_t_i_n_g, Phrack Magazine Volume 8, Issue 54, 12/1998. · R. Russel. _L_i_n_u_x _n_e_t_f_i_l_t_e_r _H_a_c_k_i_n_g _H_O_W_T_O, Linux Documentation Project, 05/2000.