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# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2018 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
10import math
11import array
12from six.moves import xrange, reduce
14from pcapy import lookupdev, open_live
15from impacket.ImpactPacket import UDP, TCPOption, Data, TCP, IP, ICMP, Ethernet
16from impacket.ImpactDecoder import EthDecoder
17from impacket import LOG
19g_nmap1_signature_filename="nmap-os-fingerprints"
20g_nmap2_signature_filename="nmap-os-db"
23def my_gcd(a, b):
24 if a < b:
25 c = a
26 a = b
27 b = c
29 while 0 != b:
30 c = a & b
31 a = b
32 b = c
33 return a
35class os_id_exception:
36 def __init__(self, value):
37 self.value = value
38 def __str__(self):
39 return repr(self.value)
41class os_id_test:
43 def __init__(self, id):
44 self.__id = id
45 self.__my_packet = None
46 self.__result_dict = {}
48 def test_id(self):
49 return self.__class__.__name__
51 def get_test_packet(self):
52 return self.__my_packet.get_packet()
54 def set_packet(self, packet):
55 self.__my_packet = packet
57 def get_packet(self):
58 return self.__my_packet
60 def process(self, packet):
61 pass
63 def add_result(self, name, value):
64 self.__result_dict[name] = value
66 def get_id(self):
67 return self.__id
68 def is_mine(self, packet):
69 pass
71 def get_result_dict(self):
72 return self.__result_dict;
74 def get_final_result(self):
75 "Returns a string representation of the final result of this test or None if no response was received"
76 pass
79class icmp_request(os_id_test):
80 type_filter = { ICMP.ICMP_ECHO : ICMP.ICMP_ECHOREPLY,
81 ICMP.ICMP_IREQ : ICMP.ICMP_IREQREPLY,
82 ICMP.ICMP_MASKREQ : ICMP.ICMP_MASKREPLY,
83 ICMP.ICMP_TSTAMP : ICMP.ICMP_TSTAMPREPLY }
85 def __init__(self, id, addresses, type):
86 os_id_test.__init__(self, id)
87 self.e = Ethernet()
88 self.i = IP()
89 self.icmp = ICMP()
91 self.i.set_ip_src(addresses[0])
92 self.i.set_ip_dst(addresses[1])
94 self.__type = type
95 self.icmp.set_icmp_type(type)
97 self.e.contains(self.i)
98 self.i.contains(self.icmp)
99 self.set_packet(self.e)
101 def is_mine(self, packet):
103 if packet.get_ether_type() != IP.ethertype:
104 return 0
105 ip = packet.child()
106 if not ip or ip.get_ip_p() != ICMP.protocol:
107 return 0
108 icmp = ip.child()
110 # icmp_request.type_filter is a dictionary that maps request
111 # type codes to the reply codes
113 if not icmp or \
114 icmp.get_icmp_type() != icmp_request.type_filter[self.__type]:
115 return 0
116 if icmp.get_icmp_id() != self.get_id():
117 return 0
119 return 1
121 def process(self, packet):
122 pass
125class nmap2_icmp_echo_probe_1(icmp_request):
126 # The first one has the IP DF bit set, a type-of-service (TOS) byte
127 # value of zero, a code of nine (even though it should be zero),
128 # the sequence number 295, a random IP ID and ICMP request identifier,
129 # and a random character repeated 120 times for the data payload.
130 sequence_number = 295
131 id = 0x5678
133 def __init__(self, id, addresses):
134 icmp_request.__init__(self, id, addresses, ICMP.ICMP_ECHO)
135 self.i.set_ip_df(True)
136 self.i.set_ip_tos(0)
137 self.icmp.set_icmp_code(9)
138 self.icmp.set_icmp_seq(nmap2_icmp_echo_probe_1.sequence_number)
139 self.i.set_ip_id(nmap2_icmp_echo_probe_1.id)
140 self.icmp.set_icmp_id(nmap2_icmp_echo_probe_1.id)
141 self.icmp.contains(Data("I" * 120))
143 def process(self, packet):
144 pass
146class nmap2_icmp_echo_probe_2(icmp_request):
147 # The second ping query is similar, except a TOS of four
148 # (IP_TOS_RELIABILITY) is used, the code is zero, 150 bytes of data is
149 # sent, and the IP ID, request ID, and sequence numbers are incremented
150 # by one from the previous query values.
152 def __init__(self, id, addresses):
153 icmp_request.__init__(self, id, addresses, ICMP.ICMP_ECHO)
154 self.i.set_ip_df(False)
155 self.i.set_ip_tos(4)
156 self.icmp.set_icmp_code(0)
157 self.icmp.set_icmp_seq(nmap2_icmp_echo_probe_1.sequence_number + 1)
158 self.i.set_ip_id(nmap2_icmp_echo_probe_1.id + 1)
159 self.icmp.set_icmp_id(nmap2_icmp_echo_probe_1.id + 1)
160 self.icmp.contains(Data("I" * 150))
162 def process(self, packet):
163 pass
165class udp_closed_probe(os_id_test):
167 ip_id = 0x1234 # HARDCODED
169 def __init__(self, id, addresses, udp_closed ):
171 os_id_test.__init__(self, id )
172 self.e = Ethernet()
173 self.i = IP()
174 self.u = UDP()
176 self.i.set_ip_src(addresses[0])
177 self.i.set_ip_dst(addresses[1])
178 self.i.set_ip_id(udp_closed_probe.ip_id)
179 self.u.set_uh_sport(id)
181 self.u.set_uh_dport( udp_closed )
183 self.e.contains(self.i)
184 self.i.contains(self.u)
185 self.set_packet(self.e)
187 def is_mine(self, packet):
188 if packet.get_ether_type() != IP.ethertype:
189 return 0
190 ip = packet.child()
191 if not ip or ip.get_ip_p() != ICMP.protocol:
192 return 0
193 icmp = ip.child()
194 if not icmp or icmp.get_icmp_type() != ICMP.ICMP_UNREACH:
195 return 0
197 if icmp.get_icmp_code() != ICMP.ICMP_UNREACH_PORT:
198 return 0;
201 self.err_data = icmp.child()
202 if not self.err_data:
203 return 0
206 return 1
209class tcp_probe(os_id_test):
211 def __init__(self, id, addresses, tcp_ports, open_port ):
213 self.result_string = "[]"
214 os_id_test.__init__(self, id)
215 self.e = Ethernet()
216 self.i = IP()
217 self.t = TCP()
218 self.i.set_ip_src(addresses[0])
219 self.i.set_ip_dst(addresses[1])
220 self.i.set_ip_id(0x2323) # HARDCODED
221 self.t.set_th_sport(id)
223 if open_port:
224 self.target_port = tcp_ports[0]
225 else:
226 self.target_port = tcp_ports[1]
228 self.t.set_th_dport(self.target_port)
230 self.e.contains(self.i)
231 self.i.contains(self.t)
232 self.set_packet(self.e)
234 self.source_ip = addresses[0]
235 self.target_ip = addresses[1]
237 def socket_match(self, ip, tcp):
238 # scr ip and port
239 if (ip.get_ip_src() != self.target_ip) or (tcp.get_th_sport() != self.target_port):
240 return 0
241 # dst ip and port
242 if(ip.get_ip_dst() != self.source_ip) or (tcp.get_th_dport() != self.get_id()):
243 return 0
244 return 1
246 def is_mine(self, packet):
247 if packet.get_ether_type() != IP.ethertype:
248 return 0
249 ip = packet.child()
250 if not ip or ip.get_ip_p() != TCP.protocol:
251 return 0
252 tcp = ip.child()
253 if self.socket_match(ip, tcp):
254 return 1
256 return 0
259class nmap_tcp_probe(tcp_probe):
261 def __init__(self, id, addresses, tcp_ports, open_port, sequence, options):
262 tcp_probe.__init__(self, id, addresses, tcp_ports, open_port)
263 self.t.set_th_seq(sequence)
264 self.set_resp(False)
265 for op in options:
266 self.t.add_option(op)
268 def set_resp(self,resp):
269 pass
271class nmap1_tcp_probe(nmap_tcp_probe):
272 sequence = 0x8453 # 0xBASE, obviously
273 mss = 265
275 # From: https://nmap.org/nmap-fingerprinting-old.html
276 # [...]
277 # Nmap sends these options along with almost every probe packet:
278 # Window Scale=10; NOP; Max Segment Size = 265; Timestamp; End of Ops;
279 # [...]
280 # From nmap-4.22SOC8/osscan.cc:get_fingerprint(...)
281 # [...]
282 # "\003\003\012\001\002\004\001\011\010\012\077\077\077\077\000\000\000\000\000\000"
283 # [...]
284 tcp_options = [
285 TCPOption(TCPOption.TCPOPT_WINDOW, 0o12), #\003\003\012
286 TCPOption(TCPOption.TCPOPT_NOP), #\001
287 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011
288 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0x3F3F3F3F), #\010\012\077\077\077\077\000\000\000\000
289 TCPOption(TCPOption.TCPOPT_EOL), #\000
290 TCPOption(TCPOption.TCPOPT_EOL) #\000
291 ]
293 def __init__(self, id, addresses, tcp_ports, open_port):
294 nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
295 self.sequence, self.tcp_options)
297 def set_resp(self,resp):
298 if resp:
299 self.add_result("Resp", "Y")
300 else:
301 self.add_result("Resp", "N")
303 def process(self, packet):
304 ip = packet.child()
305 tcp = ip.child()
307 self.set_resp(True)
309 if ip.get_ip_df():
310 self.add_result("DF", "Y")
311 else:
312 self.add_result("DF", "N")
314 self.add_result("W", tcp.get_th_win())
316 if tcp.get_th_ack() == self.sequence + 1:
317 self.add_result("ACK", "S++")
318 elif tcp.get_th_ack() == self.sequence:
319 self.add_result("ACK", "S")
320 else:
321 self.add_result("ACK", "O")
323 flags = []
325 # TCP flags
326 if tcp.get_ECE():
327 flags.append("B")
328 if tcp.get_URG():
329 flags.append("U")
330 if tcp.get_ACK():
331 flags.append("A")
332 if tcp.get_PSH():
333 flags.append("P")
334 if tcp.get_RST():
335 flags.append("R")
336 if tcp.get_SYN():
337 flags.append("S")
338 if tcp.get_FIN():
339 flags.append("F")
341 self.add_result("FLAGS", flags)
343 options = []
345 for op in tcp.get_options():
346 if op.get_kind() == TCPOption.TCPOPT_EOL:
347 options.append("L")
348 elif op.get_kind() == TCPOption.TCPOPT_MAXSEG:
349 options.append("M")
350 if op.get_mss() == self.mss:
351 options.append("E") # Echoed
352 elif op.get_kind() == TCPOption.TCPOPT_NOP:
353 options.append("N")
354 elif op.get_kind() == TCPOption.TCPOPT_TIMESTAMP:
355 options.append("T")
356 elif op.get_kind() == TCPOption.TCPOPT_WINDOW:
357 options.append("W")
359 self.add_result("OPTIONS", options)
361 def get_final_result(self):
362 return {self.test_id(): self.get_result_dict()}
365class nmap2_tcp_probe(nmap_tcp_probe):
366 acknowledgment = 0x181d4f7b
368 def __init__(self, id, addresses, tcp_ports, open_port, sequence, options):
369 nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
370 sequence, options)
371 self.t.set_th_ack(self.acknowledgment)
373 def set_resp(self,resp):
374 # Responsiveness (R)
375 # This test simply records whether the target responded to a given probe.
376 # Possible values are Y and N. If there is no reply, remaining fields
377 # for the test are omitted.
378 if resp:
379 self.add_result("R", "Y")
380 else:
381 self.add_result("R", "N")
383 def process(self, packet):
384 ip = packet.child()
385 tcp = ip.child()
387 # R, DF, T*, TG*, W, S, A, F, O, RD*, Q
388 self.set_resp(True)
390 tests = nmap2_tcp_tests(ip, tcp, self.sequence, self.acknowledgment)
392 self.add_result("DF", tests.get_df())
393 self.add_result("W", tests.get_win())
394 self.add_result("S", tests.get_seq())
395 self.add_result("A", tests.get_ack())
396 self.add_result("F", tests.get_flags())
397 self.add_result("O", tests.get_options())
398 self.add_result("Q", tests.get_quirks())
400 def get_final_result(self):
401 return {self.test_id() : self.get_result_dict()}
404class nmap2_ecn_probe(nmap_tcp_probe):
405 # From nmap-4.22SOC8/osscan2.cc:
406 # [...]
407 # "\003\003\012\001\002\004\005\264\004\002\001\001"
408 # [...]
410 # From: https://nmap.org/book/osdetect-methods.html
411 # [...]
412 # This probe tests for explicit congestion notification (ECN) support
413 # in the target TCP stack. ECN is a method for improving Internet
414 # performance by allowing routers to signal congestion problems before
415 # they start having to drop packets. It is documented in RFC 3168.
416 # Nmap tests this by sending a SYN packet which also has the ECN CWR
417 # and ECE congestion control flags set. For an unrelated (to ECN) test,
418 # the urgent field value of 0xF7F5 is used even though the urgent flag
419 # is not set. The acknowledgment number is zero, sequence number is
420 # random, window size field is three, and the reserved bit which
421 # immediately precedes the CWR bit is set. TCP options are WScale (10),
422 # NOP, MSS (1460), SACK permitted, NOP, NOP. The probe is sent to an
423 # open port.
424 # [...]
425 tcp_options = [
426 TCPOption(TCPOption.TCPOPT_WINDOW, 0o12), #\003\003\012
427 TCPOption(TCPOption.TCPOPT_NOP), #\001
428 TCPOption(TCPOption.TCPOPT_MAXSEG, 1460), #\002\004\005\0264
429 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), #\004\002
430 TCPOption(TCPOption.TCPOPT_NOP), #\001
431 TCPOption(TCPOption.TCPOPT_NOP) #\001
432 ]
435 def __init__(self, id, addresses, tcp_ports):
436 nmap_tcp_probe.__init__(self, id, addresses, tcp_ports, 1,
437 0x8b6a, self.tcp_options)
438 self.t.set_SYN()
439 self.t.set_CWR()
440 self.t.set_ECE()
441 self.t.set_flags(0x800)
442 self.t.set_th_urp(0xF7F5)
443 self.t.set_th_ack(0)
444 self.t.set_th_win(3)
445 #self.t.set_th_flags(self.t.get_th_flags() | 0x0100) # 0000 0001 00000000
447 def test_id(self):
448 return "ECN"
450 def set_resp(self,resp):
451 if resp:
452 self.add_result("R", "Y")
453 else:
454 self.add_result("R", "N")
456 def process(self, packet):
457 ip = packet.child()
458 tcp = ip.child()
460 # R, DF, T*, TG*, W, O, CC, Q
461 self.set_resp(True)
463 tests = nmap2_tcp_tests(ip, tcp, 0, 0)
465 self.add_result("DF", tests.get_df())
466 self.add_result("W", tests.get_win())
467 self.add_result("O", tests.get_options())
468 self.add_result("CC", tests.get_cc())
469 self.add_result("Q", tests.get_quirks())
471 def get_final_result(self):
472 return {self.test_id() : self.get_result_dict()}
474class nmap2_tcp_tests:
475 def __init__(self, ip, tcp, sequence, acknowledgment):
476 self.__ip = ip
477 self.__tcp = tcp
478 self.__sequence = sequence
479 self.__acknowledgment = acknowledgment
481 def get_df(self):
482 # IP don't fragment bit (DF)
483 # The IP header contains a single bit which forbids routers from fragmenting
484 # a packet. If the packet is too large for routers to handle, they will just
485 # have to drop it (and ideally return a "destination unreachable,
486 # fragmentation needed" response). This test records Y if the bit is set,
487 # and N if it isn't.
488 if self.__ip.get_ip_df():
489 return "Y"
490 else:
491 return "N"
493 def get_win(self):
494 # TCP initial window size (W, W1-W6)
495 # This test simply records the 16-bit TCP window size of the received packet.
496 return "%X" % self.__tcp.get_th_win()
498 def get_ack(self):
499 # TCP acknowledgment number (A)
500 # This test is the same as S except that it tests how the acknowledgment
501 # number in the response compares to the sequence number in the
502 # respective probe.
503 # Value Description
504 # Z Acknowledgment number is zero.
505 # S Acknowledgment number is the same as the sequence number in the probe.
506 # S+ Acknowledgment number is the same as the sequence number in the probe plus one.
507 # O Acknowledgment number is something else (other).
508 if self.__tcp.get_th_ack() == self.__sequence + 1:
509 return "S+"
510 elif self.__tcp.get_th_ack() == self.__sequence:
511 return "S"
512 elif self.__tcp.get_th_ack() == 0:
513 return "Z"
514 else:
515 return "O"
517 def get_seq(self):
518 # TCP sequence number (S)
519 # This test examines the 32-bit sequence number field in the TCP
520 # header. Rather than record the field value as some other tests
521 # do, this one examines how it compares to the TCP acknowledgment
522 # number from the probe that elicited the response.
523 # Value Description
524 # Z Sequence number is zero.
525 # A Sequence number is the same as the acknowledgment number in the probe.
526 # A+ Sequence number is the same as the acknowledgment number in the probe plus one.
527 # O Sequence number is something else (other).
528 if self.__tcp.get_th_seq() == self.__acknowledgment + 1:
529 return "A+"
530 elif self.__tcp.get_th_seq() == self.__acknowledgment:
531 return "A"
532 elif self.__tcp.get_th_seq() == 0:
533 return "Z"
534 else:
535 return "O"
537 def get_flags(self):
538 # TCP flags (F)
539 # This field records the TCP flags in the response. Each letter represents
540 # one flag, and they occur in the same order as in a TCP packet (from
541 # high-bit on the left, to the low ones). So the value SA represents the
542 # SYN and ACK bits set, while the value AS is illegal (wrong order).
543 # The possible flags are shown in Table 8.7.
544 # Character Flag name Flag byte value
545 # E ECN Echo (ECE) 64
546 # U Urgent Data (URG) 32
547 # A Acknowledgment (ACK) 16
548 # P Push (PSH) 8
549 # R Reset (RST) 4
550 # S Synchronize (SYN) 2
551 # F Final (FIN) 1
553 flags = ""
555 if self.__tcp.get_ECE():
556 flags += "E"
557 if self.__tcp.get_URG():
558 flags += "U"
559 if self.__tcp.get_ACK():
560 flags += "A"
561 if self.__tcp.get_PSH():
562 flags += "P"
563 if self.__tcp.get_RST():
564 flags += "R"
565 if self.__tcp.get_SYN():
566 flags += "S"
567 if self.__tcp.get_FIN():
568 flags += "F"
570 return flags
572 def get_options(self):
573 # Option Name Character Argument (if any)
574 # End of Options List (EOL) L
575 # No operation (NOP) N
576 # Maximum Segment Size (MSS) M The value is appended. Many systems
577 # echo the value used in the corresponding probe.
578 # Window Scale (WS) W The actual value is appended.
579 # Timestamp (TS) T The T is followed by two binary characters
580 # representing the TSval and TSecr values respectively.
581 # The characters are 0 if the field is zero
582 # and 1 otherwise.
583 # Selective ACK permitted (SACK) S
585 options = ""
587 for op in self.__tcp.get_options():
588 if op.get_kind() == TCPOption.TCPOPT_EOL:
589 options += "L"
590 elif op.get_kind() == TCPOption.TCPOPT_MAXSEG:
591 options += "M%X" % (op.get_mss())
592 elif op.get_kind() == TCPOption.TCPOPT_NOP:
593 options += "N"
594 elif op.get_kind() == TCPOption.TCPOPT_TIMESTAMP:
595 options += "T%i%i" % (int(op.get_ts()!=0),
596 int(op.get_ts_echo()!=0))
597 elif op.get_kind() == TCPOption.TCPOPT_WINDOW:
598 options += "W%X" % (op.get_shift_cnt())
599 elif op.get_kind() == TCPOption.TCPOPT_SACK_PERMITTED:
600 options += "S"
602 return options
604 def get_cc(self):
605 # Explicit congestion notification (CC)
606 # This test is only used for the ECN probe. That probe is a SYN packet
607 # which includes the CWR and ECE congestion control flags. When the
608 # response SYN/ACK is received, those flags are examined to set the
609 # CC (congestion control) test value as described in Table 8.3.
611 # Table 8.3. CC test values
612 # Value Description
613 # Y Only the ECE bit is set (not CWR). This host supports ECN.
614 # N Neither of these two bits is set. The target does not support
615 # ECN.
616 # S Both bits are set. The target does not support ECN, but it
617 # echoes back what it thinks is a reserved bit.
618 # O The one remaining combination of these two bits (other).
619 ece, cwr = self.__tcp.get_ECE(), self.__tcp.get_CWR()
620 if ece and not cwr:
621 return "Y"
622 elif not ece and not cwr:
623 return "N"
624 elif ece and cwr:
625 return "S"
626 else:
627 return "O"
629 def get_quirks(self):
630 # TCP miscellaneous quirks (Q)
631 # This tests for two quirks that a few implementations have in their
632 # TCP stack. The first is that the reserved field in the TCP header
633 # (right after the header length) is nonzero. This is particularly
634 # likely to happen in response to the ECN test as that one sets a
635 # reserved bit in the probe. If this is seen in a packet, an "R"
636 # is recorded in the Q string.
638 # The other quirk Nmap tests for is a nonzero urgent pointer field
639 # value when the URG flag is not set. This is also particularly
640 # likely to be seen in response to the ECN probe, which sets a
641 # non-zero urgent field. A "U" is appended to the Q string when
642 # this is seen.
644 # The Q string must always be generated in alphabetical order.
645 # If no quirks are present, the Q test is empty but still shown.
647 quirks = ""
649 if ((self.__tcp.get_th_flags() >> 8) & 0x0f) != 0:
650 quirks += "R"
651 if self.__tcp.get_URG() == 0 and self.__tcp.get_th_urp() != 0:
652 quirks += "U"
654 return quirks
656class nmap2_tcp_probe_2_6(nmap2_tcp_probe):
657 sequence = 0x8453 # 0xBASE, obviously
658 mss = 265
660 # From nmap-4.22SOC8/osscan2.cc:
661 # [...]
662 # "\003\003\012\001\002\004\001\011\010\012\377\377\377\377\000\000\000\000\004\002"
663 # [...]
665 # From: https://nmap.org/book/osdetect-methods.html
666 # [...]
667 # The six T2 through T7 tests each send one TCP probe packet.
668 # With one exception, the TCP options data in each case is (in hex)
669 # 03030A0102040109080AFFFFFFFF000000000402.
670 # Those 20 bytes correspond to window scale (10), NOP, MSS (265),
671 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0), then SACK permitted.
672 # (...
673 tcp_options = [
674 TCPOption(TCPOption.TCPOPT_WINDOW, 0o12), #\003\003\012
675 TCPOption(TCPOption.TCPOPT_NOP), #\001
676 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011
677 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), #\010\012\377\377\377\377\000\000\000\000
678 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) #\004\002
679 ]
681 def __init__(self, id, addresses, tcp_ports, open_port):
682 nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
683 self.sequence, self.tcp_options)
685class nmap2_tcp_probe_7(nmap2_tcp_probe):
686 sequence = 0x8453 # 0xBASE, obviously
687 mss = 265
689 # ...)
690 # The exception is that T7 uses a Window scale value of 15 rather than 10
691 # [...]
692 tcp_options = [
693 TCPOption(TCPOption.TCPOPT_WINDOW, 0o17), #\003\003\017
694 TCPOption(TCPOption.TCPOPT_NOP), #\001
695 TCPOption(TCPOption.TCPOPT_MAXSEG, mss), #\002\004\001\011
696 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), #\010\012\377\377\377\377\000\000\000\000
697 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) #\004\002
698 ]
700 def __init__(self, id, addresses, tcp_ports, open_port):
701 nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, open_port,
702 self.sequence, self.tcp_options)
704class nmap_port_unreachable(udp_closed_probe):
706 def __init__(self, id, addresses, ports):
707 udp_closed_probe.__init__(self, id, addresses, ports[2])
708 self.set_resp(False)
710 def test_id(self):
711 pass
713 def set_resp(self, resp):
714 pass
716 def process(self, packet):
717 pass
719class nmap1_port_unreachable(nmap_port_unreachable):
721 def __init__(self, id, addresses, ports):
722 nmap_port_unreachable.__init__(self, id, addresses, ports)
723 self.u.contains(Data("A" * 300))
725 def test_id(self):
726 return "PU"
728 def set_resp(self,resp):
729 if resp:
730 self.add_result("Resp", "Y")
731 else:
732 self.add_result("Resp", "N")
734 def process(self, packet):
735 ip_orig = self.err_data
736 if ip_orig.get_ip_p() != UDP.protocol:
737 return
739 udp = ip_orig.child()
741 if not udp:
742 return
744 ip = packet.child()
746 self.set_resp(True)
748 if ip.get_ip_df():
749 self.add_result("DF", "Y")
750 else:
751 self.add_result("DF", "N")
753 self.add_result("TOS", ip.get_ip_tos())
755 self.add_result("IPLEN", ip.get_ip_len())
757 self.add_result("RIPTL", ip_orig.get_ip_len()) # Some systems return a different IPLEN
759 recv_ip_id = ip_orig.get_ip_id()
760 if 0 == recv_ip_id:
761 self.add_result("RID", "0")
762 elif udp_closed_probe.ip_id == recv_ip_id:
763 self.add_result("RID", "E")
764 else:
765 self.add_result("RID", "F")
767 ip_sum = ip_orig.get_ip_sum()
768 ip_orig.set_ip_sum(0)
769 checksum = ip_orig.compute_checksum(ip_orig.get_bytes())
771 if 0 == checksum:
772 self.add_result("RIPCK", "0")
773 elif checksum == ip_sum:
774 self.add_result("RIPCK", "E")
775 else:
776 self.add_result("RIPCK", "F")
778 udp_sum = udp.get_uh_sum()
779 udp.set_uh_sum(0)
780 udp.auto_checksum = 1
781 udp.calculate_checksum()
783 if 0 == udp_sum:
784 self.add_result("UCK", "0")
785 elif self.u.get_uh_sum() == udp_sum:
786 self.add_result("UCK", "E")
787 else:
788 self.add_result("UCK", "F")
790 self.add_result("ULEN", udp.get_uh_ulen())
792 if ip.child().child().child().child() == udp.child(): # Some systems meddle with the data
793 self.add_result("DAT", "E")
794 else:
795 self.add_result("DAT", "F")
797 def get_final_result(self):
798 return {self.test_id(): self.get_result_dict()}
800class nmap2_port_unreachable(nmap_port_unreachable):
801 # UDP (U1)
802 # This probe is a UDP packet sent to a closed port. The character 'C'
803 # (0x43) is repeated 300 times for the data field. The IP ID value is
804 # set to 0x1042 for operating systems which allow us to set this. If
805 # the port is truly closed and there is no firewall in place, Nmap
806 # expects to receive an ICMP port unreachable message in return.
807 # That response is then subjected to the R, DF, T, TG, TOS, IPL, UN,
808 # RIPL, RID, RIPCK, RUCK, RUL, and RUD tests.
809 def __init__(self, id, addresses, ports):
810 nmap_port_unreachable.__init__(self, id, addresses, ports)
811 self.u.contains(Data("C" * 300))
812 self.i.set_ip_id(0x1042)
814 def test_id(self):
815 return "U1"
817 def set_resp(self,resp):
818 if resp:
819 self.add_result("R", "Y")
820 else:
821 self.add_result("R", "N")
823 def process(self, packet):
824 ip_orig = self.err_data
825 if ip_orig.get_ip_p() != UDP.protocol:
826 return
828 udp = ip_orig.child()
830 if not udp:
831 return
833 ip = packet.child()
835 icmp = ip.child()
837 if ip.get_ip_df():
838 self.add_result("DF", "Y")
839 else:
840 self.add_result("DF", "N")
842 # XXX T
843 # IP initial time-to-live (T)
844 # IP packets contain a field named time-to-live (TTL) which is
845 # decremented every time they traverse a router. If the field
846 # reaches zero, the packet must be discarded. This prevents
847 # packets from looping endlessly. Because operating systems differ
848 # on which TTL they start with, it can be used for OS detection.
849 # Nmap determines how many hops away it is from the target by
850 # examining the ICMP port unreachable response to the U1 probe.
851 # That response includes the original IP packet, including the
852 # already-decremented TTL field, received by the target. By
853 # subtracting that value from our as-sent TTL, we learn how many
854 # hops away the machine is. Nmap then adds that hop distance to
855 # the probe response TTL to determine what the initial TTL was
856 # when that ICMP probe response packet was sent. That initial TTL
857 # value is stored in the fingerprint as the T result.
858 # Even though an eight-bit field like TTL can never hold values
859 # greater than 0xFF, this test occasionally results in values of
860 # 0x100 or higher. This occurs when a system (could be the source,
861 # a target, or a system in between) corrupts or otherwise fails to
862 # correctly decrement the TTL. It can also occur due to asymmetric
863 # routes.
865 # XXX TG
866 # IP initial time-to-live guess (TG)
867 # It is not uncommon for Nmap to receive no response to the U1 probe,
868 # which prevents Nmap from learning how many hops away a target is.
869 # Firewalls and NAT devices love to block unsolicited UDP packets.
870 # But since common TTL values are spread well apart and targets are
871 # rarely more than 20 hops away, Nmap can make a pretty good guess
872 # anyway. Most systems send packets with an initial TTL of 32, 60, 64,
873 # 128, or 255. So the TTL value received in the response is rounded
874 # up to the next value out of 32, 64, 128, or 255. 60 is not in that
875 # list because it cannot be reliably distinguished from 64. It is
876 # rarely seen anyway.
877 # The resulting guess is stored in the TG field. This TTL guess field
878 # is not printed in a subject fingerprint if the actual TTL (T) value
879 # was discovered.
881 # IP type of service (TOS)
882 # This test simply records the type of service byte from the
883 # IP header of ICMP port unreachable packets.
884 # This byte is described in RFC 791
885 self.add_result("TOS", "%X" % ip.get_ip_tos())
887 # IP total length (IPL)
888 # This test records the total length (in octets) of an IP packet.
889 # It is only used for the port unreachable response elicited by the
890 # U1 test.
891 self.add_result("IPL", "%X" % ip.get_ip_len())
893 # Unused port unreachable field nonzero (UN)
894 # An ICMP port unreachable message header is eight bytes long, but
895 # only the first four are used. RFC 792 states that the last four
896 # bytes must be zero. A few implementations (mostly ethernet switches
897 # and some specialized embedded devices) set it anyway. The value of
898 # those last four bytes is recorded in this field.
899 self.add_result("UN", "%X" % icmp.get_icmp_void())
901 # Returned probe IP total length value (RIPL)
902 # ICMP port unreachable messages (as are sent in response to the U1
903 # probe) are required to include the IP header which generated them.
904 # This header should be returned just as they received it, but some
905 # implementations send back a corrupted version due to changes they
906 # made during IP processing. This test simply records the returned
907 # IP total length value. If the correct value of 0x148 (328) is
908 # returned, the value G (for good) is stored instead of the actual value.
909 if ip_orig.get_ip_len() == 0x148:
910 self.add_result("RIPL","G")
911 else:
912 self.add_result("RIPL", "%X" % ip_orig.get_ip_len())
914 # Returned probe IP ID value (RID)
915 # The U1 probe has a static IP ID value of 0x1042. If that value is
916 # returned in the port unreachable message, the value G is stored for
917 # this test. Otherwise the exact value returned is stored. Some systems,
918 # such as Solaris, manipulate IP ID values for raw IP packets that
919 # Nmap sends. In such cases, this test is skipped. We have found
920 # that some systems, particularly HP and Xerox printers, flip the bytes
921 # and return 0x4210 instead.
922 if 0x1042 == ip_orig.get_ip_id():
923 self.add_result("RID", "G")
924 else:
925 self.add_result("RID", "%X" % ip_orig.get_ip_id())
927 # Integrity of returned probe IP checksum value (RIPCK)
928 # The IP checksum is one value that we don't expect to remain the same
929 # when returned in a port unreachable message. After all, each network
930 # hop during transit changes the checksum as the TTL is decremented.
931 # However, the checksum we receive should match the enclosing IP packet.
932 # If it does, the value G (good) is stored for this test. If the returned
933 # value is zero, then Z is stored. Otherwise the result is I (invalid).
934 ip_sum = ip_orig.get_ip_sum()
935 ip_orig.set_ip_sum(0)
936 checksum = ip_orig.compute_checksum(ip_orig.get_bytes())
938 if 0 == checksum:
939 self.add_result("RIPCK", "Z")
940 elif checksum == ip_sum:
941 self.add_result("RIPCK", "G")
942 else:
943 self.add_result("RIPCK", "I")
945 # Integrity of returned probe UDP length and checksum (RUL and RUCK)
946 # The UDP header length and checksum values should be returned exactly
947 # as they were sent. If so, G is recorded for these tests. Otherwise
948 # the value actually returned is recorded. The proper length is 0x134 (308).
949 udp_sum = udp.get_uh_sum()
950 udp.set_uh_sum(0)
951 udp.auto_checksum = 1
952 udp.calculate_checksum()
954 if self.u.get_uh_sum() == udp_sum:
955 self.add_result("RUCK", "G")
956 else:
957 self.add_result("RUCK", "%X" % udp_sum)
959 if udp.get_uh_ulen() == 0x134:
960 self.add_result("RUL","G")
961 else:
962 self.add_result("RUL", "%X" % udp.get_uh_ulen())
964 # Integrity of returned UDP data (RUD)
965 # If the UDP payload returned consists of 300 'C' (0x43)
966 # characters as expected, a G is recorded for this test.
967 # Otherwise I (invalid) is recorded.
968 if ip.child().child().child().child() == udp.child():
969 self.add_result("RUD", "G")
970 else:
971 self.add_result("RUD", "I")
973 def get_final_result(self):
974 return {self.test_id(): self.get_result_dict()}
976class OS_ID:
978 def __init__(self, target, ports):
979 pcap_dev = lookupdev()
980 self.p = open_live(pcap_dev, 600, 0, 3000)
982 self.__source = self.p.getlocalip()
983 self.__target = target
985 self.p.setfilter("src host %s and dst host %s" % (target, self.__source), 1, 0xFFFFFF00)
986 self.p.setmintocopy(10)
987 self.decoder = EthDecoder()
989 self.tests_sent = []
990 self.outstanding_count = 0
991 self.results = {}
992 self.current_id = 12345
994 self.__ports = ports
996 def releasePcap(self):
997 if not (self.p is None):
998 self.p.close()
1000 def get_new_id(self):
1001 id = self.current_id
1002 self.current_id += 1
1003 self.current_id &= 0xFFFF
1004 return id
1006 def send_tests(self, tests):
1007 self.outstanding_count = 0
1009 for t_class in tests:
1011 # Ok, I need to know if the constructor accepts the parameter port
1012 # We could ask also by co_varnames, but the port parameters is not a standarized... asking by args count :(
1013 if t_class.__init__.im_func.func_code.co_argcount == 4:
1014 test = t_class(self.get_new_id(), [self.__source, self.__target], self.__ports )
1015 else:
1016 test = t_class(self.get_new_id(), [self.__source, self.__target] )
1018 self.p.sendpacket(test.get_test_packet())
1019 self.outstanding_count += 1
1020 self.tests_sent.append(test)
1021 while self.p.readready():
1022 self.p.dispatch(1, self.packet_handler)
1024 while self.outstanding_count > 0:
1025 data = self.p.next()[0]
1026 if data:
1027 self.packet_handler(0, data)
1028 else:
1029 break
1031 def run(self):
1032 pass
1034 def get_source(self):
1035 return self.__source
1037 def get_target(self):
1038 return self.__target
1040 def get_ports(self):
1041 return self.__ports
1043 def packet_handler(self, len, data):
1044 packet = self.decoder.decode(data)
1046 for t in self.tests_sent:
1047 if t.is_mine(packet):
1048 t.process(packet)
1049 self.outstanding_count -= 1
1052class nmap1_tcp_open_1(nmap1_tcp_probe):
1053 def __init__(self, id, addresses, tcp_ports):
1054 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1055 self.t.set_ECE()
1056 self.t.set_SYN()
1058 def test_id(self):
1059 return "T1"
1061 def is_mine(self, packet):
1062 if tcp_probe.is_mine(self, packet):
1063 ip = packet.child()
1064 if not ip:
1065 return 0
1066 tcp = ip.child()
1067 if not tcp:
1068 return 0
1069 if tcp.get_SYN() and tcp.get_ACK():
1070 return 1
1071 else:
1072 return 0
1073 else:
1074 return 0
1077class nmap1_tcp_open_2(nmap1_tcp_probe):
1078 def __init__(self, id, addresses, tcp_ports):
1079 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1081 def test_id(self):
1082 return "T2"
1084class nmap2_tcp_open_2(nmap2_tcp_probe_2_6):
1085 # From: https://nmap.org/book/osdetect-methods.html
1086 # [...]
1087 # T2 sends a TCP null (no flags set) packet with the IP DF bit set and a
1088 # window field of 128 to an open port.
1089 # ...
1090 def __init__(self, id, addresses, tcp_ports):
1091 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1)
1092 self.i.set_ip_df(1)
1093 self.t.set_th_win(128)
1095 def test_id(self):
1096 return "T2"
1098class nmap1_tcp_open_3(nmap1_tcp_probe):
1099 def __init__(self, id, addresses, tcp_ports ):
1100 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1101 self.t.set_SYN()
1102 self.t.set_FIN()
1103 self.t.set_URG()
1104 self.t.set_PSH()
1106 def test_id(self):
1107 return "T3"
1109class nmap2_tcp_open_3(nmap2_tcp_probe_2_6):
1110 # ...
1111 # T3 sends a TCP packet with the SYN, FIN, URG, and PSH flags set and a
1112 # window field of 256 to an open port. The IP DF bit is not set.
1113 # ...
1114 def __init__(self, id, addresses, tcp_ports ):
1115 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1)
1116 self.t.set_SYN()
1117 self.t.set_FIN()
1118 self.t.set_URG()
1119 self.t.set_PSH()
1120 self.t.set_th_win(256)
1121 self.i.set_ip_df(0)
1123 def test_id(self):
1124 return "T3"
1126class nmap1_tcp_open_4(nmap1_tcp_probe):
1127 def __init__(self, id, addresses, tcp_ports):
1128 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1129 self.t.set_ACK()
1131 def test_id(self):
1132 return "T4"
1134class nmap2_tcp_open_4(nmap2_tcp_probe_2_6):
1135 # ...
1136 # T4 sends a TCP ACK packet with IP DF and a window field of 1024 to
1137 # an open port.
1138 # ...
1139 def __init__(self, id, addresses, tcp_ports ):
1140 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1)
1141 self.t.set_ACK()
1142 self.i.set_ip_df(1)
1143 self.t.set_th_win(1024)
1145 def test_id(self):
1146 return "T4"
1149class nmap1_seq(nmap1_tcp_probe):
1150 SEQ_UNKNOWN = 0
1151 SEQ_64K = 1
1152 SEQ_TD = 2
1153 SEQ_RI = 4
1154 SEQ_TR = 8
1155 SEQ_i800 = 16
1156 SEQ_CONSTANT = 32
1158 TS_SEQ_UNKNOWN = 0
1159 TS_SEQ_ZERO = 1 # At least one of the timestamps we received back was 0
1160 TS_SEQ_2HZ = 2
1161 TS_SEQ_100HZ = 3
1162 TS_SEQ_1000HZ = 4
1163 TS_SEQ_UNSUPPORTED = 5 # System didn't send back a timestamp
1165 IPID_SEQ_UNKNOWN = 0
1166 IPID_SEQ_INCR = 1 # simple increment by one each time
1167 IPID_SEQ_BROKEN_INCR = 2 # Stupid MS -- forgot htons() so it counts by 256 on little-endian platforms
1168 IPID_SEQ_RPI = 3 # Goes up each time but by a "random" positive increment
1169 IPID_SEQ_RD = 4 # Appears to select IPID using a "random" distributions (meaning it can go up or down)
1170 IPID_SEQ_CONSTANT = 5 # Contains 1 or more sequential duplicates
1171 IPID_SEQ_ZERO = 6 # Every packet that comes back has an IP.ID of 0 (eg Linux 2.4 does this)
1173 def __init__(self, id, addresses, tcp_ports):
1174 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1)
1175 self.t.set_SYN()
1176 self.t.set_th_seq(id) # Used to match results with sent packets.
1178 def process(self, p):
1179 raise Exception("Method process is meaningless for class %s." % self.__class__.__name__)
1182class nmap2_seq(nmap2_tcp_probe):
1183 TS_SEQ_UNKNOWN = 0
1184 TS_SEQ_ZERO = 1 # At least one of the timestamps we received back was 0
1185 TS_SEQ_UNSUPPORTED = 5 # System didn't send back a timestamp
1187 IPID_SEQ_UNKNOWN = 0
1188 IPID_SEQ_INCR = 1 # simple increment by one each time
1189 IPID_SEQ_BROKEN_INCR = 2 # Stupid MS -- forgot htons() so it counts by 256 on little-endian platforms
1190 IPID_SEQ_RPI = 3 # Goes up each time but by a "random" positive increment
1191 IPID_SEQ_RD = 4 # Appears to select IPID using a "random" distributions (meaning it can go up or down)
1192 IPID_SEQ_CONSTANT = 5 # Contains 1 or more sequential duplicates
1193 IPID_SEQ_ZERO = 6 # Every packet that comes back has an IP.ID of 0 (eg Linux 2.4 does this)
1195 def __init__(self, id, addresses, tcp_ports, options):
1196 nmap2_tcp_probe.__init__(self, id, addresses, tcp_ports, 1,
1197 id, options)
1198 self.t.set_SYN()
1200 def process(self, p):
1201 raise Exception("Method process is meaningless for class %s." % self.__class__.__name__)
1203class nmap2_seq_1(nmap2_seq):
1204 # Packet #1: window scale (10),
1205 # NOP,
1206 # MSS (1460),
1207 # timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1208 # SACK permitted.
1209 # The window field is 1.
1210 tcp_options = [
1211 TCPOption(TCPOption.TCPOPT_WINDOW, 10),
1212 TCPOption(TCPOption.TCPOPT_NOP),
1213 TCPOption(TCPOption.TCPOPT_MAXSEG, 1460),
1214 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1215 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED)
1216 ]
1218 def __init__(self, id, addresses, tcp_ports):
1219 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1220 self.t.set_th_win(1)
1222class nmap2_seq_2(nmap2_seq):
1223 # Packet #2: MSS (1400),
1224 # window scale (0),
1225 # SACK permitted,
1226 # timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1227 # EOL.
1228 # The window field is 63.
1229 tcp_options = [
1230 TCPOption(TCPOption.TCPOPT_MAXSEG, 1400),
1231 TCPOption(TCPOption.TCPOPT_WINDOW, 0),
1232 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1233 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1234 TCPOption(TCPOption.TCPOPT_EOL)
1235 ]
1237 def __init__(self, id, addresses, tcp_ports):
1238 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1239 self.t.set_th_win(63)
1241class nmap2_seq_3(nmap2_seq):
1242 # Packet #3: Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1243 # NOP,
1244 # NOP,
1245 # window scale (5),
1246 # NOP,
1247 # MSS (640).
1248 # The window field is 4.
1249 tcp_options = [
1250 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1251 TCPOption(TCPOption.TCPOPT_NOP),
1252 TCPOption(TCPOption.TCPOPT_NOP),
1253 TCPOption(TCPOption.TCPOPT_WINDOW, 5),
1254 TCPOption(TCPOption.TCPOPT_NOP),
1255 TCPOption(TCPOption.TCPOPT_MAXSEG, 640)
1256 ]
1258 def __init__(self, id, addresses, tcp_ports):
1259 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1260 self.t.set_th_win(4)
1262class nmap2_seq_4(nmap2_seq):
1263 # Packet #4: SACK permitted,
1264 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1265 # window scale (10),
1266 # EOL.
1267 # The window field is 4.
1268 tcp_options = [
1269 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1270 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1271 TCPOption(TCPOption.TCPOPT_WINDOW, 10),
1272 TCPOption(TCPOption.TCPOPT_EOL)
1273 ]
1275 def __init__(self, id, addresses, tcp_ports):
1276 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1277 self.t.set_th_win(4)
1279class nmap2_seq_5(nmap2_seq):
1280 # Packet #5: MSS (536),
1281 # SACK permitted,
1282 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),
1283 # window scale (10),
1284 # EOL.
1285 # The window field is 16.
1286 tcp_options = [
1287 TCPOption(TCPOption.TCPOPT_MAXSEG, 536),
1288 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1289 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF),
1290 TCPOption(TCPOption.TCPOPT_WINDOW, 10),
1291 TCPOption(TCPOption.TCPOPT_EOL)
1292 ]
1294 def __init__(self, id, addresses, tcp_ports):
1295 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1296 self.t.set_th_win(16)
1298class nmap2_seq_6(nmap2_seq):
1299 # Packet #6: MSS (265),
1300 # SACK permitted,
1301 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0).
1302 # The window field is 512.
1303 tcp_options = [
1304 TCPOption(TCPOption.TCPOPT_MAXSEG, 265),
1305 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED),
1306 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF)
1307 ]
1309 def __init__(self, id, addresses, tcp_ports):
1310 nmap2_seq.__init__(self, id, addresses, tcp_ports, self.tcp_options)
1311 self.t.set_th_win(512)
1313class nmap1_seq_container(os_id_test):
1314 def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs):
1315 os_id_test.__init__(self, 0)
1317 self.num_seq_samples = num_seq_samples
1318 self.seq_responses = responses
1319 self.seq_num_responses = len(responses)
1320 self.seq_diffs = seq_diffs
1321 self.ts_diffs = ts_diffs
1322 self.time_diffs = time_diffs
1323 self.pre_ts_seqclass = nmap1_seq.TS_SEQ_UNKNOWN
1325 def test_id(self):
1326 return "TSEQ"
1328 def set_ts_seqclass(self, ts_seqclass):
1329 self.pre_ts_seqclass = ts_seqclass
1331 def process(self):
1332 ipid_seqclass = self.ipid_sequence()
1333 if nmap1_seq.TS_SEQ_UNKNOWN != self.pre_ts_seqclass:
1334 ts_seqclass = self.pre_ts_seqclass
1335 else:
1336 ts_seqclass = self.ts_sequence()
1338 if self.seq_num_responses >= 4:
1339 seq_seqclass = self.seq_sequence()
1340 if nmap1_seq.SEQ_UNKNOWN != seq_seqclass:
1341 self.add_seqclass(seq_seqclass)
1342 if nmap1_seq.IPID_SEQ_UNKNOWN != ipid_seqclass:
1343 self.add_ipidclass(ipid_seqclass)
1344 if nmap1_seq.TS_SEQ_UNKNOWN != ts_seqclass:
1345 self.add_tsclass(ts_seqclass)
1346 else:
1347 LOG.error("Insufficient responses for TCP sequencing (%d out of %d), OS detection may be less accurate."
1348 % (self.seq_num_responses, self.num_seq_samples))
1350 def get_final_result(self):
1351 "Returns a string representation of the final result of this test or None if no response was received"
1352 return {self.test_id(): self.get_result_dict()}
1354 def ipid_sequence(self):
1355 if self.seq_num_responses < 2:
1356 return nmap1_seq.IPID_SEQ_UNKNOWN
1358 ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1))
1360 null_ipids = 1
1361 for i in xrange(1, self.seq_num_responses):
1362 prev_ipid = self.seq_responses[i-1].get_ipid()
1363 cur_ipid = self.seq_responses[i].get_ipid()
1365 if cur_ipid < prev_ipid and (cur_ipid > 500 or prev_ipid < 65000):
1366 return nmap1_seq.IPID_SEQ_RD
1368 if prev_ipid != 0 or cur_ipid != 0:
1369 null_ipids = 0
1370 ipid_diffs[i-1] = abs(cur_ipid - prev_ipid)
1372 if null_ipids:
1373 return nmap1_seq.IPID_SEQ_ZERO
1375 # Battle plan:
1376 # If any diff is > 1000, set to random, if 0, set to constant.
1377 # If any of the diffs are 1, or all are less than 9, set to incremental.
1379 for i in xrange(0, self.seq_num_responses - 1):
1380 if ipid_diffs[i] > 1000:
1381 return nmap1_seq.IPID_SEQ_RPI
1382 if ipid_diffs[i] == 0:
1383 return nmap1_seq.IPID_SEQ_CONSTANT
1385 is_incremental = 1 # All diferences are less than 9
1386 is_ms = 1 # All diferences are multiples of 256
1387 for i in xrange(0, self.seq_num_responses - 1):
1388 if ipid_diffs[i] == 1:
1389 return nmap1_seq.IPID_SEQ_INCR
1390 if is_ms and ipid_diffs[i] < 2560 and (ipid_diffs[i] % 256) != 0:
1391 is_ms = 0
1392 if ipid_diffs[i] > 9:
1393 is_incremental = 0
1395 if is_ms:
1396 return nmap1_seq.IPID_SEQ_BROKEN_INCR
1397 if is_incremental:
1398 return nmap1_seq.IPID_SEQ_INCR
1400 return nmap1_seq.IPID_SEQ_UNKNOWN
1402 def ts_sequence(self):
1403 if self.seq_num_responses < 2:
1404 return nmap1_seq.TS_SEQ_UNKNOWN
1406 # Battle plan:
1407 # 1) Compute average increments per second, and variance in incr. per second.
1408 # 2) If any are 0, set to constant.
1409 # 3) If variance is high, set to random incr. [ skip for now ]
1410 # 4) if ~10/second, set to appropriate thing.
1411 # 5) Same with ~100/s.
1413 avg_freq = 0.0
1414 for i in xrange(0, self.seq_num_responses - 1):
1415 dhz = self.ts_diffs[i] / self.time_diffs[i]
1416 avg_freq += dhz / (self.seq_num_responses - 1)
1418 LOG.info("The avg TCP TS HZ is: %f" % avg_freq)
1420 if 0 < avg_freq < 3.9:
1421 return nmap1_seq.TS_SEQ_2HZ
1422 if 85 < avg_freq < 115:
1423 return nmap1_seq.TS_SEQ_100HZ
1424 if 900 < avg_freq < 1100:
1425 return nmap1_seq.TS_SEQ_1000HZ
1427 return nmap1_seq.TS_SEQ_UNKNOWN
1429 def seq_sequence(self):
1430 self.seq_gcd = reduce(my_gcd, self.seq_diffs)
1431 avg_incr = 0
1432 seqclass = nmap1_seq.SEQ_UNKNOWN
1434 if 0 != self.seq_gcd:
1435 map(lambda x, gcd = self.seq_gcd: x / gcd, self.seq_diffs)
1436 for i in xrange(0, self.seq_num_responses - 1):
1437 if abs(self.seq_responses[i+1].get_seq() - self.seq_responses[i].get_seq()) > 50000000:
1438 seqclass = nmap1_seq.SEQ_TR;
1439 self.index = 9999999
1440 break
1441 avg_incr += self.seq_diffs[i]
1443 if 0 == self.seq_gcd:
1444 seqclass = nmap1_seq.SEQ_CONSTANT
1445 self.index = 0
1446 elif 0 == self.seq_gcd % 64000:
1447 seqclass = nmap1_seq.SEQ_64K
1448 self.index = 1
1449 elif 0 == self.seq_gcd % 800:
1450 seqclass = nmap1_seq.SEQ_i800
1451 self.index = 10
1452 elif nmap1_seq.SEQ_UNKNOWN == seqclass:
1453 avg_incr = int(.5 + avg_incr / (self.seq_num_responses - 1))
1454 sum_incr = 0.0
1455 for i in range(0, self.seq_num_responses - 1):
1456 d = abs(self.seq_diffs[i] - avg_incr)
1457 sum_incr += float(d * d)
1458 sum_incr /= self.seq_num_responses - 1
1459 self.index = int(.5 + math.sqrt(sum_incr))
1460 if self.index < 75:
1461 seqclass = nmap1_seq.SEQ_TD
1462 else:
1463 seqclass = nmap1_seq.SEQ_RI
1465 return seqclass
1467 seqclasses = {
1468 nmap1_seq.SEQ_64K: '64K',
1469 nmap1_seq.SEQ_TD: 'TD',
1470 nmap1_seq.SEQ_RI: 'RI',
1471 nmap1_seq.SEQ_TR: 'TR',
1472 nmap1_seq.SEQ_i800: 'i800',
1473 nmap1_seq.SEQ_CONSTANT: 'C',
1474 }
1476 def add_seqclass(self, id):
1477 self.add_result('CLASS', nmap1_seq_container.seqclasses[id])
1479 if nmap1_seq.SEQ_CONSTANT == id:
1480 self.add_result('VAL', '%i' % self.seq_responses[0].get_seq())
1481 elif id in (nmap1_seq.SEQ_TD, nmap1_seq.SEQ_RI):
1482 self.add_result('GCD', '%i' % self.seq_gcd)
1483 self.add_result('SI', '%i' % self.index)
1485 tsclasses = {
1486 nmap1_seq.TS_SEQ_ZERO: '0',
1487 nmap1_seq.TS_SEQ_2HZ: '2HZ',
1488 nmap1_seq.TS_SEQ_100HZ: '100HZ',
1489 nmap1_seq.TS_SEQ_1000HZ: '1000HZ',
1490 nmap1_seq.TS_SEQ_UNSUPPORTED: 'U',
1491 }
1493 def add_tsclass(self, id):
1494 self.add_result('TS', nmap1_seq_container.tsclasses[id])
1496 ipidclasses = {
1497 nmap1_seq.IPID_SEQ_INCR: 'I',
1498 nmap1_seq.IPID_SEQ_BROKEN_INCR: 'BI',
1499 nmap1_seq.IPID_SEQ_RPI: 'RPI',
1500 nmap1_seq.IPID_SEQ_RD: 'RD',
1501 nmap1_seq.IPID_SEQ_CONSTANT: 'C',
1502 nmap1_seq.IPID_SEQ_ZERO: 'Z',
1503 }
1505 def add_ipidclass(self, id):
1506 self.add_result('IPID', nmap1_seq_container.ipidclasses[id])
1509class nmap2_seq_container(os_id_test):
1510 def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs):
1511 os_id_test.__init__(self, 0)
1513 self.num_seq_samples = num_seq_samples
1514 self.seq_responses = responses
1515 self.seq_num_responses = len(responses)
1516 self.seq_diffs = seq_diffs
1517 self.ts_diffs = ts_diffs
1518 self.time_diffs = time_diffs
1519 self.pre_ts_seqclass = nmap2_seq.TS_SEQ_UNKNOWN
1521 def test_id(self):
1522 return "SEQ"
1524 def set_ts_seqclass(self, ts_seqclass):
1525 self.pre_ts_seqclass = ts_seqclass
1527 def process(self):
1528 if self.seq_num_responses >= 4:
1529 self.calc_ti()
1530 self.calc_ts()
1531 self.calc_sp()
1532 else:
1533 self.add_result('R', 'N')
1534 LOG.error("Insufficient responses for TCP sequencing (%d out of %d), OS detection may be less accurate."
1535 % (self.seq_num_responses, self.num_seq_samples))
1537 def get_final_result(self):
1538 return {self.test_id(): self.get_result_dict()}
1540 def calc_ti(self):
1541 if self.seq_num_responses < 2:
1542 return
1544 ipidclasses = {
1545 nmap2_seq.IPID_SEQ_INCR: 'I',
1546 nmap2_seq.IPID_SEQ_BROKEN_INCR: 'BI',
1547 nmap2_seq.IPID_SEQ_RPI: 'RI',
1548 nmap2_seq.IPID_SEQ_RD: 'RD',
1549 nmap2_seq.IPID_SEQ_CONSTANT: 'C',
1550 nmap2_seq.IPID_SEQ_ZERO: 'Z',
1551 }
1553 ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1))
1555 # Random and zero
1556 null_ipids = 1
1557 for i in xrange(1, self.seq_num_responses):
1558 prev_ipid = self.seq_responses[i-1].get_ipid()
1559 cur_ipid = self.seq_responses[i].get_ipid()
1561 if prev_ipid != 0 or cur_ipid != 0:
1562 null_ipids = 0
1564 if prev_ipid <= cur_ipid:
1565 ipid_diffs[i-1] = cur_ipid - prev_ipid
1566 else:
1567 ipid_diffs[i-1] = (cur_ipid - prev_ipid + 65536) & 0xffff
1569 if self.seq_num_responses > 2 and ipid_diffs[i-1] > 20000:
1570 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_RD])
1571 return
1573 if null_ipids:
1574 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_ZERO])
1575 return
1577 # Constant
1578 all_zero = 1
1579 for i in xrange(0, self.seq_num_responses - 1):
1580 if ipid_diffs[i] != 0:
1581 all_zero = 0
1582 break
1584 if all_zero:
1585 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_CONSTANT])
1586 return
1588 # Random positive increments
1589 for i in xrange(0, self.seq_num_responses - 1):
1590 if ipid_diffs[i] > 1000 and \
1591 ((ipid_diffs[i] % 256 != 0) or \
1592 ((ipid_diffs[i] % 256 == 0) and (ipid_diffs[i] >= 25600))):
1593 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_RPI])
1594 return
1596 # Broken Increment and Incremental
1597 is_incremental = 1 # All diferences are less than 10
1598 is_ms = 1 # All diferences are multiples of 256 and no greater than 5120
1599 for i in xrange(0, self.seq_num_responses - 1):
1600 if is_ms and ((ipid_diffs[i] > 5120) or (ipid_diffs[i] % 256) != 0):
1601 is_ms = 0
1602 if is_incremental and ipid_diffs[i] > 9:
1603 is_incremental = 0
1605 if is_ms:
1606 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_BROKEN_INCR])
1607 elif is_incremental:
1608 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_INCR])
1610 def calc_ts(self):
1611 # 1. If any of the responses have no timestamp option, TS
1612 # is set to U (unsupported).
1613 # 2. If any of the timestamp values are zero, TS is set to 0.
1614 # 3. If the average increments per second falls within the
1615 # ranges 0-5.66, 70-150, or 150-350, TS is set to 1, 7, or 8,
1616 # respectively. These three ranges get special treatment
1617 # because they correspond to the 2 Hz, 100 Hz, and 200 Hz
1618 # frequencies used by many hosts.
1619 # 4. In all other cases, Nmap records the binary logarithm of
1620 # the average increments per second, rounded to the nearest
1621 # integer. Since most hosts use 1,000 Hz frequencies, A is
1622 # a common result.
1624 if self.pre_ts_seqclass == nmap2_seq.TS_SEQ_ZERO:
1625 self.add_result('TS', '0')
1626 elif self.pre_ts_seqclass == nmap2_seq.TS_SEQ_UNSUPPORTED:
1627 self.add_result('TS', 'U')
1628 elif self.seq_num_responses < 2:
1629 return
1631 avg_freq = 0.0
1632 for i in xrange(0, self.seq_num_responses - 1):
1633 dhz = self.ts_diffs[i] / self.time_diffs[i]
1634 avg_freq += dhz / (self.seq_num_responses - 1)
1636 LOG.info("The avg TCP TS HZ is: %f" % avg_freq)
1638 if avg_freq <= 5.66:
1639 self.add_result('TS', "1")
1640 elif 70 < avg_freq <= 150:
1641 self.add_result('TS', "7")
1642 elif 150 < avg_freq <= 350:
1643 self.add_result('TS', "8")
1644 else:
1645 ts = int(round(.5 + math.log(avg_freq)/math.log(2)))
1646 self.add_result('TS', "%X" % ts)
1648 def calc_sp(self):
1649 seq_gcd = reduce(my_gcd, self.seq_diffs)
1651 seq_avg_rate = 0.0
1652 for i in xrange(0, self.seq_num_responses - 1):
1653 seq_avg_rate += self.seq_diffs[i] / self.time_diffs[i]
1654 seq_avg_rate /= (self.seq_num_responses - 1)
1656 seq_rate = seq_avg_rate
1657 si_index = 0
1658 seq_stddev = 0
1660 if 0 == seq_gcd:
1661 seq_rate = 0
1662 else:
1663 seq_rate = int(round(.5 + (math.log(seq_rate) / math.log(2)) * 8))
1665 div_gcd = 1
1666 if seq_gcd > 9:
1667 div_gcd = seq_gcd
1669 for i in xrange(0, self.seq_num_responses - 1):
1670 rtmp = (self.seq_diffs[i] / self.time_diffs[i]) / div_gcd - \
1671 seq_avg_rate / div_gcd
1672 seq_stddev += rtmp * rtmp
1674 seq_stddev /= self.seq_num_responses - 2
1675 seq_stddev = math.sqrt(seq_stddev)
1677 if seq_stddev <= 1:
1678 si_index = 0
1679 else:
1680 si_index = int(round(.5 + (math.log(seq_stddev) / math.log(2)) * 8.0))
1682 self.add_result('SP', "%X" % si_index)
1683 self.add_result('GCD', "%X" % seq_gcd)
1684 self.add_result('ISR', "%X" % seq_rate)
1686class nmap2_ops_container(os_id_test):
1687 def __init__(self, responses):
1688 os_id_test.__init__(self, 0)
1690 self.seq_responses = responses
1691 self.seq_num_responses = len(responses)
1693 def test_id(self):
1694 return "OPS"
1696 def process(self):
1697 if self.seq_num_responses != 6:
1698 self.add_result('R', 'N')
1699 return
1701 for i in xrange(0, self.seq_num_responses):
1702 tests = nmap2_tcp_tests(self.seq_responses[i].get_ip(),
1703 self.seq_responses[i].get_tcp(),
1704 0,
1705 0)
1706 self.add_result("O%i" % (i+1), tests.get_options())
1708 def get_final_result(self):
1709 if not self.get_result_dict():
1710 return None
1711 else:
1712 return {self.test_id(): self.get_result_dict()}
1714class nmap2_win_container(os_id_test):
1715 def __init__(self, responses):
1716 os_id_test.__init__(self, 0)
1718 self.seq_responses = responses
1719 self.seq_num_responses = len(responses)
1721 def test_id(self):
1722 return "WIN"
1724 def process(self):
1725 if self.seq_num_responses != 6:
1726 self.add_result('R', 'N')
1727 return
1729 for i in xrange(0, self.seq_num_responses):
1730 tests = nmap2_tcp_tests(self.seq_responses[i].get_ip(),
1731 self.seq_responses[i].get_tcp(),
1732 0,
1733 0)
1734 self.add_result("W%i" % (i+1), tests.get_win())
1736 def get_final_result(self):
1737 if not self.get_result_dict():
1738 return None
1739 else:
1740 return {self.test_id(): self.get_result_dict()}
1742class nmap2_t1_container(os_id_test):
1743 def __init__(self, responses, seq_base):
1744 os_id_test.__init__(self, 0)
1746 self.seq_responses = responses
1747 self.seq_num_responses = len(responses)
1748 self.seq_base = seq_base
1750 def test_id(self):
1751 return "T1"
1753 def process(self):
1754 # R, DF, T*, TG*, W-, S, A, F, O-, RD*, Q
1755 if self.seq_num_responses < 1:
1756 self.add_result("R","N")
1757 return
1759 response = self.seq_responses[0]
1760 tests = nmap2_tcp_tests(response.get_ip(),
1761 response.get_tcp(),
1762 self.seq_base,
1763 nmap2_tcp_probe.acknowledgment)
1764 self.add_result("R", "Y")
1765 self.add_result("DF", tests.get_df())
1766 self.add_result("S", tests.get_seq())
1767 self.add_result("A", tests.get_ack())
1768 self.add_result("F", tests.get_flags())
1769 self.add_result("Q", tests.get_quirks())
1771 def get_final_result(self):
1772 if not self.get_result_dict():
1773 return None
1774 else:
1775 return {self.test_id(): self.get_result_dict()}
1777class nmap2_icmp_container(os_id_test):
1778 def __init__(self, responses):
1779 os_id_test.__init__(self, 0)
1781 self.icmp_responses = responses
1782 self.icmp_num_responses = len(responses)
1784 def test_id(self):
1785 return "IE"
1787 def process(self):
1788 # R, DFI, T*, TG*, TOSI, CD, SI, DLI*
1789 if self.icmp_num_responses != 2:
1790 self.add_result("R","N")
1791 return
1793 ip1 = self.icmp_responses[0].child()
1794 ip2 = self.icmp_responses[1].child()
1795 icmp1 = ip1.child()
1796 icmp2 = ip2.child()
1798 self.add_result("R", "Y")
1800 # Value Description
1801 # N Neither of the ping responses have the DF bit set.
1802 # S Both responses echo the DF value of the probe.
1803 # Y Both of the response DF bits are set.
1804 # O The one remaining other combination-both responses have the DF bit toggled.
1805 if not ip1.get_ip_df() and not ip2.get_ip_df():
1806 self.add_result("DFI","N")
1807 elif ip1.get_ip_df() and not ip2.get_ip_df():
1808 self.add_result("DFI","S")
1809 elif ip1.get_ip_df() and ip2.get_ip_df():
1810 self.add_result("DFI","Y")
1811 else:
1812 self.add_result("DFI","O")
1814 # Value Description
1815 # Z Both TOS values are zero.
1816 # S Both TOS values are each the same as in the corresponding probe.
1817 # <NN> When they both use the same non-zero number, it is recorded here.
1818 # O Any other combination.
1819 if ip1.get_ip_tos() == 0 and ip2.get_ip_tos() == 0:
1820 self.add_result("TOSI","Z")
1821 elif ip1.get_ip_tos() == 0 and ip2.get_ip_tos() == 4:
1822 self.add_result("TOSI","S")
1823 elif ip1.get_ip_tos() == ip2.get_ip_tos():
1824 self.add_result("TOSI","%X" % ip1.get_ip_tos())
1825 else:
1826 self.add_result("TOSI","O")
1828 # Value Description
1829 # Z Both code values are zero.
1830 # S Both code values are the same as in the corresponding probe.
1831 # <NN> When they both use the same non-zero number, it is shown here.
1832 # O Any other combination.
1833 if icmp1.get_icmp_code() == 0 and icmp2.get_icmp_code() == 0:
1834 self.add_result("CD","Z")
1835 elif icmp1.get_icmp_code() == 9 and icmp2.get_icmp_code() == 0:
1836 self.add_result("CD","S")
1837 elif icmp1.get_icmp_code() == icmp2.get_icmp_code():
1838 self.add_result("CD","%X" % icmp1.get_icmp_code())
1839 else:
1840 self.add_result("CD","O")
1842 # Value Description
1843 # Z Both sequence numbers are set to 0.
1844 # S Both sequence numbers echo the ones from the probes.
1845 # <NNNN> When they both use the same non-zero number, it is recorded here.
1846 # O Any other combination.
1847 if icmp1.get_icmp_seq() == 0 and icmp2.get_icmp_seq() == 0:
1848 self.add_result("SI","Z")
1849 elif (icmp1.get_icmp_seq() == nmap2_icmp_echo_probe_1.sequence_number and
1850 icmp2.get_icmp_seq() == nmap2_icmp_echo_probe_1.sequence_number + 1):
1851 self.add_result("SI","S")
1852 elif icmp1.get_icmp_seq() == icmp2.get_icmp_seq():
1853 self.add_result("SI","%X" % icmp1.get_icmp_code())
1854 else:
1855 self.add_result("SI","O")
1857 def get_final_result(self):
1858 if not self.get_result_dict():
1859 return None
1860 else:
1861 return {self.test_id(): self.get_result_dict()}
1863class nmap1_tcp_closed_1(nmap1_tcp_probe):
1864 def __init__(self, id, addresses, tcp_ports):
1865 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0)
1866 self.t.set_SYN()
1868 def test_id(self):
1869 return "T5"
1871 def is_mine(self, packet):
1872 if tcp_probe.is_mine(self, packet):
1873 ip = packet.child()
1874 if not ip:
1875 return 0
1876 tcp = ip.child()
1877 if not tcp:
1878 return 0
1879 if tcp.get_RST():
1880 return 1
1881 else:
1882 return 0
1883 else:
1884 return 0
1886class nmap2_tcp_closed_1(nmap2_tcp_probe_2_6):
1887 # ...
1888 # T5 sends a TCP SYN packet without IP DF and a window field of
1889 # 31337 to a closed port
1890 # ...
1891 def __init__(self, id, addresses, tcp_ports):
1892 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0)
1893 self.t.set_SYN()
1894 self.i.set_ip_df(0)
1895 self.t.set_th_win(31337)
1897 def test_id(self):
1898 return "T5"
1901class nmap1_tcp_closed_2(nmap1_tcp_probe):
1903 def __init__(self, id, addresses, tcp_ports):
1904 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0)
1905 self.t.set_ACK()
1907 def test_id(self):
1908 return "T6"
1911class nmap2_tcp_closed_2(nmap2_tcp_probe_2_6):
1912 # ...
1913 # T6 sends a TCP ACK packet with IP DF and a window field of
1914 # 32768 to a closed port.
1915 # ...
1916 def __init__(self, id, addresses, tcp_ports):
1917 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0)
1918 self.t.set_ACK()
1919 self.i.set_ip_df(1)
1920 self.t.set_th_win(32768)
1922 def test_id(self):
1923 return "T6"
1926class nmap1_tcp_closed_3(nmap1_tcp_probe):
1928 def __init__(self, id, addresses, tcp_ports):
1929 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0)
1930 self.t.set_FIN()
1931 self.t.set_URG()
1932 self.t.set_PSH()
1934 def test_id(self):
1935 return "T7"
1938class nmap2_tcp_closed_3(nmap2_tcp_probe_7):
1939 # ...
1940 # T7 sends a TCP packet with the FIN, PSH, and URG flags set and a
1941 # window field of 65535 to a closed port. The IP DF bit is not set.
1942 # ...
1943 def __init__(self, id, addresses, tcp_ports):
1944 nmap2_tcp_probe_7.__init__(self, id, addresses, tcp_ports, 0)
1945 self.t.set_FIN()
1946 self.t.set_URG()
1947 self.t.set_PSH()
1948 self.t.set_th_win(65535)
1949 self.i.set_ip_df(0)
1951 def test_id(self):
1952 return "T7"
1955class NMAP2_OS_Class:
1956 def __init__(self, vendor, name, family, device_type):
1957 self.__vendor = vendor
1958 self.__name = name
1959 self.__family = family
1960 self.__device_type = device_type
1962 def get_vendor(self):
1963 return self.__vendor
1964 def get_name(self):
1965 return self.__name
1966 def get_family(self):
1967 return self.__family
1968 def get_device_type(self):
1969 return self.__device_type
1971class NMAP2_Fingerprint:
1972 def __init__(self, id, os_class, tests):
1973 self.__id = id
1974 self.__os_class = os_class
1975 self.__tests = tests
1977 def get_id(self):
1978 return self.__id
1979 def get_os_class(self):
1980 return self.__os_class
1981 def get_tests(self):
1982 return self.__tests
1984 def __str__(self):
1985 ret = "FP: [%s]" % self.__id
1986 ret += "\n vendor: %s" % self.__os_class.get_vendor()
1987 ret += "\n name: %s" % self.__os_class.get_name()
1988 ret += "\n family: %s" % self.__os_class.get_family()
1989 ret += "\n device_type: %s" % self.__os_class.get_device_type()
1991 for test in self.__tests:
1992 ret += "\n test: %s" % test
1993 for pair in self.__tests[test]:
1994 ret += "\n %s = [%s]" % (pair, self.__tests[test][pair])
1996 return ret
1998 literal_conv = { "RIPL" : { "G" : 0x148 },
1999 "RID" : { "G" : 0x1042 },
2000 "RUL" : { "G" : 0x134 } }
2002 def parse_int(self, field, value):
2003 try:
2004 return int(value, 16)
2005 except ValueError:
2006 if field in NMAP2_Fingerprint.literal_conv:
2007 if value in NMAP2_Fingerprint.literal_conv[field]:
2008 return NMAP2_Fingerprint.literal_conv[field][value]
2009 return 0
2011 def match(self, field, ref, value):
2012 options = ref.split("|")
2014 for option in options:
2015 if option.startswith(">"):
2016 if self.parse_int(field, value) > \
2017 self.parse_int(field, option[1:]):
2018 return True
2019 elif option.startswith("<"):
2020 if self.parse_int(field, value) < \
2021 self.parse_int(field, option[1:]):
2022 return True
2023 elif option.find("-") > -1:
2024 range = option.split("-")
2025 if self.parse_int (field, value) >= self.parse_int (field, range[0]) and \
2026 self.parse_int (field, value) <= self.parse_int (field, range[1]):
2027 return True
2028 else:
2029 if str(value) == str(option):
2030 return True
2032 return False
2034 def compare(self, sample, mp):
2035 max_points = 0
2036 total_points = 0
2038 for test in self.__tests:
2039 # ignore unknown response lines:
2040 if test not in sample:
2041 continue
2043 for field in self.__tests[test]:
2044 # ignore unsupported fields:
2045 if field not in sample[test] or \
2046 test not in mp or \
2047 field not in mp[test]:
2048 continue
2050 ref = self.__tests[test][field]
2051 value = sample[test][field]
2053 points = int(mp[test][field])
2055 max_points += points
2057 if self.match(field, ref, value):
2058 total_points += points
2060 return (total_points / float(max_points)) * 100
2062class NMAP2_Fingerprint_Matcher:
2063 def __init__(self, filename):
2064 self.__filename = filename
2066 def find_matches(self, res, threshold):
2067 output = []
2069 try:
2070 infile = open(self.__filename,"r")
2072 mp = self.parse_mp(self.matchpoints(infile))
2074 for fingerprint in self.fingerprints(infile):
2075 fp = self.parse_fp(fingerprint)
2076 similarity = fp.compare(res, mp)
2077 if similarity >= threshold:
2078 print("\"%s\" matches with an accuracy of %.2f%%" \
2079 % (fp.get_id(), similarity))
2080 output.append((similarity / 100,
2081 fp.get_id(),
2082 (fp.get_os_class().get_vendor(),
2083 fp.get_os_class().get_name(),
2084 fp.get_os_class().get_family(),
2085 fp.get_os_class().get_device_type())))
2087 infile.close()
2088 except IOError as err:
2089 print("IOError: %s", err)
2091 return output
2093 def sections(self, infile, token):
2094 OUT = 0
2095 IN = 1
2097 state = OUT
2098 output = []
2100 for line in infile:
2101 line = line.strip()
2102 if state == OUT:
2103 if line.startswith(token):
2104 state = IN
2105 output = [line]
2106 elif state == IN:
2107 if line:
2108 output.append(line)
2109 else:
2110 state = OUT
2111 yield output
2112 output = []
2114 if output:
2115 yield output
2117 def fingerprints(self, infile):
2118 for section in self.sections(infile,"Fingerprint"):
2119 yield section
2121 def matchpoints(self, infile):
2122 return self.sections(infile,"MatchPoints").next()
2124 def parse_line(self, line):
2125 name = line[:line.find("(")]
2126 pairs = line[line.find("(") + 1 : line.find(")")]
2128 test = {}
2130 for pair in pairs.split("%"):
2131 pair = pair.split("=")
2132 test[pair[0]] = pair[1]
2134 return (name, test)
2136 def parse_fp(self, fp):
2137 tests = {}
2139 for line in fp:
2140 if line.startswith("#"):
2141 continue
2142 elif line.startswith("Fingerprint"):
2143 fingerprint = line[len("Fingerprint") + 1:]
2144 elif line.startswith("Class"):
2145 (vendor,
2146 name,
2147 family,
2148 device_type) = line[len("Class") + 1:].split("|")
2149 os_class = NMAP2_OS_Class(vendor.strip(),
2150 name.strip(),
2151 family.strip(),
2152 device_type.strip())
2153 else:
2154 test = self.parse_line(line)
2155 tests[test[0]] = test[1]
2157 return NMAP2_Fingerprint(fingerprint, os_class, tests)
2159 def parse_mp(self, fp):
2160 tests = {}
2162 for line in fp:
2163 if line.startswith("#"):
2164 continue
2165 elif line.startswith("MatchPoints"):
2166 continue
2167 else:
2168 test = self.parse_line(line)
2169 tests[test[0]] = test[1]
2171 return tests