Coverage for /root/GitHubProjects/impacket/impacket/examples/os_ident.py : 1%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This computer software is owned by Core SDI Inc. and is
4# protected by U.S. copyright laws and other laws and by international
5# treaties. This computer software is furnished by CORE SDI Inc.
6# pursuant to a written license agreement and may be used, copied,
7# transmitted, and stored only in accordance with the terms of such
8# license and with the inclusion of the above copyright notice. This
9# computer software or any other copies thereof may not be provided or
10# otherwise made available to any other person.
11#
12#`
13# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
14# WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI Inc. BE LIABLE
15# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
16# CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF
17# THIS SOFTWARE
18#
19#--
20import math
21import array
22from six.moves import xrange, reduce
24from pcapy import lookupdev, open_live
25from impacket.ImpactPacket import UDP, TCPOption, Data, TCP, IP, ICMP, Ethernet
26from impacket.ImpactDecoder import EthDecoder
27from impacket import LOG
29g_nmap1_signature_filename="nmap-os-fingerprints"
30g_nmap2_signature_filename="nmap-os-db"
33def my_gcd(a, b):
34 if a < b:
35 c = a
36 a = b
37 b = c
39 while 0 != b:
40 c = a & b
41 a = b
42 b = c
43 return a
45class os_id_exception:
46 def __init__(self, value):
47 self.value = value
48 def __str__(self):
49 return repr(self.value)
51class os_id_test:
53 def __init__(self, id):
54 self.__id = id
55 self.__my_packet = None
56 self.__result_dict = {}
58 def test_id(self):
59 return self.__class__.__name__
61 def get_test_packet(self):
62 return self.__my_packet.get_packet()
64 def set_packet(self, packet):
65 self.__my_packet = packet
67 def get_packet(self):
68 return self.__my_packet
70 def process(self, packet):
71 pass
73 def add_result(self, name, value):
74 self.__result_dict[name] = value
76 def get_id(self):
77 return self.__id
78 def is_mine(self, packet):
79 pass
81 def get_result_dict(self):
82 return self.__result_dict;
84 def get_final_result(self):
85 "Returns a string representation of the final result of this test or None if no response was received"
86 pass
89class icmp_request(os_id_test):
90 type_filter = { ICMP.ICMP_ECHO : ICMP.ICMP_ECHOREPLY,
91 ICMP.ICMP_IREQ : ICMP.ICMP_IREQREPLY,
92 ICMP.ICMP_MASKREQ : ICMP.ICMP_MASKREPLY,
93 ICMP.ICMP_TSTAMP : ICMP.ICMP_TSTAMPREPLY }
95 def __init__(self, id, addresses, type):
96 os_id_test.__init__(self, id)
97 self.e = Ethernet()
98 self.i = IP()
99 self.icmp = ICMP()
101 self.i.set_ip_src(addresses[0])
102 self.i.set_ip_dst(addresses[1])
104 self.__type = type
105 self.icmp.set_icmp_type(type)
107 self.e.contains(self.i)
108 self.i.contains(self.icmp)
109 self.set_packet(self.e)
111 def is_mine(self, packet):
113 if packet.get_ether_type() != IP.ethertype:
114 return 0
115 ip = packet.child()
116 if not ip or ip.get_ip_p() != ICMP.protocol:
117 return 0
118 icmp = ip.child()
120 # icmp_request.type_filter is a dictionary that maps request
121 # type codes to the reply codes
123 if not icmp or \
124 icmp.get_icmp_type() != icmp_request.type_filter[self.__type]:
125 return 0
126 if icmp.get_icmp_id() != self.get_id():
127 return 0
129 return 1
131 def process(self, packet):
132 pass
135class nmap2_icmp_echo_probe_1(icmp_request):
136 # The first one has the IP DF bit set, a type-of-service (TOS) byte
137 # value of zero, a code of nine (even though it should be zero),
138 # the sequence number 295, a random IP ID and ICMP request identifier,
139 # and a random character repeated 120 times for the data payload.
140 sequence_number = 295
141 id = 0x5678
143 def __init__(self, id, addresses):
144 icmp_request.__init__(self, id, addresses, ICMP.ICMP_ECHO)
145 self.i.set_ip_df(True)
146 self.i.set_ip_tos(0)
147 self.icmp.set_icmp_code(9)
148 self.icmp.set_icmp_seq(nmap2_icmp_echo_probe_1.sequence_number)
149 self.i.set_ip_id(nmap2_icmp_echo_probe_1.id)
150 self.icmp.set_icmp_id(nmap2_icmp_echo_probe_1.id)
151 self.icmp.contains(Data("I" * 120))
153 def process(self, packet):
154 pass
156class nmap2_icmp_echo_probe_2(icmp_request):
157 # The second ping query is similar, except a TOS of four
158 # (IP_TOS_RELIABILITY) is used, the code is zero, 150 bytes of data is
159 # sent, and the IP ID, request ID, and sequence numbers are incremented
160 # by one from the previous query values.
162 def __init__(self, id, addresses):
163 icmp_request.__init__(self, id, addresses, ICMP.ICMP_ECHO)
164 self.i.set_ip_df(False)
165 self.i.set_ip_tos(4)
166 self.icmp.set_icmp_code(0)
167 self.icmp.set_icmp_seq(nmap2_icmp_echo_probe_1.sequence_number + 1)
168 self.i.set_ip_id(nmap2_icmp_echo_probe_1.id + 1)
169 self.icmp.set_icmp_id(nmap2_icmp_echo_probe_1.id + 1)
170 self.icmp.contains(Data("I" * 150))
172 def process(self, packet):
173 pass
175class udp_closed_probe(os_id_test):
177 ip_id = 0x1234 # HARDCODED
179 def __init__(self, id, addresses, udp_closed ):
181 os_id_test.__init__(self, id )
182 self.e = Ethernet()
183 self.i = IP()
184 self.u = UDP()
186 self.i.set_ip_src(addresses[0])
187 self.i.set_ip_dst(addresses[1])
188 self.i.set_ip_id(udp_closed_probe.ip_id)
189 self.u.set_uh_sport(id)
191 self.u.set_uh_dport( udp_closed )
193 self.e.contains(self.i)
194 self.i.contains(self.u)
195 self.set_packet(self.e)
197 def is_mine(self, packet):
198 if packet.get_ether_type() != IP.ethertype:
199 return 0
200 ip = packet.child()
201 if not ip or ip.get_ip_p() != ICMP.protocol:
202 return 0
203 icmp = ip.child()
204 if not icmp or icmp.get_icmp_type() != ICMP.ICMP_UNREACH:
205 return 0
207 if icmp.get_icmp_code() != ICMP.ICMP_UNREACH_PORT:
208 return 0;
211 self.err_data = icmp.child()
212 if not self.err_data:
213 return 0
216 return 1
219class tcp_probe(os_id_test):
221 def __init__(self, id, addresses, tcp_ports, open_port ):
223 self.result_string = "[]"
224 os_id_test.__init__(self, id)
225 self.e = Ethernet()
226 self.i = IP()
227 self.t = TCP()
228 self.i.set_ip_src(addresses[0])
229 self.i.set_ip_dst(addresses[1])
230 self.i.set_ip_id(0x2323) # HARDCODED
231 self.t.set_th_sport(id)
233 if open_port:
234 self.target_port = tcp_ports[0]
235 else:
236 self.target_port = tcp_ports[1]
238 self.t.set_th_dport(self.target_port)
240 self.e.contains(self.i)
241 self.i.contains(self.t)
242 self.set_packet(self.e)
244 self.source_ip = addresses[0]
245 self.target_ip = addresses[1]
247 def socket_match(self, ip, tcp):
248 # scr ip and port
249 if (ip.get_ip_src() != self.target_ip) or (tcp.get_th_sport() != self.target_port):
250 return 0
251 # dst ip and port
252 if(ip.get_ip_dst() != self.source_ip) or (tcp.get_th_dport() != self.get_id()):
253 return 0
254 return 1
256 def is_mine(self, packet):
257 if packet.get_ether_type() != IP.ethertype:
258 return 0
259 ip = packet.child()
260 if not ip or ip.get_ip_p() != TCP.protocol:
261 return 0
262 tcp = ip.child()
263 if self.socket_match(ip, tcp):
264 return 1
266 return 0
269class nmap_tcp_probe(tcp_probe):
271 def __init__(self, id, addresses, tcp_ports, open_port, sequence, options):
272 tcp_probe.__init__(self, id, addresses, tcp_ports, open_port)
273 self.t.set_th_seq(sequence)
274 self.set_resp(False)
275 for op in options:
276 self.t.add_option(op)
278 def set_resp(self,resp):
279 pass
281class nmap1_tcp_probe(nmap_tcp_probe):
282 sequence = 0x8453 # 0xBASE, obviously
283 mss = 265
285 # From: https://nmap.org/nmap-fingerprinting-old.html
286 # [...]
287 # Nmap sends these options along with almost every probe packet:
288 # Window Scale=10; NOP; Max Segment Size = 265; Timestamp; End of Ops;
289 # [...]
290 # From nmap-4.22SOC8/osscan.cc:get_fingerprint(...)
291 # [...]
292 # "\003\003\012\001\002\004\001\011\010\012\077\077\077\077\000\000\000\000\000\000"
293 # [...]
294 tcp_options = [
295 TCPOption(TCPOption.TCPOPT_WINDOW, 0o12), #\003\003\012
296 TCPOption(TCPOption.TCPOPT_NOP), #\001
297 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011
298 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0x3F3F3F3F), #\010\012\077\077\077\077\000\000\000\000
299 TCPOption(TCPOption.TCPOPT_EOL), #\000
300 TCPOption(TCPOption.TCPOPT_EOL) #\000
301 ]
303 def __init__(self, id, addresses, tcp_ports, open_port):
304 nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
305 self.sequence, self.tcp_options)
307 def set_resp(self,resp):
308 if resp:
309 self.add_result("Resp", "Y")
310 else:
311 self.add_result("Resp", "N")
313 def process(self, packet):
314 ip = packet.child()
315 tcp = ip.child()
317 self.set_resp(True)
319 if ip.get_ip_df():
320 self.add_result("DF", "Y")
321 else:
322 self.add_result("DF", "N")
324 self.add_result("W", tcp.get_th_win())
326 if tcp.get_th_ack() == self.sequence + 1:
327 self.add_result("ACK", "S++")
328 elif tcp.get_th_ack() == self.sequence:
329 self.add_result("ACK", "S")
330 else:
331 self.add_result("ACK", "O")
333 flags = []
335 # TCP flags
336 if tcp.get_ECE():
337 flags.append("B")
338 if tcp.get_URG():
339 flags.append("U")
340 if tcp.get_ACK():
341 flags.append("A")
342 if tcp.get_PSH():
343 flags.append("P")
344 if tcp.get_RST():
345 flags.append("R")
346 if tcp.get_SYN():
347 flags.append("S")
348 if tcp.get_FIN():
349 flags.append("F")
351 self.add_result("FLAGS", flags)
353 options = []
355 for op in tcp.get_options():
356 if op.get_kind() == TCPOption.TCPOPT_EOL:
357 options.append("L")
358 elif op.get_kind() == TCPOption.TCPOPT_MAXSEG:
359 options.append("M")
360 if op.get_mss() == self.mss:
361 options.append("E") # Echoed
362 elif op.get_kind() == TCPOption.TCPOPT_NOP:
363 options.append("N")
364 elif op.get_kind() == TCPOption.TCPOPT_TIMESTAMP:
365 options.append("T")
366 elif op.get_kind() == TCPOption.TCPOPT_WINDOW:
367 options.append("W")
369 self.add_result("OPTIONS", options)
371 def get_final_result(self):
372 return {self.test_id(): self.get_result_dict()}
375class nmap2_tcp_probe(nmap_tcp_probe):
376 acknowledgment = 0x181d4f7b
378 def __init__(self, id, addresses, tcp_ports, open_port, sequence, options):
379 nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
380 sequence, options)
381 self.t.set_th_ack(self.acknowledgment)
383 def set_resp(self,resp):
384 # Responsiveness (R)
385 # This test simply records whether the target responded to a given probe.
386 # Possible values are Y and N. If there is no reply, remaining fields
387 # for the test are omitted.
388 if resp:
389 self.add_result("R", "Y")
390 else:
391 self.add_result("R", "N")
393 def process(self, packet):
394 ip = packet.child()
395 tcp = ip.child()
397 # R, DF, T*, TG*, W, S, A, F, O, RD*, Q
398 self.set_resp(True)
400 tests = nmap2_tcp_tests(ip, tcp, self.sequence, self.acknowledgment)
402 self.add_result("DF", tests.get_df())
403 self.add_result("W", tests.get_win())
404 self.add_result("S", tests.get_seq())
405 self.add_result("A", tests.get_ack())
406 self.add_result("F", tests.get_flags())
407 self.add_result("O", tests.get_options())
408 self.add_result("Q", tests.get_quirks())
410 def get_final_result(self):
411 return {self.test_id() : self.get_result_dict()}
414class nmap2_ecn_probe(nmap_tcp_probe):
415 # From nmap-4.22SOC8/osscan2.cc:
416 # [...]
417 # "\003\003\012\001\002\004\005\264\004\002\001\001"
418 # [...]
420 # From: https://nmap.org/book/osdetect-methods.html
421 # [...]
422 # This probe tests for explicit congestion notification (ECN) support
423 # in the target TCP stack. ECN is a method for improving Internet
424 # performance by allowing routers to signal congestion problems before
425 # they start having to drop packets. It is documented in RFC 3168.
426 # Nmap tests this by sending a SYN packet which also has the ECN CWR
427 # and ECE congestion control flags set. For an unrelated (to ECN) test,
428 # the urgent field value of 0xF7F5 is used even though the urgent flag
429 # is not set. The acknowledgment number is zero, sequence number is
430 # random, window size field is three, and the reserved bit which
431 # immediately precedes the CWR bit is set. TCP options are WScale (10),
432 # NOP, MSS (1460), SACK permitted, NOP, NOP. The probe is sent to an
433 # open port.
434 # [...]
435 tcp_options = [
436 TCPOption(TCPOption.TCPOPT_WINDOW, 0o12), #\003\003\012
437 TCPOption(TCPOption.TCPOPT_NOP), #\001
438 TCPOption(TCPOption.TCPOPT_MAXSEG, 1460), #\002\004\005\0264
439 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), #\004\002
440 TCPOption(TCPOption.TCPOPT_NOP), #\001
441 TCPOption(TCPOption.TCPOPT_NOP) #\001
442 ]
445 def __init__(self, id, addresses, tcp_ports):
446 nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, 1,
447 0x8b6a, self.tcp_options)
448 self.t.set_SYN()
449 self.t.set_CWR()
450 self.t.set_ECE()
451 self.t.set_flags(0x800)
452 self.t.set_th_urp(0xF7F5)
453 self.t.set_th_ack(0)
454 self.t.set_th_win(3)
455 #self.t.set_th_flags(self.t.get_th_flags() | 0x0100) # 0000 0001 00000000
457 def test_id(self):
458 return "ECN"
460 def set_resp(self,resp):
461 if resp:
462 self.add_result("R", "Y")
463 else:
464 self.add_result("R", "N")
466 def process(self, packet):
467 ip = packet.child()
468 tcp = ip.child()
470 # R, DF, T*, TG*, W, O, CC, Q
471 self.set_resp(True)
473 tests = nmap2_tcp_tests(ip, tcp, 0, 0)
475 self.add_result("DF", tests.get_df())
476 self.add_result("W", tests.get_win())
477 self.add_result("O", tests.get_options())
478 self.add_result("CC", tests.get_cc())
479 self.add_result("Q", tests.get_quirks())
481 def get_final_result(self):
482 return {self.test_id() : self.get_result_dict()}
484class nmap2_tcp_tests:
485 def __init__(self, ip, tcp, sequence, acknowledgment):
486 self.__ip = ip
487 self.__tcp = tcp
488 self.__sequence = sequence
489 self.__acknowledgment = acknowledgment
491 def get_df(self):
492 # IP don't fragment bit (DF)
493 # The IP header contains a single bit which forbids routers from fragmenting
494 # a packet. If the packet is too large for routers to handle, they will just
495 # have to drop it (and ideally return a "destination unreachable,
496 # fragmentation needed" response). This test records Y if the bit is set,
497 # and N if it isn't.
498 if self.__ip.get_ip_df():
499 return "Y"
500 else:
501 return "N"
503 def get_win(self):
504 # TCP initial window size (W, W1-W6)
505 # This test simply records the 16-bit TCP window size of the received packet.
506 return "%X" % self.__tcp.get_th_win()
508 def get_ack(self):
509 # TCP acknowledgment number (A)
510 # This test is the same as S except that it tests how the acknowledgment
511 # number in the response compares to the sequence number in the
512 # respective probe.
513 # Value Description
514 # Z Acknowledgment number is zero.
515 # S Acknowledgment number is the same as the sequence number in the probe.
516 # S+ Acknowledgment number is the same as the sequence number in the probe plus one.
517 # O Acknowledgment number is something else (other).
518 if self.__tcp.get_th_ack() == self.__sequence + 1:
519 return "S+"
520 elif self.__tcp.get_th_ack() == self.__sequence:
521 return "S"
522 elif self.__tcp.get_th_ack() == 0:
523 return "Z"
524 else:
525 return "O"
527 def get_seq(self):
528 # TCP sequence number (S)
529 # This test examines the 32-bit sequence number field in the TCP
530 # header. Rather than record the field value as some other tests
531 # do, this one examines how it compares to the TCP acknowledgment
532 # number from the probe that elicited the response.
533 # Value Description
534 # Z Sequence number is zero.
535 # A Sequence number is the same as the acknowledgment number in the probe.
536 # A+ Sequence number is the same as the acknowledgment number in the probe plus one.
537 # O Sequence number is something else (other).
538 if self.__tcp.get_th_seq() == self.__acknowledgment + 1:
539 return "A+"
540 elif self.__tcp.get_th_seq() == self.__acknowledgment:
541 return "A"
542 elif self.__tcp.get_th_seq() == 0:
543 return "Z"
544 else:
545 return "O"
547 def get_flags(self):
548 # TCP flags (F)
549 # This field records the TCP flags in the response. Each letter represents
550 # one flag, and they occur in the same order as in a TCP packet (from
551 # high-bit on the left, to the low ones). So the value SA represents the
552 # SYN and ACK bits set, while the value AS is illegal (wrong order).
553 # The possible flags are shown in Table 8.7.
554 # Character Flag name Flag byte value
555 # E ECN Echo (ECE) 64
556 # U Urgent Data (URG) 32
557 # A Acknowledgment (ACK) 16
558 # P Push (PSH) 8
559 # R Reset (RST) 4
560 # S Synchronize (SYN) 2
561 # F Final (FIN) 1
563 flags = ""
565 if self.__tcp.get_ECE():
566 flags += "E"
567 if self.__tcp.get_URG():
568 flags += "U"
569 if self.__tcp.get_ACK():
570 flags += "A"
571 if self.__tcp.get_PSH():
572 flags += "P"
573 if self.__tcp.get_RST():
574 flags += "R"
575 if self.__tcp.get_SYN():
576 flags += "S"
577 if self.__tcp.get_FIN():
578 flags += "F"
580 return flags
582 def get_options(self):
583 # Option Name Character Argument (if any)
584 # End of Options List (EOL) L
585 # No operation (NOP) N
586 # Maximum Segment Size (MSS) M The value is appended. Many systems
587 # echo the value used in the corresponding probe.
588 # Window Scale (WS) W The actual value is appended.
589 # Timestamp (TS) T The T is followed by two binary characters
590 # representing the TSval and TSecr values respectively.
591 # The characters are 0 if the field is zero
592 # and 1 otherwise.
593 # Selective ACK permitted (SACK) S
595 options = ""
597 for op in self.__tcp.get_options():
598 if op.get_kind() == TCPOption.TCPOPT_EOL:
599 options += "L"
600 elif op.get_kind() == TCPOption.TCPOPT_MAXSEG:
601 options += "M%X" % (op.get_mss())
602 elif op.get_kind() == TCPOption.TCPOPT_NOP:
603 options += "N"
604 elif op.get_kind() == TCPOption.TCPOPT_TIMESTAMP:
605 options += "T%i%i" % (int(op.get_ts()!=0),
606 int(op.get_ts_echo()!=0))
607 elif op.get_kind() == TCPOption.TCPOPT_WINDOW:
608 options += "W%X" % (op.get_shift_cnt())
609 elif op.get_kind() == TCPOption.TCPOPT_SACK_PERMITTED:
610 options += "S"
612 return options
614 def get_cc(self):
615 # Explicit congestion notification (CC)
616 # This test is only used for the ECN probe. That probe is a SYN packet
617 # which includes the CWR and ECE congestion control flags. When the
618 # response SYN/ACK is received, those flags are examined to set the
619 # CC (congestion control) test value as described in Table 8.3.
621 # Table 8.3. CC test values
622 # Value Description
623 # Y Only the ECE bit is set (not CWR). This host supports ECN.
624 # N Neither of these two bits is set. The target does not support
625 # ECN.
626 # S Both bits are set. The target does not support ECN, but it
627 # echoes back what it thinks is a reserved bit.
628 # O The one remaining combination of these two bits (other).
629 ece, cwr = self.__tcp.get_ECE(), self.__tcp.get_CWR()
630 if ece and not cwr:
631 return "Y"
632 elif not ece and not cwr:
633 return "N"
634 elif ece and cwr:
635 return "S"
636 else:
637 return "O"
639 def get_quirks(self):
640 # TCP miscellaneous quirks (Q)
641 # This tests for two quirks that a few implementations have in their
642 # TCP stack. The first is that the reserved field in the TCP header
643 # (right after the header length) is nonzero. This is particularly
644 # likely to happen in response to the ECN test as that one sets a
645 # reserved bit in the probe. If this is seen in a packet, an "R"
646 # is recorded in the Q string.
648 # The other quirk Nmap tests for is a nonzero urgent pointer field
649 # value when the URG flag is not set. This is also particularly
650 # likely to be seen in response to the ECN probe, which sets a
651 # non-zero urgent field. A "U" is appended to the Q string when
652 # this is seen.
654 # The Q string must always be generated in alphabetical order.
655 # If no quirks are present, the Q test is empty but still shown.
657 quirks = ""
659 if ((self.__tcp.get_th_flags() >> 8) & 0x0f) != 0:
660 quirks += "R"
661 if self.__tcp.get_URG() == 0 and self.__tcp.get_th_urp() != 0:
662 quirks += "U"
664 return quirks
666class nmap2_tcp_probe_2_6(nmap2_tcp_probe):
667 sequence = 0x8453 # 0xBASE, obviously
668 mss = 265
670 # From nmap-4.22SOC8/osscan2.cc:
671 # [...]
672 # "\003\003\012\001\002\004\001\011\010\012\377\377\377\377\000\000\000\000\004\002"
673 # [...]
675 # From: https://nmap.org/book/osdetect-methods.html
676 # [...]
677 # The six T2 through T7 tests each send one TCP probe packet.
678 # With one exception, the TCP options data in each case is (in hex)
679 # 03030A0102040109080AFFFFFFFF000000000402.
680 # Those 20 bytes correspond to window scale (10), NOP, MSS (265),
681 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0), then SACK permitted.
682 # (...
683 tcp_options = [
684 TCPOption(TCPOption.TCPOPT_WINDOW, 0o12), #\003\003\012
685 TCPOption(TCPOption.TCPOPT_NOP), #\001
686 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011
687 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), #\010\012\377\377\377\377\000\000\000\000
688 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) #\004\002
689 ]
691 def __init__(self, id, addresses, tcp_ports, open_port):
692 nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
693 self.sequence, self.tcp_options)
695class nmap2_tcp_probe_7(nmap2_tcp_probe):
696 sequence = 0x8453 # 0xBASE, obviously
697 mss = 265
699 # ...)
700 # The exception is that T7 uses a Window scale value of 15 rather than 10
701 # [...]
702 tcp_options = [
703 TCPOption(TCPOption.TCPOPT_WINDOW, 0o17), #\003\003\017
704 TCPOption(TCPOption.TCPOPT_NOP), #\001
705 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011
706 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), #\010\012\377\377\377\377\000\000\000\000
707 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) #\004\002
708 ]
710 def __init__(self, id, addresses, tcp_ports, open_port):
711 nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
712 self.sequence, self.tcp_options)
714class nmap_port_unreachable(udp_closed_probe):
716 def __init__(self, id, addresses, ports):
717 udp_closed_probe.__init__(self, id, addresses, ports[2])
718 self.set_resp(False)
720 def test_id(self):
721 pass
723 def set_resp(self, resp):
724 pass
726 def process(self, packet):
727 pass
729class nmap1_port_unreachable(nmap_port_unreachable):
731 def __init__(self, id, addresses, ports):
732 nmap_port_unreachable.__init__(self, id, addresses, ports)
733 self.u.contains(Data("A" * 300))
735 def test_id(self):
736 return "PU"
738 def set_resp(self,resp):
739 if resp:
740 self.add_result("Resp", "Y")
741 else:
742 self.add_result("Resp", "N")
744 def process(self, packet):
745 ip_orig = self.err_data
746 if ip_orig.get_ip_p() != UDP.protocol:
747 return
749 udp = ip_orig.child()
751 if not udp:
752 return
754 ip = packet.child()
756 self.set_resp(True)
758 if ip.get_ip_df():
759 self.add_result("DF", "Y")
760 else:
761 self.add_result("DF", "N")
763 self.add_result("TOS", ip.get_ip_tos())
765 self.add_result("IPLEN", ip.get_ip_len())
767 self.add_result("RIPTL", ip_orig.get_ip_len()) # Some systems return a different IPLEN
769 recv_ip_id = ip_orig.get_ip_id()
770 if 0 == recv_ip_id:
771 self.add_result("RID", "0")
772 elif udp_closed_probe.ip_id == recv_ip_id:
773 self.add_result("RID", "E")
774 else:
775 self.add_result("RID", "F")
777 ip_sum = ip_orig.get_ip_sum()
778 ip_orig.set_ip_sum(0)
779 checksum = ip_orig.compute_checksum(ip_orig.get_bytes())
781 if 0 == checksum:
782 self.add_result("RIPCK", "0")
783 elif checksum == ip_sum:
784 self.add_result("RIPCK", "E")
785 else:
786 self.add_result("RIPCK", "F")
788 udp_sum = udp.get_uh_sum()
789 udp.set_uh_sum(0)
790 udp.auto_checksum = 1
791 udp.calculate_checksum()
793 if 0 == udp_sum:
794 self.add_result("UCK", "0")
795 elif self.u.get_uh_sum() == udp_sum:
796 self.add_result("UCK", "E")
797 else:
798 self.add_result("UCK", "F")
800 self.add_result("ULEN", udp.get_uh_ulen())
802 if ip.child().child().child().child() == udp.child(): # Some systems meddle with the data
803 self.add_result("DAT", "E")
804 else:
805 self.add_result("DAT", "F")
807 def get_final_result(self):
808 return {self.test_id(): self.get_result_dict()}
810class nmap2_port_unreachable(nmap_port_unreachable):
811 # UDP (U1)
812 # This probe is a UDP packet sent to a closed port. The character 'C'
813 # (0x43) is repeated 300 times for the data field. The IP ID value is
814 # set to 0x1042 for operating systems which allow us to set this. If
815 # the port is truly closed and there is no firewall in place, Nmap
816 # expects to receive an ICMP port unreachable message in return.
817 # That response is then subjected to the R, DF, T, TG, TOS, IPL, UN,
818 # RIPL, RID, RIPCK, RUCK, RUL, and RUD tests.
819 def __init__(self, id, addresses, ports):
820 nmap_port_unreachable.__init__(self, id, addresses, ports)
821 self.u.contains(Data("C" * 300))
822 self.i.set_ip_id(0x1042)
824 def test_id(self):
825 return "U1"
827 def set_resp(self,resp):
828 if resp:
829 self.add_result("R", "Y")
830 else:
831 self.add_result("R", "N")
833 def process(self, packet):
834 ip_orig = self.err_data
835 if ip_orig.get_ip_p() != UDP.protocol:
836 return
838 udp = ip_orig.child()
840 if not udp:
841 return
843 ip = packet.child()
845 icmp = ip.child()
847 if ip.get_ip_df():
848 self.add_result("DF", "Y")
849 else:
850 self.add_result("DF", "N")
852 # XXX T
853 # IP initial time-to-live (T)
854 # IP packets contain a field named time-to-live (TTL) which is
855 # decremented every time they traverse a router. If the field
856 # reaches zero, the packet must be discarded. This prevents
857 # packets from looping endlessly. Because operating systems differ
858 # on which TTL they start with, it can be used for OS detection.
859 # Nmap determines how many hops away it is from the target by
860 # examining the ICMP port unreachable response to the U1 probe.
861 # That response includes the original IP packet, including the
862 # already-decremented TTL field, received by the target. By
863 # subtracting that value from our as-sent TTL, we learn how many
864 # hops away the machine is. Nmap then adds that hop distance to
865 # the probe response TTL to determine what the initial TTL was
866 # when that ICMP probe response packet was sent. That initial TTL
867 # value is stored in the fingerprint as the T result.
868 # Even though an eight-bit field like TTL can never hold values
869 # greater than 0xFF, this test occasionally results in values of
870 # 0x100 or higher. This occurs when a system (could be the source,
871 # a target, or a system in between) corrupts or otherwise fails to
872 # correctly decrement the TTL. It can also occur due to asymmetric
873 # routes.
875 # XXX TG
876 # IP initial time-to-live guess (TG)
877 # It is not uncommon for Nmap to receive no response to the U1 probe,
878 # which prevents Nmap from learning how many hops away a target is.
879 # Firewalls and NAT devices love to block unsolicited UDP packets.
880 # But since common TTL values are spread well apart and targets are
881 # rarely more than 20 hops away, Nmap can make a pretty good guess
882 # anyway. Most systems send packets with an initial TTL of 32, 60, 64,
883 # 128, or 255. So the TTL value received in the response is rounded
884 # up to the next value out of 32, 64, 128, or 255. 60 is not in that
885 # list because it cannot be reliably distinguished from 64. It is
886 # rarely seen anyway.
887 # The resulting guess is stored in the TG field. This TTL guess field
888 # is not printed in a subject fingerprint if the actual TTL (T) value
889 # was discovered.
891 # IP type of service (TOS)
892 # This test simply records the type of service byte from the
893 # IP header of ICMP port unreachable packets.
894 # This byte is described in RFC 791
895 self.add_result("TOS", "%X" % ip.get_ip_tos())
897 # IP total length (IPL)
898 # This test records the total length (in octets) of an IP packet.
899 # It is only used for the port unreachable response elicited by the
900 # U1 test.
901 self.add_result("IPL", "%X" % ip.get_ip_len())
903 # Unused port unreachable field nonzero (UN)
904 # An ICMP port unreachable message header is eight bytes long, but
905 # only the first four are used. RFC 792 states that the last four
906 # bytes must be zero. A few implementations (mostly ethernet switches
907 # and some specialized embedded devices) set it anyway. The value of
908 # those last four bytes is recorded in this field.
909 self.add_result("UN", "%X" % icmp.get_icmp_void())
911 # Returned probe IP total length value (RIPL)
912 # ICMP port unreachable messages (as are sent in response to the U1
913 # probe) are required to include the IP header which generated them.
914 # This header should be returned just as they received it, but some
915 # implementations send back a corrupted version due to changes they
916 # made during IP processing. This test simply records the returned
917 # IP total length value. If the correct value of 0x148 (328) is
918 # returned, the value G (for good) is stored instead of the actual value.
919 if ip_orig.get_ip_len() == 0x148:
920 self.add_result("RIPL","G")
921 else:
922 self.add_result("RIPL", "%X" % ip_orig.get_ip_len())
924 # Returned probe IP ID value (RID)
925 # The U1 probe has a static IP ID value of 0x1042. If that value is
926 # returned in the port unreachable message, the value G is stored for
927 # this test. Otherwise the exact value returned is stored. Some systems,
928 # such as Solaris, manipulate IP ID values for raw IP packets that
929 # Nmap sends. In such cases, this test is skipped. We have found
930 # that some systems, particularly HP and Xerox printers, flip the bytes
931 # and return 0x4210 instead.
932 if 0x1042 == ip_orig.get_ip_id():
933 self.add_result("RID", "G")
934 else:
935 self.add_result("RID", "%X" % ip_orig.get_ip_id())
937 # Integrity of returned probe IP checksum value (RIPCK)
938 # The IP checksum is one value that we don't expect to remain the same
939 # when returned in a port unreachable message. After all, each network
940 # hop during transit changes the checksum as the TTL is decremented.
941 # However, the checksum we receive should match the enclosing IP packet.
942 # If it does, the value G (good) is stored for this test. If the returned
943 # value is zero, then Z is stored. Otherwise the result is I (invalid).
944 ip_sum = ip_orig.get_ip_sum()
945 ip_orig.set_ip_sum(0)
946 checksum = ip_orig.compute_checksum(ip_orig.get_bytes())
948 if 0 == checksum:
949 self.add_result("RIPCK", "Z")
950 elif checksum == ip_sum:
951 self.add_result("RIPCK", "G")
952 else:
953 self.add_result("RIPCK", "I")
955 # Integrity of returned probe UDP length and checksum (RUL and RUCK)
956 # The UDP header length and checksum values should be returned exactly
957 # as they were sent. If so, G is recorded for these tests. Otherwise
958 # the value actually returned is recorded. The proper length is 0x134 (308).
959 udp_sum = udp.get_uh_sum()
960 udp.set_uh_sum(0)
961 udp.auto_checksum = 1
962 udp.calculate_checksum()
964 if self.u.get_uh_sum() == udp_sum:
965 self.add_result("RUCK", "G")
966 else:
967 self.add_result("RUCK", "%X" % udp_sum)
969 if udp.get_uh_ulen() == 0x134:
970 self.add_result("RUL","G")
971 else:
972 self.add_result("RUL", "%X" % udp.get_uh_ulen())
974 # Integrity of returned UDP data (RUD)
975 # If the UDP payload returned consists of 300 'C' (0x43)
976 # characters as expected, a G is recorded for this test.
977 # Otherwise I (invalid) is recorded.
978 if ip.child().child().child().child() == udp.child():
979 self.add_result("RUD", "G")
980 else:
981 self.add_result("RUD", "I")
983 def get_final_result(self):
984 return {self.test_id(): self.get_result_dict()}
986class OS_ID:
988 def __init__(self, target, ports):
989 pcap_dev = lookupdev()
990 self.p = open_live(pcap_dev, 600, 0, 3000)
992 self.__source = self.p.getlocalip()
993 self.__target = target
995 self.p.setfilter("src host %s and dst host %s" % (target, self.__source), 1, 0xFFFFFF00)
996 self.p.setmintocopy(10)
997 self.decoder = EthDecoder()
999 self.tests_sent = []
1000 self.outstanding_count = 0
1001 self.results = {}
1002 self.current_id = 12345
1004 self.__ports = ports
1006 def releasePcap(self):
1007 if not (self.p is None):
1008 self.p.close()
1010 def get_new_id(self):
1011 id = self.current_id
1012 self.current_id += 1
1013 self.current_id &= 0xFFFF
1014 return id
1016 def send_tests(self, tests):
1017 self.outstanding_count = 0
1019 for t_class in tests:
1021 # Ok, I need to know if the constructor accepts the parameter port
1022 # We could ask also by co_varnames, but the port parameters is not a standarized... asking by args count :(
1023 if t_class.__init__.im_func.func_code.co_argcount == 4:
1024 test = t_class(self.get_new_id(), [self.__source, self.__target], self.__ports )
1025 else:
1026 test = t_class(self.get_new_id(), [self.__source, self.__target] )
1028 self.p.sendpacket(test.get_test_packet())
1029 self.outstanding_count += 1
1030 self.tests_sent.append(test)
1031 while self.p.readready():
1032 self.p.dispatch(1, self.packet_handler)
1034 while self.outstanding_count > 0:
1035 data = self.p.next()[0]
1036 if data:
1037 self.packet_handler(0, data)
1038 else:
1039 break
1041 def run(self):
1042 pass
1044 def get_source(self):
1045 return self.__source
1047 def get_target(self):
1048 return self.__target
1050 def get_ports(self):
1051 return self.__ports
1053 def packet_handler(self, len, data):
1054 packet = self.decoder.decode(data)
1056 for t in self.tests_sent:
1057 if t.is_mine(packet):
1058 t.process(packet)
1059 self.outstanding_count -= 1
1062class nmap1_tcp_open_1(nmap1_tcp_probe):
1063 def __init__(self, id, addresses, tcp_ports):
1064 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1065 self.t.set_ECE()
1066 self.t.set_SYN()
1068 def test_id(self):
1069 return "T1"
1071 def is_mine(self, packet):
1072 if tcp_probe.is_mine(self, packet):
1073 ip = packet.child()
1074 if not ip:
1075 return 0
1076 tcp = ip.child()
1077 if not tcp:
1078 return 0
1079 if tcp.get_SYN() and tcp.get_ACK():
1080 return 1
1081 else:
1082 return 0
1083 else:
1084 return 0
1087class nmap1_tcp_open_2(nmap1_tcp_probe):
1088 def __init__(self, id, addresses, tcp_ports):
1089 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1091 def test_id(self):
1092 return "T2"
1094class nmap2_tcp_open_2(nmap2_tcp_probe_2_6):
1095 # From: https://nmap.org/book/osdetect-methods.html
1096 # [...]
1097 # T2 sends a TCP null (no flags set) packet with the IP DF bit set and a
1098 # window field of 128 to an open port.
1099 # ...
1100 def __init__(self, id, addresses, tcp_ports):
1101 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1)
1102 self.i.set_ip_df(1)
1103 self.t.set_th_win(128)
1105 def test_id(self):
1106 return "T2"
1108class nmap1_tcp_open_3(nmap1_tcp_probe):
1109 def __init__(self, id, addresses, tcp_ports ):
1110 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1111 self.t.set_SYN()
1112 self.t.set_FIN()
1113 self.t.set_URG()
1114 self.t.set_PSH()
1116 def test_id(self):
1117 return "T3"
1119class nmap2_tcp_open_3(nmap2_tcp_probe_2_6):
1120 # ...
1121 # T3 sends a TCP packet with the SYN, FIN, URG, and PSH flags set and a
1122 # window field of 256 to an open port. The IP DF bit is not set.
1123 # ...
1124 def __init__(self, id, addresses, tcp_ports ):
1125 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1)
1126 self.t.set_SYN()
1127 self.t.set_FIN()
1128 self.t.set_URG()
1129 self.t.set_PSH()
1130 self.t.set_th_win(256)
1131 self.i.set_ip_df(0)
1133 def test_id(self):
1134 return "T3"
1136class nmap1_tcp_open_4(nmap1_tcp_probe):
1137 def __init__(self, id, addresses, tcp_ports):
1138 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1139 self.t.set_ACK()
1141 def test_id(self):
1142 return "T4"
1144class nmap2_tcp_open_4(nmap2_tcp_probe_2_6):
1145 # ...
1146 # T4 sends a TCP ACK packet with IP DF and a window field of 1024 to
1147 # an open port.
1148 # ...
1149 def __init__(self, id, addresses, tcp_ports ):
1150 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1)
1151 self.t.set_ACK()
1152 self.i.set_ip_df(1)
1153 self.t.set_th_win(1024)
1155 def test_id(self):
1156 return "T4"
1159class nmap1_seq(nmap1_tcp_probe):
1160 SEQ_UNKNOWN = 0
1161 SEQ_64K = 1
1162 SEQ_TD = 2
1163 SEQ_RI = 4
1164 SEQ_TR = 8
1165 SEQ_i800 = 16
1166 SEQ_CONSTANT = 32
1168 TS_SEQ_UNKNOWN = 0
1169 TS_SEQ_ZERO = 1 # At least one of the timestamps we received back was 0
1170 TS_SEQ_2HZ = 2
1171 TS_SEQ_100HZ = 3
1172 TS_SEQ_1000HZ = 4
1173 TS_SEQ_UNSUPPORTED = 5 # System didn't send back a timestamp
1175 IPID_SEQ_UNKNOWN = 0
1176 IPID_SEQ_INCR = 1 # simple increment by one each time
1177 IPID_SEQ_BROKEN_INCR = 2 # Stupid MS -- forgot htons() so it counts by 256 on little-endian platforms
1178 IPID_SEQ_RPI = 3 # Goes up each time but by a "random" positive increment
1179 IPID_SEQ_RD = 4 # Appears to select IPID using a "random" distributions (meaning it can go up or down)
1180 IPID_SEQ_CONSTANT = 5 # Contains 1 or more sequential duplicates
1181 IPID_SEQ_ZERO = 6 # Every packet that comes back has an IP.ID of 0 (eg Linux 2.4 does this)
1183 def __init__(self, id, addresses, tcp_ports):
1184 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1185 self.t.set_SYN()
1186 self.t.set_th_seq(id) # Used to match results with sent packets.
1188 def process(self, p):
1189 raise Exception("Method process is meaningless for class %s." % self.__class__.__name__)
1192class nmap2_seq(nmap2_tcp_probe):
1193 TS_SEQ_UNKNOWN = 0
1194 TS_SEQ_ZERO = 1 # At least one of the timestamps we received back was 0
1195 TS_SEQ_UNSUPPORTED = 5 # System didn't send back a timestamp
1197 IPID_SEQ_UNKNOWN = 0
1198 IPID_SEQ_INCR = 1 # simple increment by one each time
1199 IPID_SEQ_BROKEN_INCR = 2 # Stupid MS -- forgot htons() so it counts by 256 on little-endian platforms
1200 IPID_SEQ_RPI = 3 # Goes up each time but by a "random" positive increment
1201 IPID_SEQ_RD = 4 # Appears to select IPID using a "random" distributions (meaning it can go up or down)
1202 IPID_SEQ_CONSTANT = 5 # Contains 1 or more sequential duplicates
1203 IPID_SEQ_ZERO = 6 # Every packet that comes back has an IP.ID of 0 (eg Linux 2.4 does this)
1205 def __init__(self, id, addresses, tcp_ports, options):
1206 nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, 1,
1207 id, options)
1208 self.t.set_SYN()
1210 def process(self, p):
1211 raise Exception("Method process is meaningless for class %s." % self.__class__.__name__)
1213class nmap2_seq_1(nmap2_seq):
1214 # Packet #1: window scale (10),
1215 # NOP,
1216 # MSS (1460),
1217 # timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1218 # SACK permitted.
1219 # The window field is 1.
1220 tcp_options = [
1221 TCPOption(TCPOption.TCPOPT_WINDOW, 10),
1222 TCPOption(TCPOption.TCPOPT_NOP),
1223 TCPOption(TCPOption.TCPOPT_MAXSEG, 1460),
1224 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1225 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED)
1226 ]
1228 def __init__(self, id, addresses, tcp_ports):
1229 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1230 self.t.set_th_win(1)
1232class nmap2_seq_2(nmap2_seq):
1233 # Packet #2: MSS (1400),
1234 # window scale (0),
1235 # SACK permitted,
1236 # timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1237 # EOL.
1238 # The window field is 63.
1239 tcp_options = [
1240 TCPOption(TCPOption.TCPOPT_MAXSEG, 1400),
1241 TCPOption(TCPOption.TCPOPT_WINDOW, 0),
1242 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1243 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1244 TCPOption(TCPOption.TCPOPT_EOL)
1245 ]
1247 def __init__(self, id, addresses, tcp_ports):
1248 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1249 self.t.set_th_win(63)
1251class nmap2_seq_3(nmap2_seq):
1252 # Packet #3: Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1253 # NOP,
1254 # NOP,
1255 # window scale (5),
1256 # NOP,
1257 # MSS (640).
1258 # The window field is 4.
1259 tcp_options = [
1260 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1261 TCPOption(TCPOption.TCPOPT_NOP),
1262 TCPOption(TCPOption.TCPOPT_NOP),
1263 TCPOption(TCPOption.TCPOPT_WINDOW, 5),
1264 TCPOption(TCPOption.TCPOPT_NOP),
1265 TCPOption(TCPOption.TCPOPT_MAXSEG, 640)
1266 ]
1268 def __init__(self, id, addresses, tcp_ports):
1269 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1270 self.t.set_th_win(4)
1272class nmap2_seq_4(nmap2_seq):
1273 # Packet #4: SACK permitted,
1274 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1275 # window scale (10),
1276 # EOL.
1277 # The window field is 4.
1278 tcp_options = [
1279 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1280 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1281 TCPOption(TCPOption.TCPOPT_WINDOW, 10),
1282 TCPOption(TCPOption.TCPOPT_EOL)
1283 ]
1285 def __init__(self, id, addresses, tcp_ports):
1286 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1287 self.t.set_th_win(4)
1289class nmap2_seq_5(nmap2_seq):
1290 # Packet #5: MSS (536),
1291 # SACK permitted,
1292 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1293 # window scale (10),
1294 # EOL.
1295 # The window field is 16.
1296 tcp_options = [
1297 TCPOption(TCPOption.TCPOPT_MAXSEG, 536),
1298 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1299 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1300 TCPOption(TCPOption.TCPOPT_WINDOW, 10),
1301 TCPOption(TCPOption.TCPOPT_EOL)
1302 ]
1304 def __init__(self, id, addresses, tcp_ports):
1305 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1306 self.t.set_th_win(16)
1308class nmap2_seq_6(nmap2_seq):
1309 # Packet #6: MSS (265),
1310 # SACK permitted,
1311 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0).
1312 # The window field is 512.
1313 tcp_options = [
1314 TCPOption(TCPOption.TCPOPT_MAXSEG, 265),
1315 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1316 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF)
1317 ]
1319 def __init__(self, id, addresses, tcp_ports):
1320 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1321 self.t.set_th_win(512)
1323class nmap1_seq_container(os_id_test):
1324 def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs):
1325 os_id_test.__init__(self, 0)
1327 self.num_seq_samples = num_seq_samples
1328 self.seq_responses = responses
1329 self.seq_num_responses = len(responses)
1330 self.seq_diffs = seq_diffs
1331 self.ts_diffs = ts_diffs
1332 self.time_diffs = time_diffs
1333 self.pre_ts_seqclass = nmap1_seq.TS_SEQ_UNKNOWN
1335 def test_id(self):
1336 return "TSEQ"
1338 def set_ts_seqclass(self, ts_seqclass):
1339 self.pre_ts_seqclass = ts_seqclass
1341 def process(self):
1342 ipid_seqclass = self.ipid_sequence()
1343 if nmap1_seq.TS_SEQ_UNKNOWN != self.pre_ts_seqclass:
1344 ts_seqclass = self.pre_ts_seqclass
1345 else:
1346 ts_seqclass = self.ts_sequence()
1348 if self.seq_num_responses >= 4:
1349 seq_seqclass = self.seq_sequence()
1350 if nmap1_seq.SEQ_UNKNOWN != seq_seqclass:
1351 self.add_seqclass(seq_seqclass)
1352 if nmap1_seq.IPID_SEQ_UNKNOWN != ipid_seqclass:
1353 self.add_ipidclass(ipid_seqclass)
1354 if nmap1_seq.TS_SEQ_UNKNOWN != ts_seqclass:
1355 self.add_tsclass(ts_seqclass)
1356 else:
1357 LOG.error("Insufficient responses for TCP sequencing (%d out of %d), OS detection may be less accurate."
1358 % (self.seq_num_responses, self.num_seq_samples))
1360 def get_final_result(self):
1361 "Returns a string representation of the final result of this test or None if no response was received"
1362 return {self.test_id(): self.get_result_dict()}
1364 def ipid_sequence(self):
1365 if self.seq_num_responses < 2:
1366 return nmap1_seq.IPID_SEQ_UNKNOWN
1368 ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1))
1370 null_ipids = 1
1371 for i in xrange(1, self.seq_num_responses):
1372 prev_ipid = self.seq_responses[i-1].get_ipid()
1373 cur_ipid = self.seq_responses[i].get_ipid()
1375 if cur_ipid < prev_ipid and (cur_ipid > 500 or prev_ipid < 65000):
1376 return nmap1_seq.IPID_SEQ_RD
1378 if prev_ipid != 0 or cur_ipid != 0:
1379 null_ipids = 0
1380 ipid_diffs[i-1] = abs(cur_ipid - prev_ipid)
1382 if null_ipids:
1383 return nmap1_seq.IPID_SEQ_ZERO
1385 # Battle plan:
1386 # If any diff is > 1000, set to random, if 0, set to constant.
1387 # If any of the diffs are 1, or all are less than 9, set to incremental.
1389 for i in xrange(0, self.seq_num_responses - 1):
1390 if ipid_diffs[i] > 1000:
1391 return nmap1_seq.IPID_SEQ_RPI
1392 if ipid_diffs[i] == 0:
1393 return nmap1_seq.IPID_SEQ_CONSTANT
1395 is_incremental = 1 # All diferences are less than 9
1396 is_ms = 1 # All diferences are multiples of 256
1397 for i in xrange(0, self.seq_num_responses - 1):
1398 if ipid_diffs[i] == 1:
1399 return nmap1_seq.IPID_SEQ_INCR
1400 if is_ms and ipid_diffs[i] < 2560 and (ipid_diffs[i] % 256) != 0:
1401 is_ms = 0
1402 if ipid_diffs[i] > 9:
1403 is_incremental = 0
1405 if is_ms:
1406 return nmap1_seq.IPID_SEQ_BROKEN_INCR
1407 if is_incremental:
1408 return nmap1_seq.IPID_SEQ_INCR
1410 return nmap1_seq.IPID_SEQ_UNKNOWN
1412 def ts_sequence(self):
1413 if self.seq_num_responses < 2:
1414 return nmap1_seq.TS_SEQ_UNKNOWN
1416 # Battle plan:
1417 # 1) Compute average increments per second, and variance in incr. per second.
1418 # 2) If any are 0, set to constant.
1419 # 3) If variance is high, set to random incr. [ skip for now ]
1420 # 4) if ~10/second, set to appropriate thing.
1421 # 5) Same with ~100/s.
1423 avg_freq = 0.0
1424 for i in xrange(0, self.seq_num_responses - 1):
1425 dhz = self.ts_diffs[i] / self.time_diffs[i]
1426 avg_freq += dhz / (self.seq_num_responses - 1)
1428 LOG.info("The avg TCP TS HZ is: %f" % avg_freq)
1430 if 0 < avg_freq < 3.9:
1431 return nmap1_seq.TS_SEQ_2HZ
1432 if 85 < avg_freq < 115:
1433 return nmap1_seq.TS_SEQ_100HZ
1434 if 900 < avg_freq < 1100:
1435 return nmap1_seq.TS_SEQ_1000HZ
1437 return nmap1_seq.TS_SEQ_UNKNOWN
1439 def seq_sequence(self):
1440 self.seq_gcd = reduce(my_gcd, self.seq_diffs)
1441 avg_incr = 0
1442 seqclass = nmap1_seq.SEQ_UNKNOWN
1444 if 0 != self.seq_gcd:
1445 map(lambda x, gcd = self.seq_gcd: x / gcd, self.seq_diffs)
1446 for i in xrange(0, self.seq_num_responses - 1):
1447 if abs(self.seq_responses[i+1].get_seq() - self.seq_responses[i].get_seq()) > 50000000:
1448 seqclass = nmap1_seq.SEQ_TR;
1449 self.index = 9999999
1450 break
1451 avg_incr += self.seq_diffs[i]
1453 if 0 == self.seq_gcd:
1454 seqclass = nmap1_seq.SEQ_CONSTANT
1455 self.index = 0
1456 elif 0 == self.seq_gcd % 64000:
1457 seqclass = nmap1_seq.SEQ_64K
1458 self.index = 1
1459 elif 0 == self.seq_gcd % 800:
1460 seqclass = nmap1_seq.SEQ_i800
1461 self.index = 10
1462 elif nmap1_seq.SEQ_UNKNOWN == seqclass:
1463 avg_incr = int(.5 + avg_incr / (self.seq_num_responses - 1))
1464 sum_incr = 0.0
1465 for i in range(0, self.seq_num_responses - 1):
1466 d = abs(self.seq_diffs[i] - avg_incr)
1467 sum_incr += float(d * d)
1468 sum_incr /= self.seq_num_responses - 1
1469 self.index = int(.5 + math.sqrt(sum_incr))
1470 if self.index < 75:
1471 seqclass = nmap1_seq.SEQ_TD
1472 else:
1473 seqclass = nmap1_seq.SEQ_RI
1475 return seqclass
1477 seqclasses = {
1478 nmap1_seq.SEQ_64K: '64K',
1479 nmap1_seq.SEQ_TD: 'TD',
1480 nmap1_seq.SEQ_RI: 'RI',
1481 nmap1_seq.SEQ_TR: 'TR',
1482 nmap1_seq.SEQ_i800: 'i800',
1483 nmap1_seq.SEQ_CONSTANT: 'C',
1484 }
1486 def add_seqclass(self, id):
1487 self.add_result('CLASS', nmap1_seq_container.seqclasses[id])
1489 if nmap1_seq.SEQ_CONSTANT == id:
1490 self.add_result('VAL', '%i' % self.seq_responses[0].get_seq())
1491 elif id in (nmap1_seq.SEQ_TD, nmap1_seq.SEQ_RI):
1492 self.add_result('GCD', '%i' % self.seq_gcd)
1493 self.add_result('SI', '%i' % self.index)
1495 tsclasses = {
1496 nmap1_seq.TS_SEQ_ZERO: '0',
1497 nmap1_seq.TS_SEQ_2HZ: '2HZ',
1498 nmap1_seq.TS_SEQ_100HZ: '100HZ',
1499 nmap1_seq.TS_SEQ_1000HZ: '1000HZ',
1500 nmap1_seq.TS_SEQ_UNSUPPORTED: 'U',
1501 }
1503 def add_tsclass(self, id):
1504 self.add_result('TS', nmap1_seq_container.tsclasses[id])
1506 ipidclasses = {
1507 nmap1_seq.IPID_SEQ_INCR: 'I',
1508 nmap1_seq.IPID_SEQ_BROKEN_INCR: 'BI',
1509 nmap1_seq.IPID_SEQ_RPI: 'RPI',
1510 nmap1_seq.IPID_SEQ_RD: 'RD',
1511 nmap1_seq.IPID_SEQ_CONSTANT: 'C',
1512 nmap1_seq.IPID_SEQ_ZERO: 'Z',
1513 }
1515 def add_ipidclass(self, id):
1516 self.add_result('IPID', nmap1_seq_container.ipidclasses[id])
1519class nmap2_seq_container(os_id_test):
1520 def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs):
1521 os_id_test.__init__(self, 0)
1523 self.num_seq_samples = num_seq_samples
1524 self.seq_responses = responses
1525 self.seq_num_responses = len(responses)
1526 self.seq_diffs = seq_diffs
1527 self.ts_diffs = ts_diffs
1528 self.time_diffs = time_diffs
1529 self.pre_ts_seqclass = nmap2_seq.TS_SEQ_UNKNOWN
1531 def test_id(self):
1532 return "SEQ"
1534 def set_ts_seqclass(self, ts_seqclass):
1535 self.pre_ts_seqclass = ts_seqclass
1537 def process(self):
1538 if self.seq_num_responses >= 4:
1539 self.calc_ti()
1540 self.calc_ts()
1541 self.calc_sp()
1542 else:
1543 self.add_result('R', 'N')
1544 LOG.error("Insufficient responses for TCP sequencing (%d out of %d), OS detection may be less accurate."
1545 % (self.seq_num_responses, self.num_seq_samples))
1547 def get_final_result(self):
1548 return {self.test_id(): self.get_result_dict()}
1550 def calc_ti(self):
1551 if self.seq_num_responses < 2:
1552 return
1554 ipidclasses = {
1555 nmap2_seq.IPID_SEQ_INCR: 'I',
1556 nmap2_seq.IPID_SEQ_BROKEN_INCR: 'BI',
1557 nmap2_seq.IPID_SEQ_RPI: 'RI',
1558 nmap2_seq.IPID_SEQ_RD: 'RD',
1559 nmap2_seq.IPID_SEQ_CONSTANT: 'C',
1560 nmap2_seq.IPID_SEQ_ZERO: 'Z',
1561 }
1563 ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1))
1565 # Random and zero
1566 null_ipids = 1
1567 for i in xrange(1, self.seq_num_responses):
1568 prev_ipid = self.seq_responses[i-1].get_ipid()
1569 cur_ipid = self.seq_responses[i].get_ipid()
1571 if prev_ipid != 0 or cur_ipid != 0:
1572 null_ipids = 0
1574 if prev_ipid <= cur_ipid:
1575 ipid_diffs[i-1] = cur_ipid - prev_ipid
1576 else:
1577 ipid_diffs[i-1] = (cur_ipid - prev_ipid + 65536) & 0xffff
1579 if self.seq_num_responses > 2 and ipid_diffs[i-1] > 20000:
1580 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_RD])
1581 return
1583 if null_ipids:
1584 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_ZERO])
1585 return
1587 # Constant
1588 all_zero = 1
1589 for i in xrange(0, self.seq_num_responses - 1):
1590 if ipid_diffs[i] != 0:
1591 all_zero = 0
1592 break
1594 if all_zero:
1595 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_CONSTANT])
1596 return
1598 # Random positive increments
1599 for i in xrange(0, self.seq_num_responses - 1):
1600 if ipid_diffs[i] > 1000 and \
1601 ((ipid_diffs[i] % 256 != 0) or \
1602 ((ipid_diffs[i] % 256 == 0) and (ipid_diffs[i] >= 25600))):
1603 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_RPI])
1604 return
1606 # Broken Increment and Incremental
1607 is_incremental = 1 # All diferences are less than 10
1608 is_ms = 1 # All diferences are multiples of 256 and no greater than 5120
1609 for i in xrange(0, self.seq_num_responses - 1):
1610 if is_ms and ((ipid_diffs[i] > 5120) or (ipid_diffs[i] % 256) != 0):
1611 is_ms = 0
1612 if is_incremental and ipid_diffs[i] > 9:
1613 is_incremental = 0
1615 if is_ms:
1616 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_BROKEN_INCR])
1617 elif is_incremental:
1618 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_INCR])
1620 def calc_ts(self):
1621 # 1. If any of the responses have no timestamp option, TS
1622 # is set to U (unsupported).
1623 # 2. If any of the timestamp values are zero, TS is set to 0.
1624 # 3. If the average increments per second falls within the
1625 # ranges 0-5.66, 70-150, or 150-350, TS is set to 1, 7, or 8,
1626 # respectively. These three ranges get special treatment
1627 # because they correspond to the 2 Hz, 100 Hz, and 200 Hz
1628 # frequencies used by many hosts.
1629 # 4. In all other cases, Nmap records the binary logarithm of
1630 # the average increments per second, rounded to the nearest
1631 # integer. Since most hosts use 1,000 Hz frequencies, A is
1632 # a common result.
1634 if self.pre_ts_seqclass == nmap2_seq.TS_SEQ_ZERO:
1635 self.add_result('TS', '0')
1636 elif self.pre_ts_seqclass == nmap2_seq.TS_SEQ_UNSUPPORTED:
1637 self.add_result('TS', 'U')
1638 elif self.seq_num_responses < 2:
1639 return
1641 avg_freq = 0.0
1642 for i in xrange(0, self.seq_num_responses - 1):
1643 dhz = self.ts_diffs[i] / self.time_diffs[i]
1644 avg_freq += dhz / (self.seq_num_responses - 1)
1646 LOG.info("The avg TCP TS HZ is: %f" % avg_freq)
1648 if avg_freq <= 5.66:
1649 self.add_result('TS', "1")
1650 elif 70 < avg_freq <= 150:
1651 self.add_result('TS', "7")
1652 elif 150 < avg_freq <= 350:
1653 self.add_result('TS', "8")
1654 else:
1655 ts = int(round(.5 + math.log(avg_freq)/math.log(2)))
1656 self.add_result('TS', "%X" % ts)
1658 def calc_sp(self):
1659 seq_gcd = reduce(my_gcd, self.seq_diffs)
1661 seq_avg_rate = 0.0
1662 for i in xrange(0, self.seq_num_responses - 1):
1663 seq_avg_rate += self.seq_diffs[i] / self.time_diffs[i]
1664 seq_avg_rate /= (self.seq_num_responses - 1)
1666 seq_rate = seq_avg_rate
1667 si_index = 0
1668 seq_stddev = 0
1670 if 0 == seq_gcd:
1671 seq_rate = 0
1672 else:
1673 seq_rate = int(round(.5 + (math.log(seq_rate) / math.log(2)) * 8))
1675 div_gcd = 1
1676 if seq_gcd > 9:
1677 div_gcd = seq_gcd
1679 for i in xrange(0, self.seq_num_responses - 1):
1680 rtmp = (self.seq_diffs[i] / self.time_diffs[i]) / div_gcd - \
1681 seq_avg_rate / div_gcd
1682 seq_stddev += rtmp * rtmp
1684 seq_stddev /= self.seq_num_responses - 2
1685 seq_stddev = math.sqrt(seq_stddev)
1687 if seq_stddev <= 1:
1688 si_index = 0
1689 else:
1690 si_index = int(round(.5 + (math.log(seq_stddev) / math.log(2)) * 8.0))
1692 self.add_result('SP', "%X" % si_index)
1693 self.add_result('GCD', "%X" % seq_gcd)
1694 self.add_result('ISR', "%X" % seq_rate)
1696class nmap2_ops_container(os_id_test):
1697 def __init__(self, responses):
1698 os_id_test.__init__(self, 0)
1700 self.seq_responses = responses
1701 self.seq_num_responses = len(responses)
1703 def test_id(self):
1704 return "OPS"
1706 def process(self):
1707 if self.seq_num_responses != 6:
1708 self.add_result('R', 'N')
1709 return
1711 for i in xrange(0, self.seq_num_responses):
1712 tests = nmap2_tcp_tests(self.seq_responses[i].get_ip(),
1713 self.seq_responses[i].get_tcp(),
1714 0,
1715 0)
1716 self.add_result("O%i" % (i+1), tests.get_options())
1718 def get_final_result(self):
1719 if not self.get_result_dict():
1720 return None
1721 else:
1722 return {self.test_id(): self.get_result_dict()}
1724class nmap2_win_container(os_id_test):
1725 def __init__(self, responses):
1726 os_id_test.__init__(self, 0)
1728 self.seq_responses = responses
1729 self.seq_num_responses = len(responses)
1731 def test_id(self):
1732 return "WIN"
1734 def process(self):
1735 if self.seq_num_responses != 6:
1736 self.add_result('R', 'N')
1737 return
1739 for i in xrange(0, self.seq_num_responses):
1740 tests = nmap2_tcp_tests(self.seq_responses[i].get_ip(),
1741 self.seq_responses[i].get_tcp(),
1742 0,
1743 0)
1744 self.add_result("W%i" % (i+1), tests.get_win())
1746 def get_final_result(self):
1747 if not self.get_result_dict():
1748 return None
1749 else:
1750 return {self.test_id(): self.get_result_dict()}
1752class nmap2_t1_container(os_id_test):
1753 def __init__(self, responses, seq_base):
1754 os_id_test.__init__(self, 0)
1756 self.seq_responses = responses
1757 self.seq_num_responses = len(responses)
1758 self.seq_base = seq_base
1760 def test_id(self):
1761 return "T1"
1763 def process(self):
1764 # R, DF, T*, TG*, W-, S, A, F, O-, RD*, Q
1765 if self.seq_num_responses < 1:
1766 self.add_result("R","N")
1767 return
1769 response = self.seq_responses[0]
1770 tests = nmap2_tcp_tests(response.get_ip(),
1771 response.get_tcp(),
1772 self.seq_base,
1773 nmap2_tcp_probe.acknowledgment)
1774 self.add_result("R", "Y")
1775 self.add_result("DF", tests.get_df())
1776 self.add_result("S", tests.get_seq())
1777 self.add_result("A", tests.get_ack())
1778 self.add_result("F", tests.get_flags())
1779 self.add_result("Q", tests.get_quirks())
1781 def get_final_result(self):
1782 if not self.get_result_dict():
1783 return None
1784 else:
1785 return {self.test_id(): self.get_result_dict()}
1787class nmap2_icmp_container(os_id_test):
1788 def __init__(self, responses):
1789 os_id_test.__init__(self, 0)
1791 self.icmp_responses = responses
1792 self.icmp_num_responses = len(responses)
1794 def test_id(self):
1795 return "IE"
1797 def process(self):
1798 # R, DFI, T*, TG*, TOSI, CD, SI, DLI*
1799 if self.icmp_num_responses != 2:
1800 self.add_result("R","N")
1801 return
1803 ip1 = self.icmp_responses[0].child()
1804 ip2 = self.icmp_responses[1].child()
1805 icmp1 = ip1.child()
1806 icmp2 = ip2.child()
1808 self.add_result("R", "Y")
1810 # Value Description
1811 # N Neither of the ping responses have the DF bit set.
1812 # S Both responses echo the DF value of the probe.
1813 # Y Both of the response DF bits are set.
1814 # O The one remaining other combination-both responses have the DF bit toggled.
1815 if not ip1.get_ip_df() and not ip2.get_ip_df():
1816 self.add_result("DFI","N")
1817 elif ip1.get_ip_df() and not ip2.get_ip_df():
1818 self.add_result("DFI","S")
1819 elif ip1.get_ip_df() and ip2.get_ip_df():
1820 self.add_result("DFI","Y")
1821 else:
1822 self.add_result("DFI","O")
1824 # Value Description
1825 # Z Both TOS values are zero.
1826 # S Both TOS values are each the same as in the corresponding probe.
1827 # <NN> When they both use the same non-zero number, it is recorded here.
1828 # O Any other combination.
1829 if ip1.get_ip_tos() == 0 and ip2.get_ip_tos() == 0:
1830 self.add_result("TOSI","Z")
1831 elif ip1.get_ip_tos() == 0 and ip2.get_ip_tos() == 4:
1832 self.add_result("TOSI","S")
1833 elif ip1.get_ip_tos() == ip2.get_ip_tos():
1834 self.add_result("TOSI","%X" % ip1.get_ip_tos())
1835 else:
1836 self.add_result("TOSI","O")
1838 # Value Description
1839 # Z Both code values are zero.
1840 # S Both code values are the same as in the corresponding probe.
1841 # <NN> When they both use the same non-zero number, it is shown here.
1842 # O Any other combination.
1843 if icmp1.get_icmp_code() == 0 and icmp2.get_icmp_code() == 0:
1844 self.add_result("CD","Z")
1845 elif icmp1.get_icmp_code() == 9 and icmp2.get_icmp_code() == 0:
1846 self.add_result("CD","S")
1847 elif icmp1.get_icmp_code() == icmp2.get_icmp_code():
1848 self.add_result("CD","%X" % icmp1.get_icmp_code())
1849 else:
1850 self.add_result("CD","O")
1852 # Value Description
1853 # Z Both sequence numbers are set to 0.
1854 # S Both sequence numbers echo the ones from the probes.
1855 # <NNNN> When they both use the same non-zero number, it is recorded here.
1856 # O Any other combination.
1857 if icmp1.get_icmp_seq() == 0 and icmp2.get_icmp_seq() == 0:
1858 self.add_result("SI","Z")
1859 elif (icmp1.get_icmp_seq() == nmap2_icmp_echo_probe_1.sequence_number and
1860 icmp2.get_icmp_seq() == nmap2_icmp_echo_probe_1.sequence_number + 1):
1861 self.add_result("SI","S")
1862 elif icmp1.get_icmp_seq() == icmp2.get_icmp_seq():
1863 self.add_result("SI","%X" % icmp1.get_icmp_code())
1864 else:
1865 self.add_result("SI","O")
1867 def get_final_result(self):
1868 if not self.get_result_dict():
1869 return None
1870 else:
1871 return {self.test_id(): self.get_result_dict()}
1873class nmap1_tcp_closed_1(nmap1_tcp_probe):
1874 def __init__(self, id, addresses, tcp_ports):
1875 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0)
1876 self.t.set_SYN()
1878 def test_id(self):
1879 return "T5"
1881 def is_mine(self, packet):
1882 if tcp_probe.is_mine(self, packet):
1883 ip = packet.child()
1884 if not ip:
1885 return 0
1886 tcp = ip.child()
1887 if not tcp:
1888 return 0
1889 if tcp.get_RST():
1890 return 1
1891 else:
1892 return 0
1893 else:
1894 return 0
1896class nmap2_tcp_closed_1(nmap2_tcp_probe_2_6):
1897 # ...
1898 # T5 sends a TCP SYN packet without IP DF and a window field of
1899 # 31337 to a closed port
1900 # ...
1901 def __init__(self, id, addresses, tcp_ports):
1902 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0)
1903 self.t.set_SYN()
1904 self.i.set_ip_df(0)
1905 self.t.set_th_win(31337)
1907 def test_id(self):
1908 return "T5"
1911class nmap1_tcp_closed_2(nmap1_tcp_probe):
1913 def __init__(self, id, addresses, tcp_ports):
1914 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0)
1915 self.t.set_ACK()
1917 def test_id(self):
1918 return "T6"
1921class nmap2_tcp_closed_2(nmap2_tcp_probe_2_6):
1922 # ...
1923 # T6 sends a TCP ACK packet with IP DF and a window field of
1924 # 32768 to a closed port.
1925 # ...
1926 def __init__(self, id, addresses, tcp_ports):
1927 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0)
1928 self.t.set_ACK()
1929 self.i.set_ip_df(1)
1930 self.t.set_th_win(32768)
1932 def test_id(self):
1933 return "T6"
1936class nmap1_tcp_closed_3(nmap1_tcp_probe):
1938 def __init__(self, id, addresses, tcp_ports):
1939 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0)
1940 self.t.set_FIN()
1941 self.t.set_URG()
1942 self.t.set_PSH()
1944 def test_id(self):
1945 return "T7"
1948class nmap2_tcp_closed_3(nmap2_tcp_probe_7):
1949 # ...
1950 # T7 sends a TCP packet with the FIN, PSH, and URG flags set and a
1951 # window field of 65535 to a closed port. The IP DF bit is not set.
1952 # ...
1953 def __init__(self, id, addresses, tcp_ports):
1954 nmap2_tcp_probe_7.__init__(self, id, addresses, tcp_ports, 0)
1955 self.t.set_FIN()
1956 self.t.set_URG()
1957 self.t.set_PSH()
1958 self.t.set_th_win(65535)
1959 self.i.set_ip_df(0)
1961 def test_id(self):
1962 return "T7"
1965class NMAP2_OS_Class:
1966 def __init__(self, vendor, name, family, device_type):
1967 self.__vendor = vendor
1968 self.__name = name
1969 self.__family = family
1970 self.__device_type = device_type
1972 def get_vendor(self):
1973 return self.__vendor
1974 def get_name(self):
1975 return self.__name
1976 def get_family(self):
1977 return self.__family
1978 def get_device_type(self):
1979 return self.__device_type
1981class NMAP2_Fingerprint:
1982 def __init__(self, id, os_class, tests):
1983 self.__id = id
1984 self.__os_class = os_class
1985 self.__tests = tests
1987 def get_id(self):
1988 return self.__id
1989 def get_os_class(self):
1990 return self.__os_class
1991 def get_tests(self):
1992 return self.__tests
1994 def __str__(self):
1995 ret = "FP: [%s]" % self.__id
1996 ret += "\n vendor: %s" % self.__os_class.get_vendor()
1997 ret += "\n name: %s" % self.__os_class.get_name()
1998 ret += "\n family: %s" % self.__os_class.get_family()
1999 ret += "\n device_type: %s" % self.__os_class.get_device_type()
2001 for test in self.__tests:
2002 ret += "\n test: %s" % test
2003 for pair in self.__tests[test]:
2004 ret += "\n %s = [%s]" % (pair, self.__tests[test][pair])
2006 return ret
2008 literal_conv = { "RIPL" : { "G" : 0x148 },
2009 "RID" : { "G" : 0x1042 },
2010 "RUL" : { "G" : 0x134 } }
2012 def parse_int(self, field, value):
2013 try:
2014 return int(value, 16)
2015 except ValueError:
2016 if field in NMAP2_Fingerprint.literal_conv:
2017 if value in NMAP2_Fingerprint.literal_conv[field]:
2018 return NMAP2_Fingerprint.literal_conv[field][value]
2019 return 0
2021 def match(self, field, ref, value):
2022 options = ref.split("|")
2024 for option in options:
2025 if option.startswith(">"):
2026 if self.parse_int(field, value) > \
2027 self.parse_int(field, option[1:]):
2028 return True
2029 elif option.startswith("<"):
2030 if self.parse_int(field, value) < \
2031 self.parse_int(field, option[1:]):
2032 return True
2033 elif option.find("-") > -1:
2034 range = option.split("-")
2035 if self.parse_int (field, value) >= self.parse_int (field, range[0]) and \
2036 self.parse_int (field, value) <= self.parse_int (field, range[1]):
2037 return True
2038 else:
2039 if str(value) == str(option):
2040 return True
2042 return False
2044 def compare(self, sample, mp):
2045 max_points = 0
2046 total_points = 0
2048 for test in self.__tests:
2049 # ignore unknown response lines:
2050 if test not in sample:
2051 continue
2053 for field in self.__tests[test]:
2054 # ignore unsupported fields:
2055 if field not in sample[test] or \
2056 test not in mp or \
2057 field not in mp[test]:
2058 continue
2060 ref = self.__tests[test][field]
2061 value = sample[test][field]
2063 points = int(mp[test][field])
2065 max_points += points
2067 if self.match(field, ref, value):
2068 total_points += points
2070 return (total_points / float(max_points)) * 100
2072class NMAP2_Fingerprint_Matcher:
2073 def __init__(self, filename):
2074 self.__filename = filename
2076 def find_matches(self, res, threshold):
2077 output = []
2079 try:
2080 infile = open(self.__filename,"r")
2082 mp = self.parse_mp(self.matchpoints(infile))
2084 for fingerprint in self.fingerprints(infile):
2085 fp = self.parse_fp(fingerprint)
2086 similarity = fp.compare(res, mp)
2087 if similarity >= threshold:
2088 print("\"%s\" matches with an accuracy of %.2f%%" \
2089 % (fp.get_id(), similarity))
2090 output.append((similarity / 100,
2091 fp.get_id(),
2092 (fp.get_os_class().get_vendor(),
2093 fp.get_os_class().get_name(),
2094 fp.get_os_class().get_family(),
2095 fp.get_os_class().get_device_type())))
2097 infile.close()
2098 except IOError as err:
2099 print("IOError: %s", err)
2101 return output
2103 def sections(self, infile, token):
2104 OUT = 0
2105 IN = 1
2107 state = OUT
2108 output = []
2110 for line in infile:
2111 line = line.strip()
2112 if state == OUT:
2113 if line.startswith(token):
2114 state = IN
2115 output = [line]
2116 elif state == IN:
2117 if line:
2118 output.append(line)
2119 else:
2120 state = OUT
2121 yield output
2122 output = []
2124 if output:
2125 yield output
2127 def fingerprints(self, infile):
2128 for section in self.sections(infile,"Fingerprint"):
2129 yield section
2131 def matchpoints(self, infile):
2132 return self.sections(infile,"MatchPoints").next()
2134 def parse_line(self, line):
2135 name = line[:line.find("(")]
2136 pairs = line[line.find("(") + 1 : line.find(")")]
2138 test = {}
2140 for pair in pairs.split("%"):
2141 pair = pair.split("=")
2142 test[pair[0]] = pair[1]
2144 return (name, test)
2146 def parse_fp(self, fp):
2147 tests = {}
2149 for line in fp:
2150 if line.startswith("#"):
2151 continue
2152 elif line.startswith("Fingerprint"):
2153 fingerprint = line[len("Fingerprint") + 1:]
2154 elif line.startswith("Class"):
2155 (vendor,
2156 name,
2157 family,
2158 device_type) = line[len("Class") + 1:].split("|")
2159 os_class = NMAP2_OS_Class(vendor.strip(),
2160 name.strip(),
2161 family.strip(),
2162 device_type.strip())
2163 else:
2164 test = self.parse_line(line)
2165 tests[test[0]] = test[1]
2167 return NMAP2_Fingerprint(fingerprint, os_class, tests)
2169 def parse_mp(self, fp):
2170 tests = {}
2172 for line in fp:
2173 if line.startswith("#"):
2174 continue
2175 elif line.startswith("MatchPoints"):
2176 continue
2177 else:
2178 test = self.parse_line(line)
2179 tests[test[0]] = test[1]
2181 return tests