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

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

## 1238 statements

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#--

20 import math

21 import array

22 from six.moves import xrange, reduce

23

24 from pcapy import lookupdev, open_live

25 from impacket.ImpactPacket import UDP, TCPOption, Data, TCP, IP, ICMP, Ethernet

26 from impacket.ImpactDecoder import EthDecoder

27 from impacket import LOG

28

29 g_nmap1_signature_filename="nmap-os-fingerprints"

30 g_nmap2_signature_filename="nmap-os-db"

31

32

33 def my_gcd(a, b):

34 if a < b:

35 c = a

36 a = b

37 b = c

38

39 while 0 != b:

40 c = a & b

41 a = b

42 b = c

43 return a

44

45 class os_id_exception:

46 def __init__(self, value):

47 self.value = value

48 def __str__(self):

49 return repr(self.value)

50

51 class os_id_test:

52

53 def __init__(self, id):

54 self.__id = id

55 self.__my_packet = None

56 self.__result_dict = {}

57

58 def test_id(self):

59 return self.__class__.__name__

60

61 def get_test_packet(self):

62 return self.__my_packet.get_packet()

63

64 def set_packet(self, packet):

65 self.__my_packet = packet

66

67 def get_packet(self):

68 return self.__my_packet

69

70 def process(self, packet):

71 pass

72

73 def add_result(self, name, value):

74 self.__result_dict[name] = value

75

76 def get_id(self):

77 return self.__id

78 def is_mine(self, packet):

79 pass

80

81 def get_result_dict(self):

82 return self.__result_dict;

83

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

87

88

89 class 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 }

94

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()

100

101 self.i.set_ip_src(addresses[0])

102 self.i.set_ip_dst(addresses[1])

103

104 self.__type = type

105 self.icmp.set_icmp_type(type)

106

107 self.e.contains(self.i)

108 self.i.contains(self.icmp)

109 self.set_packet(self.e)

110

111 def is_mine(self, packet):

112

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()

119

120 # icmp_request.type_filter is a dictionary that maps request

121 # type codes to the reply codes

122

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

128

129 return 1

130

131 def process(self, packet):

132 pass

133

134

135 class 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

142

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))

152

153 def process(self, packet):

154 pass

155

156 class 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.

161

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))

171

172 def process(self, packet):

173 pass

174

175 class udp_closed_probe(os_id_test):

176

177 ip_id = 0x1234 # HARDCODED

178

179 def __init__(self, id, addresses, udp_closed ):

180

181 os_id_test.__init__(self, id )

182 self.e = Ethernet()

183 self.i = IP()

184 self.u = UDP()

185

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)

190

191 self.u.set_uh_dport( udp_closed )

192

193 self.e.contains(self.i)

194 self.i.contains(self.u)

195 self.set_packet(self.e)

196

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

206

207 if icmp.get_icmp_code() != ICMP.ICMP_UNREACH_PORT:

208 return 0;

209

210

211 self.err_data = icmp.child()

212 if not self.err_data:

213 return 0

214

215

216 return 1

217

218

219 class tcp_probe(os_id_test):

220

221 def __init__(self, id, addresses, tcp_ports, open_port ):

222

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)

232

233 if open_port:

234 self.target_port = tcp_ports[0]

235 else:

236 self.target_port = tcp_ports[1]

237

238 self.t.set_th_dport(self.target_port)

239

240 self.e.contains(self.i)

241 self.i.contains(self.t)

242 self.set_packet(self.e)

243

244 self.source_ip = addresses[0]

245 self.target_ip = addresses[1]

246

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

255

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

265

266 return 0

267

268

269 class nmap_tcp_probe(tcp_probe):

270

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)

277

278 def set_resp(self,resp):

279 pass

280

281 class nmap1_tcp_probe(nmap_tcp_probe):

282 sequence = 0x8453 # 0xBASE, obviously

283 mss = 265

284

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 ]

302

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)

306

307 def set_resp(self,resp):

308 if resp:

309 self.add_result("Resp", "Y")

310 else:

311 self.add_result("Resp", "N")

312

313 def process(self, packet):

314 ip = packet.child()

315 tcp = ip.child()

316

317 self.set_resp(True)

318

319 if ip.get_ip_df():

320 self.add_result("DF", "Y")

321 else:

