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 tcp section.
tcp {
incoming yes;
outgoing no;
max-window 32768;
}
The TSeq line in nmap signature defines the ISN generator to use. The important parameter is the class one. The various possible classes are:
Hence here we will use:
tcp_isn {
type fixed-inc 64000;
initial-value random;
}
Then follow the various TCP tests results on Tx 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:
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 decoy 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 udp_decoy section) to the last nmap test, an UDP port-unreach probe (PU), 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:
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 mangle-original subsection.
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.).
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:
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 : amigaos.conf, linux.conf, and win9x.conf.
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.