Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 

2# 

3# This computer software is owned by Core SDI Inc. and is 

4# protected by U.S. copyright laws and other laws and by international 

5# treaties. This computer software is furnished by CORE SDI Inc. 

6# pursuant to a written license agreement and may be used, copied, 

7# transmitted, and stored only in accordance with the terms of such 

8# license and with the inclusion of the above copyright notice. This 

9# computer software or any other copies thereof may not be provided or 

10# otherwise made available to any other person. 

11# 

12#` 

13# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 

14# WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI Inc. BE LIABLE 

15# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR 

16# CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF 

17# THIS SOFTWARE 

18# 

19#-- 

20import math 

21import array 

22from six.moves import xrange, reduce 

23 

24from pcapy import lookupdev, open_live 

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

26from impacket.ImpactDecoder import EthDecoder 

27from impacket import LOG 

28 

29g_nmap1_signature_filename="nmap-os-fingerprints" 

30g_nmap2_signature_filename="nmap-os-db" 

31 

32 

33def 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 

45class os_id_exception: 

46 def __init__(self, value): 

47 self.value = value 

48 def __str__(self): 

49 return repr(self.value) 

50 

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

89class icmp_request(os_id_test): 

90 type_filter = { ICMP.ICMP_ECHO : ICMP.ICMP_ECHOREPLY, 

91 ICMP.ICMP_IREQ : ICMP.ICMP_IREQREPLY, 

92 ICMP.ICMP_MASKREQ : ICMP.ICMP_MASKREPLY, 

93 ICMP.ICMP_TSTAMP : ICMP.ICMP_TSTAMPREPLY } 

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 

135class nmap2_icmp_echo_probe_1(icmp_request): 

136 # The first one has the IP DF bit set, a type-of-service (TOS) byte  

137 # value of zero, a code of nine (even though it should be zero),  

138 # the sequence number 295, a random IP ID and ICMP request identifier,  

139 # and a random character repeated 120 times for the data payload. 

140 sequence_number = 295 

141 id = 0x5678 

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 

156class nmap2_icmp_echo_probe_2(icmp_request): 

157 # The second ping query is similar, except a TOS of four  

158 # (IP_TOS_RELIABILITY) is used, the code is zero, 150 bytes of data is  

159 # sent, and the IP ID, request ID, and sequence numbers are incremented  

160 # by one from the previous query values. 

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 

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

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

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

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

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

414class nmap2_ecn_probe(nmap_tcp_probe): 

415 # From nmap-4.22SOC8/osscan2.cc: 

416 # [...] 

417 # "\003\003\012\001\002\004\005\264\004\002\001\001" 

418 # [...] 

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 

484class nmap2_tcp_tests: 

485 def __init__(self, ip, tcp, sequence, acknowledgment): 

486 self.__ip = ip 

487 self.__tcp = tcp 

488 self.__sequence = sequence 

489 self.__acknowledgment = acknowledgment 

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 

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

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

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

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

810class nmap2_port_unreachable(nmap_port_unreachable): 

811 # UDP (U1) 

812 # This probe is a UDP packet sent to a closed port. The character 'C' 

813 # (0x43) is repeated 300 times for the data field. The IP ID value is  

814 # set to 0x1042 for operating systems which allow us to set this. If  

815 # the port is truly closed and there is no firewall in place, Nmap  

816 # expects to receive an ICMP port unreachable message in return.  

817 # That response is then subjected to the R, DF, T, TG, TOS, IPL, UN,  

818 # RIPL, RID, RIPCK, RUCK, RUL, and RUD tests.  

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

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

821 self.u.contains(Data("C" * 300)) 

822 self.i.set_ip_id(0x1042) 

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 

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

1062class nmap1_tcp_open_1(nmap1_tcp_probe): 

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

1064 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) 

1065 self.t.set_ECE() 

1066 self.t.set_SYN() 

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 

1087class nmap1_tcp_open_2(nmap1_tcp_probe): 

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

1089 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) 

1090 

1091 def test_id(self): 

1092 return "T2" 

1093 

1094class nmap2_tcp_open_2(nmap2_tcp_probe_2_6): 

1095 # From: https://nmap.org/book/osdetect-methods.html 

1096 # [...] 

1097 # T2 sends a TCP null (no flags set) packet with the IP DF bit set and a  

1098 # window field of 128 to an open port. 

1099 # ... 

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

1101 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1) 

1102 self.i.set_ip_df(1) 