322 self.add_result("DF", "N")

323

324 self.add_result("W", tcp.get_th_win())

325

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")

332

333 flags = []

334

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")

350

351 self.add_result("FLAGS", flags)

352

353 options = []

354

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")

368

369 self.add_result("OPTIONS", options)

370

371 def get_final_result(self):

372 return {self.test_id(): self.get_result_dict()}

373

374

375 class nmap2_tcp_probe(nmap_tcp_probe):

376 acknowledgment = 0x181d4f7b

377

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)

382

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")

392

393 def process(self, packet):

394 ip = packet.child()

395 tcp = ip.child()

396

397 # R, DF, T*, TG*, W, S, A, F, O, RD*, Q

398 self.set_resp(True)

399

400 tests = nmap2_tcp_tests(ip, tcp, self.sequence, self.acknowledgment)

401

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())

409

410 def get_final_result(self):

411 return {self.test_id() : self.get_result_dict()}

412

413

414 class 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 # [...]

419

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 ]

443

444

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

456

457 def test_id(self):

458 return "ECN"

459

460 def set_resp(self,resp):

461 if resp:

462 self.add_result("R", "Y")

463 else:

464 self.add_result("R", "N")

465

466 def process(self, packet):

467 ip = packet.child()

468 tcp = ip.child()

469

470 # R, DF, T*, TG*, W, O, CC, Q

471 self.set_resp(True)

472

473 tests = nmap2_tcp_tests(ip, tcp, 0, 0)

474

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())

480

481 def get_final_result(self):

482 return {self.test_id() : self.get_result_dict()}

483

484 class 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

490

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"

502

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()

507

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"

526

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"

546

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

562

563 flags = ""

564

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"

579

580 return flags

581

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

594

595 options = ""

596

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"

611

612 return options

613

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.

620

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"

638

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.

647

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.

653

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.

656

657 quirks = ""

658

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"

663

664 return quirks

665

666 class nmap2_tcp_probe_2_6(nmap2_tcp_probe):

667 sequence = 0x8453 # 0xBASE, obviously

668 mss = 265

669

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 # [...]

674

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 ]

690

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)

694

695 class nmap2_tcp_probe_7(nmap2_tcp_probe):

696 sequence = 0x8453 # 0xBASE, obviously

697 mss = 265

698

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 ]

709

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)

713

714 class nmap_port_unreachable(udp_closed_probe):

715

716 def __init__(self, id, addresses, ports):

717 udp_closed_probe.__init__(self, id, addresses, ports[2])

718 self.set_resp(False)

719

720 def test_id(self):

721 pass

722

723 def set_resp(self, resp):

724 pass

725

726 def process(self, packet):

727 pass

728

729 class nmap1_port_unreachable(nmap_port_unreachable):

730

731 def __init__(self, id, addresses, ports):

732 nmap_port_unreachable.__init__(self, id, addresses, ports)

733 self.u.contains(Data("A" * 300))

734

735 def test_id(self):

736 return "PU"

737

738 def set_resp(self,resp):

739 if resp:

740 self.add_result("Resp", "Y")

741 else:

742 self.add_result("Resp", "N")

743

744 def process(self, packet):

745 ip_orig = self.err_data

746 if ip_orig.get_ip_p() != UDP.protocol:

747 return

748

749 udp = ip_orig.child()

750

751 if not udp:

752 return

753

754 ip = packet.child()

755

756 self.set_resp(True)

757

758 if ip.get_ip_df():

759 self.add_result("DF", "Y")

760 else:

761 self.add_result("DF", "N")

762

763 self.add_result("TOS", ip.get_ip_tos())

764

765 self.add_result("IPLEN", ip.get_ip_len())

766

767 self.add_result("RIPTL", ip_orig.get_ip_len()) # Some systems return a different IPLEN

768

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")

776

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())

780

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")

787

788 udp_sum = udp.get_uh_sum()

789 udp.set_uh_sum(0)

790 udp.auto_checksum = 1

791 udp.calculate_checksum()

792

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")

799

800 self.add_result("ULEN", udp.get_uh_ulen())

801

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")

806

807 def get_final_result(self):

808 return {self.test_id(): self.get_result_dict()}

809

810 class 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)

823

824 def test_id(self):

825 return "U1"

826

827 def set_resp(self,resp):