1103 self.t.set_th_win(128) 

1104 

1105 def test_id(self): 

1106 return "T2" 

1107 

1108class nmap1_tcp_open_3(nmap1_tcp_probe): 

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

1110 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) 

1111 self.t.set_SYN() 

1112 self.t.set_FIN() 

1113 self.t.set_URG() 

1114 self.t.set_PSH() 

1115 

1116 def test_id(self): 

1117 return "T3" 

1118 

1119class nmap2_tcp_open_3(nmap2_tcp_probe_2_6): 

1120 # ... 

1121 # T3 sends a TCP packet with the SYN, FIN, URG, and PSH flags set and a  

1122 # window field of 256 to an open port. The IP DF bit is not set. 

1123 # ... 

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

1125 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1) 

1126 self.t.set_SYN() 

1127 self.t.set_FIN() 

1128 self.t.set_URG() 

1129 self.t.set_PSH() 

1130 self.t.set_th_win(256) 

1131 self.i.set_ip_df(0) 

1132 

1133 def test_id(self): 

1134 return "T3" 

1135 

1136class nmap1_tcp_open_4(nmap1_tcp_probe): 

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

1138 nmap1_tcp_probe.__init__(self, id, addresses, tcp_ports, 1) 

1139 self.t.set_ACK() 

1140 

1141 def test_id(self): 

1142 return "T4" 

1143 

1144class nmap2_tcp_open_4(nmap2_tcp_probe_2_6): 

1145 # ... 

1146 # T4 sends a TCP ACK packet with IP DF and a window field of 1024 to  

1147 # an open port. 

1148 # ... 

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

1150 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 1) 

1151 self.t.set_ACK() 

1152 self.i.set_ip_df(1) 

1153 self.t.set_th_win(1024) 

1154 

1155 def test_id(self): 

1156 return "T4" 

1157 

1158 

1159class nmap1_seq(nmap1_tcp_probe): 

1160 SEQ_UNKNOWN = 0 

1161 SEQ_64K = 1 

1162 SEQ_TD = 2 

1163 SEQ_RI = 4 

1164 SEQ_TR = 8 

1165 SEQ_i800 = 16 

1166 SEQ_CONSTANT = 32 

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 

1192class nmap2_seq(nmap2_tcp_probe): 

1193 TS_SEQ_UNKNOWN = 0 

1194 TS_SEQ_ZERO = 1 # At least one of the timestamps we received back was 0 

1195 TS_SEQ_UNSUPPORTED = 5 # System didn't send back a timestamp 

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 

1213class nmap2_seq_1(nmap2_seq): 

1214 # Packet #1: window scale (10),  

1215 # NOP,  

1216 # MSS (1460),  

1217 # timestamp (TSval: 0xFFFFFFFF; TSecr: 0),  

1218 # SACK permitted.  

1219 # The window field is 1. 

1220 tcp_options = [ 

1221 TCPOption(TCPOption.TCPOPT_WINDOW, 10), 

1222 TCPOption(TCPOption.TCPOPT_NOP), 

1223 TCPOption(TCPOption.TCPOPT_MAXSEG, 1460), 

1224 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), 

1225 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED) 

1226 ] 

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 

1232class nmap2_seq_2(nmap2_seq): 

1233 # Packet #2: MSS (1400),  

1234 # window scale (0),  

1235 # SACK permitted,  

1236 # timestamp (TSval: 0xFFFFFFFF; TSecr: 0),  

1237 # EOL.  

1238 # The window field is 63. 

1239 tcp_options = [ 

1240 TCPOption(TCPOption.TCPOPT_MAXSEG, 1400), 

1241 TCPOption(TCPOption.TCPOPT_WINDOW, 0), 

1242 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), 

1243 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), 

1244 TCPOption(TCPOption.TCPOPT_EOL) 

1245 ] 

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 

1251class nmap2_seq_3(nmap2_seq): 

1252 # Packet #3: Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),  

1253 # NOP,  

1254 # NOP,  

1255 # window scale (5),  

1256 # NOP,  

1257 # MSS (640).  

1258 # The window field is 4. 

1259 tcp_options = [ 

1260 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), 

1261 TCPOption(TCPOption.TCPOPT_NOP), 

1262 TCPOption(TCPOption.TCPOPT_NOP), 

1263 TCPOption(TCPOption.TCPOPT_WINDOW, 5), 

1264 TCPOption(TCPOption.TCPOPT_NOP), 