828 if resp:

829 self.add_result("R", "Y")

830 else:

831 self.add_result("R", "N")

832

833 def process(self, packet):

834 ip_orig = self.err_data

835 if ip_orig.get_ip_p() != UDP.protocol:

836 return

837

838 udp = ip_orig.child()

839

840 if not udp:

841 return

842

843 ip = packet.child()

844

845 icmp = ip.child()

846

847 if ip.get_ip_df():

848 self.add_result("DF", "Y")

849 else:

850 self.add_result("DF", "N")

851

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.

874

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.

890

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())

896

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())

902

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())

910

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())

923

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())

936

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())

947

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")

954

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()

963

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)

968

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())

973

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")

982

983 def get_final_result(self):

984 return {self.test_id(): self.get_result_dict()}

985

986 class OS_ID:

987

988 def __init__(self, target, ports):

989 pcap_dev = lookupdev()

990 self.p = open_live(pcap_dev, 600, 0, 3000)

991

992 self.__source = self.p.getlocalip()

993 self.__target = target

994

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()

998

999 self.tests_sent = []

1000 self.outstanding_count = 0

1001 self.results = {}

1002 self.current_id = 12345

1003

1004 self.__ports = ports

1005

1006 def releasePcap(self):

1007 if not (self.p is None):

1008 self.p.close()

1009

1010 def get_new_id(self):

1011 id = self.current_id

1012 self.current_id += 1

1013 self.current_id &= 0xFFFF

1014 return id

1015

1016 def send_tests(self, tests):

1017 self.outstanding_count = 0

1018

1019 for t_class in tests:

1020

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] )

1027

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)

1033

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

1040

1041 def run(self):

1042 pass

1043

1044 def get_source(self):

1045 return self.__source

1046

1047 def get_target(self):

1048 return self.__target

1049

1050 def get_ports(self):

1051 return self.__ports

1052

1053 def packet_handler(self, len, data):

1054 packet = self.decoder.decode(data)

1055

1056 for t in self.tests_sent:

1057 if t.is_mine(packet):

1058 t.process(packet)

1059 self.outstanding_count -= 1

1060

1061

1062 class 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()

1067

1068 def test_id(self):

1069 return "T1"

1070

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

1085

1086

1087 class 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)

1090

1091 def test_id(self):

1092 return "T2"

1093

1094 class 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)

1104

1105 def test_id(self):

1106 return "T2"

1107

1108 class 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()

1115

1116 def test_id(self):

1117 return "T3"

1118

1119 class 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)

1132

1133 def test_id(self):

1134 return "T3"

1135

1136 class 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()

1140

1141 def test_id(self):

1142 return "T4"

1143

1144 class 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)

1154

1155 def test_id(self):

1156 return "T4"

1157

1158

1159 class 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

1167

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

1174

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)

1182

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.

1187

1188 def process(self, p):

1189 raise Exception("Method process is meaningless for class %s." % self.__class__.__name__)

1190

1191

1192 class 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

1196

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)

1204

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()

1209

1210 def process(self, p):

1211 raise Exception("Method process is meaningless for class %s." % self.__class__.__name__)

1212

1213 class 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 ]

1227

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)

1231

1232 class 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 ]

1246

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)

1250

1251 class 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 ]

1267

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)

1271

1272 class 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 ]

1284

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)

1288

1289 class 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 ]

1303

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)

1307

1308 class 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 ]

1318

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)

1322

1323 class 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)

1326

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

1334

1335 def test_id(self):

1336 return "TSEQ"

1337

1338 def set_ts_seqclass(self, ts_seqclass):

1339 self.pre_ts_seqclass = ts_seqclass

1340

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()

1347

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))

1359

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()}

1363

1364 def ipid_sequence(self):

1365 if self.seq_num_responses < 2:

1366 return nmap1_seq.IPID_SEQ_UNKNOWN

1367

1368 ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1))

1369

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()

1374

1375 if cur_ipid < prev_ipid and (cur_ipid > 500 or prev_ipid < 65000):

1376 return nmap1_seq.IPID_SEQ_RD

1377

1378 if prev_ipid != 0 or cur_ipid != 0:

1379 null_ipids = 0

1380 ipid_diffs[i-1] = abs(cur_ipid - prev_ipid)

1381

1382 if null_ipids:

1383 return nmap1_seq.IPID_SEQ_ZERO

1384

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.

1388

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

1394

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

1404

1405 if is_ms:

1406 return nmap1_seq.IPID_SEQ_BROKEN_INCR

1407 if is_incremental:

1408 return nmap1_seq.IPID_SEQ_INCR

1409

1410 return nmap1_seq.IPID_SEQ_UNKNOWN

1411

1412 def ts_sequence(self):

1413 if self.seq_num_responses < 2:

1414 return nmap1_seq.TS_SEQ_UNKNOWN

1415

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.

1422

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)

1427

1428 LOG.info("The avg TCP TS HZ is: %f" % avg_freq)

1429

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

1436

1437 return nmap1_seq.TS_SEQ_UNKNOWN

1438

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

1443

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]

1452

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

1474

1475 return seqclass

1476

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 }

1485

1486 def add_seqclass(self, id):

1487 self.add_result('CLASS', nmap1_seq_container.seqclasses[id])

1488

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)

1494

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 }

1502

1503 def add_tsclass(self, id):

1504 self.add_result('TS', nmap1_seq_container.tsclasses[id])

1505

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 }

1514

1515 def add_ipidclass(self, id):

1516 self.add_result('IPID', nmap1_seq_container.ipidclasses[id])

1517

1518

1519 class 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)

1522

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

1530

1531 def test_id(self):

1532 return "SEQ"

1533

1534 def set_ts_seqclass(self, ts_seqclass):

1535 self.pre_ts_seqclass = ts_seqclass

1536

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))

1546

1547 def get_final_result(self):

1548 return {self.test_id(): self.get_result_dict()}

1549

1550 def calc_ti(self):

1551 if self.seq_num_responses < 2:

1552 return

1553

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 }

1562

1563 ipid_diffs = array.array('H', [0] * (self.seq_num_responses - 1))

1564

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()

1570

1571 if prev_ipid != 0 or cur_ipid != 0:

1572 null_ipids = 0

1573

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

1578

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

1582

1583 if null_ipids:

1584 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_ZERO])

1585 return

1586

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

1593

1594 if all_zero:

1595 self.add_result('TI', ipidclasses[nmap2_seq.IPID_SEQ_CONSTANT])

1596 return

1597

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

1605

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

1614

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])

1619

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.

1633

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

1640

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)

1645

1646 LOG.info("The avg TCP TS HZ is: %f" % avg_freq)

1647

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)

1657

1658 def calc_sp(self):

1659 seq_gcd = reduce(my_gcd, self.seq_diffs)

1660

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)

1665

1666 seq_rate = seq_avg_rate

1667 si_index = 0

1668 seq_stddev = 0

1669

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))

1674

1675 div_gcd = 1

1676 if seq_gcd > 9:

1677 div_gcd = seq_gcd

1678

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

1683

1684 seq_stddev /= self.seq_num_responses - 2

1685 seq_stddev = math.sqrt(seq_stddev)

1686

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))

1691

1692 self.add_result('SP', "%X" % si_index)

1693 self.add_result('GCD', "%X" % seq_gcd)

1694 self.add_result('ISR', "%X" % seq_rate)

1695

1696 class nmap2_ops_container(os_id_test):

1697 def __init__(self, responses):

1698 os_id_test.__init__(self, 0)

1699

1700 self.seq_responses = responses

1701 self.seq_num_responses = len(responses)

1702

1703 def test_id(self):

1704 return "OPS"

1705

1706 def process(self):

1707 if self.seq_num_responses != 6:

1708 self.add_result('R', 'N')

1709 return

1710

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())

1717

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()}

1723

1724 class nmap2_win_container(os_id_test):

1725 def __init__(self, responses):

1726 os_id_test.__init__(self, 0)

1727

1728 self.seq_responses = responses

1729 self.seq_num_responses = len(responses)

1730

1731 def test_id(self):

1732 return "WIN"

1733

1734 def process(self):

1735 if self.seq_num_responses != 6:

1736 self.add_result('R', 'N')

1737 return

1738

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())

1745

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()}

1751

1752 class nmap2_t1_container(os_id_test):

1753 def __init__(self, responses, seq_base):

1754 os_id_test.__init__(self, 0)

1755

1756 self.seq_responses = responses

1757 self.seq_num_responses = len(responses)