1265 TCPOption(TCPOption.TCPOPT_MAXSEG, 640) 

1266 ] 

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 

1272class nmap2_seq_4(nmap2_seq): 

1273 # Packet #4: SACK permitted,  

1274 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),  

1275 # window scale (10),  

1276 # EOL.  

1277 # The window field is 4. 

1278 tcp_options = [ 

1279 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), 

1280 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), 

1281 TCPOption(TCPOption.TCPOPT_WINDOW, 10), 

1282 TCPOption(TCPOption.TCPOPT_EOL) 

1283 ] 

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 

1289class nmap2_seq_5(nmap2_seq): 

1290 # Packet #5: MSS (536),  

1291 # SACK permitted, 

1292 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0),  

1293 # window scale (10),  

1294 # EOL.  

1295 # The window field is 16. 

1296 tcp_options = [ 

1297 TCPOption(TCPOption.TCPOPT_MAXSEG, 536), 

1298 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), 

1299 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF), 

1300 TCPOption(TCPOption.TCPOPT_WINDOW, 10), 

1301 TCPOption(TCPOption.TCPOPT_EOL) 

1302 ] 

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 

1308class nmap2_seq_6(nmap2_seq): 

1309 # Packet #6: MSS (265),  

1310 # SACK permitted,  

1311 # Timestamp (TSval: 0xFFFFFFFF; TSecr: 0).  

1312 # The window field is 512. 

1313 tcp_options = [ 

1314 TCPOption(TCPOption.TCPOPT_MAXSEG, 265), 

1315 TCPOption(TCPOption.TCPOPT_SACK_PERMITTED), 

1316 TCPOption(TCPOption.TCPOPT_TIMESTAMP, 0xFFFFFFFF) 

1317 ] 

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 

1323class nmap1_seq_container(os_id_test): 

1324 def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs): 

1325 os_id_test.__init__(self, 0) 

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 

1519class nmap2_seq_container(os_id_test): 

1520 def __init__(self, num_seq_samples, responses, seq_diffs, ts_diffs, time_diffs): 

1521 os_id_test.__init__(self, 0) 

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 

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

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

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

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

1873class nmap1_tcp_closed_1(nmap1_tcp_probe): 

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

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

1876 self.t.set_SYN() 

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 

1896class nmap2_tcp_closed_1(nmap2_tcp_probe_2_6): 

1897 # ... 

1898 # T5 sends a TCP SYN packet without IP DF and a window field of  

1899 # 31337 to a closed port 

1900 # ... 

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

1902 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0) 

1903 self.t.set_SYN() 

1904 self.i.set_ip_df(0) 

1905 self.t.set_th_win(31337) 

1906 

1907 def test_id(self): 

1908 return "T5" 

1909 

1910 

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

1921class nmap2_tcp_closed_2(nmap2_tcp_probe_2_6): 

1922 # ... 

1923 # T6 sends a TCP ACK packet with IP DF and a window field of  

1924 # 32768 to a closed port. 

1925 # ... 

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

1927 nmap2_tcp_probe_2_6.__init__(self, id, addresses, tcp_ports, 0) 

1928 self.t.set_ACK() 

1929 self.i.set_ip_df(1) 

1930 self.t.set_th_win(32768) 

1931 

1932 def test_id(self): 

1933 return "T6" 

1934 

1935 

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

1948class nmap2_tcp_closed_3(nmap2_tcp_probe_7): 

1949 # ... 

1950 # T7 sends a TCP packet with the FIN, PSH, and URG flags set and a  

1951 # window field of 65535 to a closed port. The IP DF bit is not set. 

1952 # ... 

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

1954 nmap2_tcp_probe_7.__init__(self, id, addresses, tcp_ports, 0) 

1955 self.t.set_FIN() 

1956 self.t.set_URG() 

1957 self.t.set_PSH() 

1958 self.t.set_th_win(65535) 

1959 self.i.set_ip_df(0) 

1960 

1961 def test_id(self): 

1962 return "T7" 

1963 

1964 

1965class NMAP2_OS_Class: 

1966 def __init__(self, vendor, name, family, device_type): 

1967 self.__vendor = vendor 

1968 self.__name = name 

1969 self.__family = family 

1970 self.__device_type = device_type 

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 

1981class NMAP2_Fingerprint: 

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

1983 self.__id = id 

1984 self.__os_class = os_class 

1985 self.__tests = tests 

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 

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