1758 self.seq_base = seq_base

1759

1760 def test_id(self):

1761 return "T1"

1762

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

1768

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())

1780

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()}

1786

1787 class nmap2_icmp_container(os_id_test):

1788 def __init__(self, responses):

1789 os_id_test.__init__(self, 0)

1790

1791 self.icmp_responses = responses

1792 self.icmp_num_responses = len(responses)

1793

1794 def test_id(self):

1795 return "IE"

1796

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

1802

1803 ip1 = self.icmp_responses[0].child()

1804 ip2 = self.icmp_responses[1].child()

1805 icmp1 = ip1.child()

1806 icmp2 = ip2.child()

1807

1808 self.add_result("R", "Y")

1809

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")

1823

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")

1837

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")

1851

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")

1866

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()}

1872

1873 class 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()

1877

1878 def test_id(self):

1879 return "T5"

1880

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

1895

1896 class 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)

1906

1907 def test_id(self):

1908 return "T5"

1909

1910

1911 class nmap1_tcp_closed_2(nmap1_tcp_probe):

1912

1913 def __init__(self, id, addresses, tcp_ports):

1914 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 0)

1915 self.t.set_ACK()

1916

1917 def test_id(self):

1918 return "T6"

1919

1920

1921 class 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)

1931

1932 def test_id(self):

1933 return "T6"

1934

1935

1936 class nmap1_tcp_closed_3(nmap1_tcp_probe):

1937

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()

1943

1944 def test_id(self):

1945 return "T7"

1946

1947

1948 class 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)

1960

1961 def test_id(self):

1962 return "T7"

1963

1964

1965 class 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

1971

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

1980

1981 class NMAP2_Fingerprint:

1982 def __init__(self, id, os_class, tests):

1983 self.__id = id

1984 self.__os_class = os_class

1985 self.__tests = tests

1986

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

1993

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()

2000

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])

2005

2006 return ret

2007

2008 literal_conv = { "RIPL" : { "G" : 0x148 },

2009 "RID" : { "G" : 0x1042 },

2010 "RUL" : { "G" : 0x134 } }

2011

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

2020

2021 def match(self, field, ref, value):

2022 options = ref.split("|")

2023

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

2041

2042 return False

2043

2044 def compare(self, sample, mp):

2045 max_points = 0

2046 total_points = 0

2047

2048 for test in self.__tests:

2049 # ignore unknown response lines:

2050 if test not in sample:

2051 continue

2052

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

2059

2060 ref = self.__tests[test][field]

2061 value = sample[test][field]

2062

2063 points = int(mp[test][field])

2064

2065 max_points += points

2066

2067 if self.match(field, ref, value):

2068 total_points += points

2069

2070 return (total_points / float(max_points)) * 100

2071

2072 class NMAP2_Fingerprint_Matcher:

2073 def __init__(self, filename):

2074 self.__filename = filename

2075

2076 def find_matches(self, res, threshold):

2077 output = []

2078

2079 try:

2080 infile = open(self.__filename,"r")

2081

2082 mp = self.parse_mp(self.matchpoints(infile))

2083

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())))

2096

2097 infile.close()

2098 except IOError as err:

2099 print("IOError: %s", err)

2100

2101 return output

2102

2103 def sections(self, infile, token):

2104 OUT = 0

2105 IN = 1

2106

2107 state = OUT

2108 output = []

2109

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 = []

2123

2124 if output:

2125 yield output

2126

2127 def fingerprints(self, infile):

2128 for section in self.sections(infile,"Fingerprint"):

2129 yield section

2130

2131 def matchpoints(self, infile):

2132 return self.sections(infile,"MatchPoints").next()

2133

2134 def parse_line(self, line):

2135 name = line[:line.find("(")]

2136 pairs = line[line.find("(") + 1 : line.find(")")]

2137

2138 test = {}

2139

2140 for pair in pairs.split("%"):

2141 pair = pair.split("=")

2142 test[pair[0]] = pair[1]

2143

2144 return (name, test)

2145

2146 def parse_fp(self, fp):

2147 tests = {}

2148

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]

2166

2167 return NMAP2_Fingerprint(fingerprint, os_class, tests)

2168

2169 def parse_mp(self, fp):

2170 tests = {}

2171

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]

2180

2181 return tests