github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/nmapAnswerMachine.py (about)

     1  #!/usr/bin/env python
     2  import uncrc32
     3  
     4  try:
     5      import pcap as pcapy
     6  except ImportError:
     7      import pcapy
     8  
     9  from impacket import ImpactPacket
    10  from impacket import ImpactDecoder
    11  from impacket.ImpactPacket import TCPOption
    12  from impacket.examples import logger
    13  from impacket.examples import os_ident
    14  
    15  #defaults
    16  
    17  MAC = "01:02:03:04:05:06"
    18  IP  = "192.168.67.254"
    19  IFACE = "eth0"
    20  OPEN_TCP_PORTS = [80, 443]
    21  OPEN_UDP_PORTS = [111]
    22  UDP_CMD_PORT = 12345
    23  nmapOSDB = '/usr/share/nmap/nmap-os-db'
    24  
    25  # Fingerprint = 'Adtran NetVanta 3200 router' # CD=Z TOSI=Z <----------- NMAP detects it as Linux!!!
    26  # Fingerprint = 'ADIC Scalar 1000 tape library remote management unit' # DFI=S
    27  # Fingerprint = 'Siemens Gigaset SX541 or USRobotics USR9111 wireless DSL modem' # DFI=O U1(DF=N IPL=38)
    28  # Fingerprint = 'Apple Mac OS X 10.5.6 (Leopard) (Darwin 9.6.0)' # DFI=Y SI=S U1(DF=Y)
    29  
    30  Fingerprint = 'Sun Solaris 10 (SPARC)' 
    31  # Fingerprint = 'Sun Solaris 9 (x86)'
    32  
    33  # Fingerprint = '3Com OfficeConnect 3CRWER100-75 wireless broadband router'  # TI=Z DFI=N !SS TI=Z II=I
    34  # Fingerprint = 'WatchGuard Firebox X5w firewall/WAP' # TI=RD
    35  # no TI=Hex
    36  # Fingerprint = 'FreeBSD 6.0-STABLE - 6.2-RELEASE' # TI=RI
    37  # Fingerprint = 'Microsoft Windows 98 SE' # TI=BI ----> BROKEN! nmap shows no SEQ() output
    38  # Fingerprint = 'Microsoft Windows NT 4.0 SP5 - SP6' # TI=BI TOSI=S SS=S
    39  # Fingerprint = 'Microsoft Windows Vista Business' # TI=I U1(IPL=164)
    40  
    41  # Fingerprint = 'FreeBSD 6.1-RELEASE' # no TI (TI=O)
    42  
    43  # Fingerprint = '2Wire 1701HG wireless ADSL modem' # IE(R=N)
    44  
    45  # Fingerprint = 'Cisco Catalyst 1912 switch' # TOSI=O SS=S
    46  
    47  O_ETH = 0
    48  O_IP  = 1
    49  O_ARP = 1
    50  O_UDP = 2
    51  O_TCP = 2
    52  O_ICMP = 2
    53  O_UDP_DATA = 3
    54  O_ICMP_DATA = 3
    55  
    56  def string2tuple(string):
    57      if string.find(':') >= 0:
    58         return [int(x) for x in string.split(':')]
    59      else:
    60         return [int(x) for x in string.split('.')]
    61  
    62  class Responder:
    63     templateClass = None
    64     signatureName      = None
    65  
    66     def __init__(self, machine):
    67         self.machine = machine
    68         print("Initializing %s" % self.__class__.__name__)
    69         self.initTemplate()
    70         self.initFingerprint()
    71  
    72     def initTemplate(self):
    73         if not self.templateClass:
    74            self.template_onion = None
    75         else:
    76            try:
    77               probe = self.templateClass(0, ['0.0.0.0',self.getIP()],[0, 0])
    78            except:
    79               probe = self.templateClass(0, ['0.0.0.0',self.getIP()])
    80            self.template_onion = [probe.get_packet()]
    81            try:
    82                while 1: self.template_onion.append (self.template_onion[-1].child ())
    83            except: pass
    84         
    85            # print("Template: %s" % self.template_onion[O_ETH])
    86            # print("Options: %r" % self.template_onion[O_TCP].get_padded_options())
    87            # print("Flags: 0x%04x" % self.template_onion[O_TCP].get_th_flags())
    88  
    89     def initFingerprint(self):
    90         if not self.signatureName:
    91            self.fingerprint = None
    92         else:
    93            self.fingerprint = self.machine.fingerprint.get_tests()[self.signatureName].copy()
    94  
    95     def isMine(self, in_onion):
    96         return False
    97  
    98     def buildAnswer(self, in_onion):
    99         return None
   100  
   101     def sendAnswer(self, out_onion):
   102         self.machine.sendPacket(out_onion)
   103  
   104     def process(self, in_onion):
   105         if not self.isMine(in_onion): return False
   106         print("Got packet for %s" % self.__class__.__name__)
   107  
   108         out_onion = self.buildAnswer(in_onion)
   109  
   110         if out_onion: self.sendAnswer(out_onion)
   111         return True
   112  
   113     def getIP(self):
   114         return self.machine.ipAddress
   115  
   116  # Generic Responders (does the word Responder exist?)
   117  
   118  class ARPResponder(Responder):
   119     def isMine(self, in_onion):
   120         if len(in_onion) < 2: return False
   121  
   122         if in_onion[O_ARP].ethertype != ImpactPacket.ARP.ethertype:
   123            return False
   124  
   125         return (
   126            in_onion[O_ARP].get_ar_op() == 1 and # ARP REQUEST
   127            in_onion[O_ARP].get_ar_tpa() == string2tuple(self.machine.ipAddress))
   128  
   129     def buildAnswer(self, in_onion):
   130         eth = ImpactPacket.Ethernet()
   131         arp = ImpactPacket.ARP()
   132         eth.contains(arp)
   133  
   134         arp.set_ar_hrd(1)	# Hardward type Ethernet
   135         arp.set_ar_pro(0x800)	# IP
   136         arp.set_ar_op(2)	# REPLY
   137         arp.set_ar_hln(6)
   138         arp.set_ar_pln(4)
   139         arp.set_ar_sha(string2tuple(self.machine.macAddress))
   140         arp.set_ar_spa(string2tuple(self.machine.ipAddress))
   141         arp.set_ar_tha(in_onion[O_ARP].get_ar_sha())
   142         arp.set_ar_tpa(in_onion[O_ARP].get_ar_spa())
   143  
   144         eth.set_ether_shost(arp.get_ar_sha())
   145         eth.set_ether_dhost(arp.get_ar_tha())
   146  
   147         return [eth, arp]
   148  
   149  class IPResponder(Responder):
   150     def buildAnswer(self, in_onion):
   151         eth = ImpactPacket.Ethernet()
   152         ip = ImpactPacket.IP()
   153  
   154         eth.contains(ip)
   155  
   156         eth.set_ether_shost(in_onion[O_ETH].get_ether_dhost())
   157         eth.set_ether_dhost(in_onion[O_ETH].get_ether_shost())
   158  
   159         ip.set_ip_src(in_onion[O_IP].get_ip_dst())
   160         ip.set_ip_dst(in_onion[O_IP].get_ip_src())
   161         ip.set_ip_id(self.machine.getIPID())
   162  
   163         return [eth, ip]
   164  
   165     def sameIPFlags(self, in_onion):
   166         if not self.template_onion: return True
   167         return (self.template_onion[O_IP].get_ip_off() & 0xe000) == (in_onion[O_IP].get_ip_off() & 0xe000)
   168  
   169     def isMine(self, in_onion):
   170         if len(in_onion) < 2: return False
   171  
   172         return (
   173             (in_onion[O_IP].ethertype == ImpactPacket.IP.ethertype) and
   174             (in_onion[O_IP].get_ip_dst() == self.machine.ipAddress) and
   175             self.sameIPFlags(in_onion)
   176         )
   177  
   178     def setTTLFromFingerprint(self, out_onion):
   179         f = self.fingerprint
   180         # Test T: Initial TTL = range_low-range_hi, base 16
   181         # Assumption: we are using the minimum in the TTL range
   182         try:
   183            ttl = f['T'].split('-')
   184            ttl = int(ttl[0], 16)
   185         except:
   186            ttl = 0x7f
   187  
   188         # Test TG: Initial TTL Guess. It's just a number, we prefer this
   189         try: ttl = int(f['TG'], 16)
   190         except: pass
   191  
   192         out_onion[O_IP].set_ip_ttl(ttl)
   193  
   194  class ICMPResponder(IPResponder):
   195     def buildAnswer(self, in_onion):
   196         out_onion = IPResponder.buildAnswer(self, in_onion)
   197         icmp = ImpactPacket.ICMP()
   198  
   199         out_onion[O_IP].contains(icmp)
   200         out_onion.append(icmp)
   201  
   202         icmp.set_icmp_id(in_onion[O_ICMP].get_icmp_id())
   203         icmp.set_icmp_seq(in_onion[O_ICMP].get_icmp_seq())
   204  
   205         out_onion[O_IP].set_ip_id(self.machine.getIPID_ICMP())
   206  
   207         return out_onion
   208  
   209     def isMine(self, in_onion):
   210         if not IPResponder.isMine(self, in_onion): return False
   211         if len(in_onion) < 3: return False
   212  
   213         return (in_onion[O_ICMP].protocol == ImpactPacket.ICMP.protocol) and self.sameICMPTemplate(in_onion)
   214  
   215     def sameICMPTemplate(self, in_onion):
   216         t_ip           = self.template_onion[O_IP]
   217         t_icmp         = self.template_onion[O_ICMP]
   218         t_icmp_datalen = self.template_onion[O_ICMP_DATA].get_size()
   219  
   220         return (
   221                 (t_ip.get_ip_tos () == in_onion[O_IP].get_ip_tos ()) and (
   222                     t_ip.get_ip_df () == in_onion[O_IP].get_ip_df ()) and (
   223                             t_icmp.get_icmp_type () == in_onion[O_ICMP].get_icmp_type ()) and (
   224                             t_icmp.get_icmp_code () == in_onion[O_ICMP].get_icmp_code ()) and (
   225                             t_icmp_datalen == in_onion[O_ICMP_DATA].get_size ())
   226         )
   227  
   228  class UDPResponder(IPResponder):
   229     def isMine(self, in_onion):
   230         return (
   231             IPResponder.isMine(self, in_onion) and
   232             (len(in_onion) >= 3) and
   233             (in_onion[O_UDP].protocol == ImpactPacket.UDP.protocol)
   234         )
   235  
   236  class OpenUDPResponder(UDPResponder):
   237     def isMine(self, in_onion):
   238         return (
   239            UDPResponder.isMine(self, in_onion) and
   240            self.machine.isUDPPortOpen(in_onion[O_UDP].get_uh_dport()))
   241  
   242     def buildAnswer(self, in_onion):
   243         out_onion = IPResponder.buildAnswer(self, in_onion)
   244         udp = ImpactPacket.UDP()
   245  
   246         out_onion[O_IP].contains(udp)
   247         out_onion.append(udp)
   248  
   249         udp.set_uh_dport(in_onion[O_UDP].get_uh_sport())
   250         udp.set_uh_sport(in_onion[O_UDP].get_uh_dport())
   251  
   252         return out_onion
   253  
   254  class ClosedUDPResponder(UDPResponder):
   255     def isMine(self, in_onion):
   256         return (
   257            UDPResponder.isMine(self, in_onion) and
   258            not self.machine.isUDPPortOpen(in_onion[O_UDP].get_uh_dport()))
   259  
   260     def buildAnswer(self, in_onion):
   261         out_onion = IPResponder.buildAnswer(self, in_onion)
   262         icmp = ImpactPacket.ICMP()
   263  
   264         out_onion[O_IP].contains(icmp)
   265         out_onion.append(icmp)
   266  
   267         icmp.contains(in_onion[O_IP])
   268         out_onion += in_onion[O_IP:]
   269  
   270         icmp.set_icmp_type(icmp.ICMP_UNREACH)
   271         icmp.set_icmp_code(icmp.ICMP_UNREACH_PORT)
   272  
   273         return out_onion
   274  
   275  class TCPResponder(IPResponder):
   276     def buildAnswer(self, in_onion):
   277         out_onion = IPResponder.buildAnswer(self, in_onion)
   278         tcp = ImpactPacket.TCP()
   279  
   280         out_onion[O_IP].contains(tcp)
   281         out_onion.append(tcp)
   282  
   283         tcp.set_th_dport(in_onion[O_TCP].get_th_sport())
   284         tcp.set_th_sport(in_onion[O_TCP].get_th_dport())
   285  
   286         return out_onion
   287  
   288     def sameTCPFlags(self, in_onion):
   289         if not self.template_onion: return True
   290         in_flags = in_onion[O_TCP].get_th_flags() & 0xfff
   291         t_flags  = self.template_onion[O_TCP].get_th_flags() & 0xfff
   292  
   293         return in_flags == t_flags
   294  
   295     def sameTCPOptions(self, in_onion):
   296         if not self.template_onion: return True
   297         in_options = in_onion[O_TCP].get_padded_options()
   298         t_options  = self.template_onion[O_TCP].get_padded_options()
   299  
   300         return in_options == t_options
   301  
   302     def isMine(self, in_onion):
   303         if not IPResponder.isMine(self, in_onion): return False
   304         if len(in_onion) < 3: return False
   305  
   306         return (in_onion[O_TCP].protocol == ImpactPacket.TCP.protocol and self.sameTCPFlags (in_onion) and self.sameTCPOptions (
   307             in_onion))
   308  
   309  class OpenTCPResponder(TCPResponder):
   310     def isMine(self, in_onion):
   311         return (TCPResponder.isMine (self, in_onion) and in_onion[O_TCP].get_SYN () and self.machine.isTCPPortOpen (
   312             in_onion[O_TCP].get_th_dport ()))
   313  
   314     def buildAnswer(self, in_onion):
   315         out_onion = TCPResponder.buildAnswer(self, in_onion)
   316  
   317         out_onion[O_TCP].set_SYN()
   318         out_onion[O_TCP].set_ACK()
   319         out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq()+1)
   320         out_onion[O_TCP].set_th_seq(self.machine.getTCPSequence())
   321  
   322         return out_onion
   323  
   324  class ClosedTCPResponder(TCPResponder):
   325     def isMine(self, in_onion):
   326         return (
   327            TCPResponder.isMine(self, in_onion) and 
   328            in_onion[O_TCP].get_SYN() and
   329            not self.machine.isTCPPortOpen(in_onion[O_TCP].get_th_dport()))
   330  
   331     def buildAnswer(self, in_onion):
   332         out_onion = TCPResponder.buildAnswer(self, in_onion)
   333  
   334         out_onion[O_TCP].set_RST()
   335         out_onion[O_TCP].set_ACK()
   336         out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq()+1)
   337         out_onion[O_TCP].set_th_seq(self.machine.getTCPSequence())
   338  
   339         return out_onion
   340  
   341  class UDPCommandResponder(OpenUDPResponder):
   342     # default UDP_CMD_PORT is 12345
   343     # use with:
   344     # echo cmd:exit | nc -u $(IP) $(UDP_CMD_PORT)
   345     # echo cmd:who | nc -u $(IP) $(UDP_CMD_PORT)
   346  
   347     def set_port(self, port):
   348         self.port = port
   349         self.machine.openUDPPort(port)
   350         return self
   351  
   352     def isMine(self, in_onion):
   353         return ( OpenUDPResponder.isMine(self, in_onion))# and
   354            #in_onion[O_UDP].get_uh_dport() == self.port)
   355  
   356     def buildAnswer(self, in_onion):
   357         cmd = in_onion[O_UDP_DATA].get_bytes().tostring()
   358         if cmd[:4] == 'cmd:': cmd = cmd[4:].strip()
   359         print("Got command: %r" % cmd)
   360  
   361         if cmd == 'exit':
   362            from sys import exit
   363            exit()
   364  
   365         out_onion = OpenUDPResponder.buildAnswer(self, in_onion)
   366         out_onion.append(ImpactPacket.Data())
   367         out_onion[O_UDP].contains(out_onion[O_UDP_DATA])
   368         if cmd == 'who':
   369            out_onion[O_UDP_DATA].set_data(self.machine.fingerprint.get_id())
   370            
   371         return out_onion
   372  
   373  # NMAP2 specific responders
   374  class NMAP2UDPResponder(ClosedUDPResponder):
   375     signatureName      = 'U1'
   376  
   377     # No real need to filter
   378     # def isMine(self, in_onion):
   379     #     return (
   380     #        ClosedUDPResponder.isMine(self, inOnion) and
   381     #        (in_onion[O_UDP_DATA].get_size() == 300))
   382  
   383     def buildAnswer(self, in_onion):
   384         out_onion = ClosedUDPResponder.buildAnswer(self, in_onion)
   385         f = self.fingerprint
   386  
   387         # assume R = Y
   388         try:
   389            if (f['R'] == 'N'): return None
   390         except: pass
   391  
   392         # Test DF: Don't fragment IP bit set = [YN]
   393         if (f['DF'] == 'Y'): out_onion[O_IP].set_ip_df(True)
   394         else: out_onion[O_IP].set_ip_df(False)
   395  
   396         self.setTTLFromFingerprint(out_onion)
   397  
   398         # UN. Assume 0
   399         try: un = int(f['UN'],16)
   400         except: un = 0
   401         out_onion[O_ICMP].set_icmp_void(un)
   402  
   403         # RIPL. Assume original packet just quoted
   404         try:
   405            ripl = int(f['RIPL'],16)	# G generates exception
   406            out_onion[O_ICMP_DATA].set_ip_len(ripl)
   407         except:
   408            pass
   409  
   410         # RID. Assume original packet just quoted
   411         try:
   412            rid = int(f['RID'],16)	# G generates exception
   413            out_onion[O_ICMP_DATA].set_ip_id(rid)
   414         except:
   415            pass
   416  
   417         # RIPCK. Assume original packet just quoted
   418         try: ripck = f['RIPCK']
   419         except: ripck = 'G'
   420         if   ripck == 'I': out_onion[O_ICMP_DATA].set_ip_sum(0x6765)
   421         elif ripck == 'Z': out_onion[O_ICMP_DATA].set_ip_sum(0)
   422         elif ripck == 'G': out_onion[O_ICMP_DATA].auto_checksum = 0
   423  
   424         # RUCK. Assume original packet just quoted
   425         try:
   426            ruck = int(f['RUCK'], 16)
   427            out_onion[O_ICMP_DATA+1].set_uh_sum(ruck)
   428         except: 
   429            out_onion[O_ICMP_DATA+1].auto_checksum = 0
   430  
   431         # RUD. Assume original packet just quoted
   432         try: rud = f['RUD']
   433         except: rud = 'G'
   434  
   435         if rud == 'I':
   436            udp_data = out_onion[O_ICMP_DATA+2]
   437            udp_data.set_data('G'*udp_data.get_size())
   438  
   439         # IPL. Assume all original packet is quoted
   440         # This has to be the last thing we do
   441         # as we are going to render the packet before doing it
   442         try: ipl = int(f['IPL'], 16)
   443         except: ipl = None
   444  
   445         if not ipl is None:
   446            data = out_onion[O_ICMP_DATA].get_packet()
   447            out_onion[O_ICMP].contains(ImpactPacket.Data())
   448            ip_and_icmp_len = out_onion[O_IP].get_size()
   449  
   450            data = data[:ipl - ip_and_icmp_len]
   451  
   452            data += '\x00'*(ipl-len(data)-ip_and_icmp_len)
   453            out_onion = out_onion[:O_ICMP_DATA]
   454            out_onion.append(ImpactPacket.Data(data))
   455            out_onion[O_ICMP].contains(out_onion[O_ICMP_DATA])
   456  
   457         return out_onion
   458  
   459  class NMAP2ICMPResponder(ICMPResponder):
   460     def buildAnswer(self, in_onion):
   461         f = self.fingerprint
   462  
   463         # assume R = Y
   464         try:
   465            if (f['R'] == 'N'): return None
   466         except: pass
   467  
   468         out_onion = ICMPResponder.buildAnswer(self, in_onion)
   469  
   470         # assume DFI = N
   471         try: dfi = f['DFI'] 
   472         except: dfi = 'N'
   473  
   474         if   dfi == 'N': out_onion[O_IP].set_ip_df(False)
   475         elif dfi == 'Y': out_onion[O_IP].set_ip_df(True)
   476         elif dfi == 'S': out_onion[O_IP].set_ip_df(in_onion[O_IP].get_ip_df())
   477         elif dfi == 'O': out_onion[O_IP].set_ip_df(not in_onion[O_IP].get_ip_df())
   478         else: raise Exception('Unsupported IE(DFI=%s)' % dfi)
   479  
   480         # assume DLI = S
   481         try: dli = f['DLI'] 
   482         except: dli = 'S'
   483  
   484         if   dli == 'S': out_onion[O_ICMP].contains(in_onion[O_ICMP_DATA])
   485         elif dli != 'Z': raise Exception('Unsupported IE(DFI=%s)' % dli)
   486  
   487         self.setTTLFromFingerprint(out_onion)
   488  
   489         # assume SI = S
   490         try: si = f['SI'] 
   491         except: si = 'S'
   492  
   493         if   si == 'S': out_onion[O_ICMP].set_icmp_seq(in_onion[O_ICMP].get_icmp_seq())
   494         elif si == 'Z': out_onion[O_ICMP].set_icmp_seq(0) # this is not currently supported by nmap, but I've done it already
   495         else:
   496             try: out_onion[O_ICMP].set_icmp_seq(int(si, 16)) # this is not supported either by nmap
   497             except: raise Exception('Unsupported IE(SI=%s)' % si)
   498  
   499         # assume CD = S
   500         try: cd = f['CD'] 
   501         except: cd = 'S'
   502  
   503         if   cd == 'Z': out_onion[O_ICMP].set_icmp_code(0)
   504         elif cd == 'S': out_onion[O_ICMP].set_icmp_code(in_onion[O_ICMP].get_icmp_code())
   505         elif cd == 'O': out_onion[O_ICMP].set_icmp_code(in_onion[O_ICMP].get_icmp_code()+1)	# no examples in DB
   506         else:
   507             try: out_onion[O_ICMP].set_icmp_code(int(cd, 16)) # documented, but no examples available
   508             except: raise Exception('Unsupported IE(CD=%s)' % cd)
   509  
   510         # assume TOSI = S
   511         try: tosi = f['TOSI'] 
   512         except: tosi = 'S'
   513  
   514         if   tosi == 'Z': out_onion[O_IP].set_ip_tos(0)
   515         elif tosi == 'S': out_onion[O_IP].set_ip_tos(in_onion[O_IP].get_ip_tos())
   516         elif tosi == 'O': out_onion[O_IP].set_ip_tos(in_onion[O_IP].get_ip_tos()+1)	# no examples in DB
   517         else:
   518             try: out_onion[O_IP].set_ip_tos(int(tosi, 16)) # documented, but no examples available
   519             except: raise Exception('Unsupported IE(TOSI=%s)' % tosi)
   520  
   521         return out_onion
   522  
   523  class NMAP2TCPResponder(TCPResponder):
   524     def buildAnswer(self, in_onion):
   525         out_onion = TCPResponder.buildAnswer(self, in_onion)
   526  
   527         f = self.fingerprint
   528  
   529         # Test R: There is a response = [YN]
   530         if (f['R'] == 'N'): return None
   531  
   532         # Test DF: Don't fragment IP bit set = [YN]
   533         if (f['DF'] == 'Y'): out_onion[O_IP].set_ip_df(True)
   534         else: out_onion[O_IP].set_ip_df(False)
   535  
   536         # Test W: Initial TCP windows size
   537         try: win = int(f['W'],16)
   538         except: win = 0
   539         out_onion[O_TCP].set_th_win(win)
   540  
   541         self.setTTLFromFingerprint(out_onion)
   542  
   543         # Test CC: Explicit congestion notification
   544         # Two TCP flags are used in this test: ECE and CWR
   545         try:
   546             cc = f['CC']
   547             if cc == 'N': ece,cwr = 0,0
   548             if cc == 'Y': ece,cwr = 1,0
   549             if cc == 'S': ece,cwr = 1,1
   550             if cc == 'O': ece,cwr = 0,1
   551         except:
   552             ece,cwr = 0,0
   553  
   554         if ece: out_onion[O_TCP].set_ECE()
   555         else:   out_onion[O_TCP].reset_ECE()
   556         if cwr: out_onion[O_TCP].set_CWR()
   557         else:   out_onion[O_TCP].reset_CWR()
   558  
   559  
   560         # Test O: TCP Options
   561         try: options = f['O']
   562         except: options = ''
   563         self.setTCPOptions(out_onion, options)
   564         
   565         # Test S: TCP Sequence number
   566         # Z: Sequence number is zero
   567         # A: Sequence number is the same as the ACK in the probe
   568         # A+: Sequence number is the same as the ACK in the probe + 1
   569         # O: Other value
   570         try: s = f['S']
   571         except: s = 'O'
   572         if s == 'Z': out_onion[O_TCP].set_th_seq(0)
   573         if s == 'A': out_onion[O_TCP].set_th_seq(in_onion[O_TCP].get_th_ack())
   574         if s == 'A+': out_onion[O_TCP].set_th_seq(in_onion[O_TCP].get_th_ack()+1)
   575         if s == 'O': out_onion[O_TCP].set_th_seq(self.machine.getTCPSequence())
   576  
   577         # Test A: TCP ACK number
   578         # Z: Ack is zero
   579         # S: Ack is the same as the Squence number in the probe
   580         # S+: Ack is the same as the Squence number in the probe + 1
   581         # O: Other value
   582         try: a = f['A']
   583         except: a = 'O'
   584         if a == 'Z': out_onion[O_TCP].set_th_ack(0)
   585         if a == 'S': out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq())
   586         if a == 'S+': out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq()+1)
   587  
   588         # Test Q: Quirks
   589         # R: Reserved bit set (right after the header length)
   590         # U: Urgent pointer non-zero and URG flag clear
   591         try: 
   592            if 'R' in f['Q']: out_onion[O_TCP].set_flags(0x800)
   593         except: pass
   594         try: 
   595            if 'U' in f['Q']: out_onion[O_TCP].set_th_urp(0xffff)
   596         except: pass
   597  
   598         # Test F: TCP Flags
   599         try: flags = f['F']
   600         except: flags = ''
   601         if 'E' in flags: out_onion[O_TCP].set_ECE()
   602         if 'U' in flags: out_onion[O_TCP].set_URG()
   603         if 'A' in flags: out_onion[O_TCP].set_ACK()
   604         if 'P' in flags: out_onion[O_TCP].set_PSH()
   605         if 'R' in flags: out_onion[O_TCP].set_RST()
   606         if 'S' in flags: out_onion[O_TCP].set_SYN()
   607         if 'F' in flags: out_onion[O_TCP].set_FIN()
   608  
   609         # Test RD: TCP Data checksum (mostly for data in RST)
   610         try:
   611             crc = f['RD']
   612             if crc != '0':	# when the
   613                 crc = int(crc, 16)
   614                 data  = 'TCP Port is closed\x00'
   615                 data += uncrc32.compensate(data, crc)
   616                 data = ImpactPacket.Data(data) 
   617                 out_onion.append(data)
   618                 out_onion[O_TCP].contains(data)
   619         except:
   620             pass
   621         return out_onion
   622  
   623     def setTCPOptions(self, onion, options):
   624         def getValue(string, i):
   625             value = 0
   626             
   627             idx = i
   628             for c in options[i:]:
   629                 try:
   630                     value = value * 0x10 + int(c,16)
   631                 except:
   632                     break
   633                 idx += 1
   634  
   635             return value, idx
   636  
   637         # Test O,O1=O6: TCP Options
   638         # L: End of Options
   639         # N: NOP
   640         # S: Selective ACK
   641         # Mx: MSS (x is a hex number)
   642         # Wx: Windows Scale (x is a hex number)
   643         # Tve: Timestamp (v and e are two binary digits, v for TSval and e for TSecr
   644  
   645         i = 0
   646         tcp = onion[O_TCP]
   647         while i < len(options):
   648            opt = options[i]
   649            i += 1
   650            if opt == 'L': tcp.add_option(TCPOption(TCPOption.TCPOPT_EOL))
   651            if opt == 'N': tcp.add_option(TCPOption(TCPOption.TCPOPT_NOP))
   652            if opt == 'S': tcp.add_option(TCPOption(TCPOption.TCPOPT_SACK_PERMITTED))
   653            if opt == 'T':
   654               opt = TCPOption(TCPOption.TCPOPT_TIMESTAMP)  # default ts = 0, ts_echo = 0
   655               if options[i] == '1':  opt.set_ts(self.machine.getTCPTimeStamp())
   656               if options[i+1] == '1': opt.set_ts_echo(0xffffffff)
   657               tcp.add_option(opt)
   658               i += 2
   659            if opt == 'M':
   660               maxseg, i = getValue(options, i)
   661               tcp.add_option(TCPOption(TCPOption.TCPOPT_MAXSEG, maxseg))
   662            if opt == 'W':
   663               window, i = getValue(options, i)
   664               tcp.add_option(TCPOption(TCPOption.TCPOPT_WINDOW, window))
   665  
   666  class nmap2_SEQ(NMAP2TCPResponder):
   667     templateClass = None
   668     signatureName = None
   669     seqNumber     = None
   670  
   671     def initFingerprint(self):
   672         NMAP2TCPResponder.initFingerprint(self)
   673         if not self.seqNumber: return
   674         else:
   675            OPS = self.machine.fingerprint.get_tests()['OPS']
   676            WIN = self.machine.fingerprint.get_tests()['WIN']
   677            self.fingerprint['O'] = OPS['O%d' % self.seqNumber]
   678            self.fingerprint['W'] = WIN['W%d' % self.seqNumber]
   679  
   680  class nmap2_ECN(NMAP2TCPResponder):
   681     templateClass = os_ident.nmap2_ecn_probe
   682     signatureName      = 'ECN'
   683  
   684  class nmap2_SEQ1(nmap2_SEQ):
   685     templateClass = os_ident.nmap2_seq_1
   686     signatureName = 'T1'
   687     seqNumber     = 1
   688  
   689  class nmap2_SEQ2(nmap2_SEQ):
   690     templateClass = os_ident.nmap2_seq_2
   691     signatureName = 'T1'
   692     seqNumber     = 2
   693  
   694  class nmap2_SEQ3(nmap2_SEQ):
   695     templateClass = os_ident.nmap2_seq_3
   696     signatureName = 'T1'
   697     seqNumber     = 3
   698  
   699  class nmap2_SEQ4(nmap2_SEQ):
   700     templateClass = os_ident.nmap2_seq_4
   701     signatureName = 'T1'
   702     seqNumber     = 4
   703  
   704  class nmap2_SEQ5(nmap2_SEQ):
   705     templateClass = os_ident.nmap2_seq_5
   706     signatureName = 'T1'
   707     seqNumber     = 5
   708  
   709  class nmap2_SEQ6(nmap2_SEQ):
   710     templateClass = os_ident.nmap2_seq_6
   711     signatureName = 'T1'
   712     seqNumber     = 6
   713  
   714  class nmap2_T2(NMAP2TCPResponder):
   715     templateClass = os_ident.nmap2_tcp_open_2
   716     signatureName = 'T2'
   717  
   718  class nmap2_T3(NMAP2TCPResponder):
   719     templateClass = os_ident.nmap2_tcp_open_3
   720     signatureName = 'T3'
   721  
   722  class nmap2_T4(NMAP2TCPResponder):
   723     templateClass = os_ident.nmap2_tcp_open_4
   724     signatureName = 'T4'
   725  
   726  class nmap2_T5(NMAP2TCPResponder):
   727     templateClass = os_ident.nmap2_tcp_closed_1
   728     signatureName = 'T5'
   729  
   730  class nmap2_T6(NMAP2TCPResponder):
   731     templateClass = os_ident.nmap2_tcp_closed_2
   732     signatureName = 'T6'
   733  
   734  class nmap2_T7(NMAP2TCPResponder):
   735     templateClass = os_ident.nmap2_tcp_closed_3
   736     signatureName = 'T7'
   737  
   738  class nmap2_ICMP_1(NMAP2ICMPResponder):
   739     templateClass = os_ident.nmap2_icmp_echo_probe_1
   740     signatureName = 'IE'
   741  
   742  class nmap2_ICMP_2(NMAP2ICMPResponder):
   743     templateClass = os_ident.nmap2_icmp_echo_probe_2
   744     signatureName = 'IE'
   745  
   746  class Machine:
   747     AssumedTimeIntervalPerPacket = 0.11 # seconds
   748     def __init__(self, emmulating, interface, ipAddress, macAddress, openTCPPorts = [], openUDPPorts = [], nmapOSDB = 'nmap-os-db'):
   749         self.interface = interface
   750         self.ipAddress = ipAddress
   751         self.macAddress = macAddress
   752         self.responders = []
   753         self.decoder = ImpactDecoder.EthDecoder()
   754  
   755         self.initPcap()
   756         self.initFingerprint(emmulating, nmapOSDB)
   757  
   758         self.initSequenceGenerators()
   759         self.openTCPPorts = openTCPPorts
   760         self.openUDPPorts = openUDPPorts
   761         print(self)
   762  
   763     def openUDPPort(self, port):
   764         if self.isUDPPortOpen(port): return
   765         self.openUDPPorts.append(port)
   766  
   767     def isUDPPortOpen(self, port):
   768         return port in self.openUDPPorts
   769  
   770     def isTCPPortOpen(self, port):
   771         return port in self.openTCPPorts
   772  
   773     def initPcap(self):
   774         self.pcap = pcapy.open_live(self.interface, 65535, 1, 0)
   775         try:       self.pcap.setfilter("host %s or ether host %s" % (self.ipAddress, self.macAddress))
   776         except:    self.pcap.setfilter("host %s or ether host %s" % (self.ipAddress, self.macAddress), 1, 0xFFFFFF00)
   777  
   778     def initGenericResponders(self):
   779         # generic responders
   780         self.addResponder(ARPResponder(self))
   781         self.addResponder(OpenUDPResponder(self))
   782         self.addResponder(ClosedUDPResponder(self))
   783         self.addResponder(OpenTCPResponder(self))
   784         self.addResponder(ClosedTCPResponder(self))
   785  
   786     def initFingerprint(self, emmulating, nmapOSDB):
   787         fpm = os_ident.NMAP2_Fingerprint_Matcher('')
   788         f = open(nmapOSDB, 'r')
   789         for text in fpm.fingerprints(f):
   790             fingerprint = fpm.parse_fp(text)
   791             if fingerprint.get_id() == emmulating:
   792                self.fingerprint = fingerprint
   793                self.simplifyFingerprint()
   794                # print(fingerprint)
   795                return
   796  
   797         raise Exception("Couldn't find fingerprint data for %r" % emmulating)
   798  
   799     def simplifyFingerprint(self):
   800         tests = self.fingerprint.get_tests()
   801         for probeName in tests:
   802             probe = tests[probeName]
   803             for test in probe:
   804                 probe[test] = probe[test].split('|')[0]
   805                 
   806     def initSequenceGenerators(self):
   807         self.initIPIDGenerator()
   808         self.initTCPISNGenerator()
   809         self.initTCPTSGenerator()
   810  
   811     def initIPIDGenerator(self):
   812         seq = self.fingerprint.get_tests()['SEQ']
   813         self.ip_ID = 0
   814  
   815         try: TI = seq['TI']
   816         except: TI = 'O'
   817  
   818         if   TI == 'Z': self.ip_ID_delta = 0
   819         elif TI == 'RD': self.ip_ID_delta = 30000
   820         elif TI == 'RI': self.ip_ID_delta = 1234
   821         elif TI == 'BI': self.ip_ID_delta = 1024+256
   822         elif TI == 'I': self.ip_ID_delta = 1
   823         elif TI == 'O': self.ip_ID_delta = 123
   824         else: self.ip_ID_delta = int(TI, 16)
   825  
   826         try: ss = seq['SS']
   827         except: ss = 'O'
   828  
   829         self.ip_ID_ICMP_delta = None
   830         if ss == 'S': self.ip_ID_ICMP = None
   831         else:
   832            self.ip_ID_ICMP = 0
   833            try: II = seq['II']
   834            except: II = 'O'
   835  
   836            if   II == 'Z': self.ip_ID_ICMP_delta = 0
   837            elif II == 'RD': self.ip_ID_ICMP_delta = 30000
   838            elif II == 'RI': self.ip_ID_ICMP_delta = 1234
   839            elif II == 'BI': self.ip_ID_ICMP_delta = 1024+256
   840            elif II == 'I': self.ip_ID_ICMP_delta = 1
   841            elif II == 'O': self.ip_ID_ICMP_delta = 123
   842            else: self.ip_ID_ICMP_delta = int(II, 16)
   843  
   844         # generate a few, so we don't start with 0 when we don't have to
   845         for i in range(10):
   846             self.getIPID()
   847             self.getIPID_ICMP()
   848  
   849         print("IP ID Delta: %d" % self.ip_ID_delta)
   850         print("IP ID ICMP Delta: %s" % self.ip_ID_ICMP_delta)
   851  
   852     def initTCPISNGenerator(self):
   853         # tcp_ISN and tcp_ISN_delta for TCP Initial sequence numbers
   854         self.tcp_ISN = 0
   855         try:
   856            self.tcp_ISN_GCD = int(self.fingerprint.get_tests()['SEQ']['GCD'].split('-')[0], 16)
   857         except:
   858            self.tcp_ISN_GCD = 1
   859  
   860         try:
   861            isr = self.fingerprint.get_tests()['SEQ']['ISR'].split('-')
   862            if len(isr) == 1:
   863               isr = int(isr[0], 16)
   864            else:
   865               isr = (int(isr[0], 16) + int(isr[1], 16)) / 2
   866         except:
   867            isr = 0
   868  
   869         try:
   870            sp = self.fingerprint.get_tests()['SEQ']['SP'].split('-')
   871            sp = int(sp[0], 16)
   872         except:
   873            sp = 0
   874  
   875         self.tcp_ISN_stdDev = (2**(sp/8.0)) * 5 / 4  # n-1 on small populations... erm...
   876  
   877         if self.tcp_ISN_GCD > 9:
   878            self.tcp_ISN_stdDev *= self.tcp_ISN_GCD
   879  
   880         self.tcp_ISN_stdDev *= self.AssumedTimeIntervalPerPacket
   881  
   882         self.tcp_ISN_delta  = 2**(isr/8.0) * self.AssumedTimeIntervalPerPacket
   883  
   884         # generate a few, so we don't start with 0 when we don't have to
   885         for i in range(10): self.getTCPSequence()
   886  
   887         print("TCP ISN Delta: %f" % self.tcp_ISN_delta)
   888         print("TCP ISN Standard Deviation: %f" % self.tcp_ISN_stdDev)
   889  
   890     def initTCPTSGenerator(self):
   891         # tcp_TS and tcp_TS_delta for TCP Time stamp generation
   892         self.tcp_TS = 0
   893  
   894         try: ts = self.fingerprint.get_tests()['SEQ']['TS']
   895         except: ts = 'U'
   896  
   897         if ts == 'U' or ts == 'Z': self.tcp_TS_delta = 0
   898         else:
   899             self.tcp_TS_delta = (2**int(ts, 16)) * self.AssumedTimeIntervalPerPacket
   900  
   901         # generate a few, so we don't start with 0 when we don't have to
   902         for i in range(10): self.getTCPTimeStamp()
   903  
   904         print("TCP TS Delta: %f" % self.tcp_TS_delta)
   905  
   906     def getIPID(self):
   907         answer = self.ip_ID
   908         self.ip_ID += self.ip_ID_delta
   909         self.ip_ID %= 0x10000
   910         # print("IP ID: %x" % answer)
   911         return answer
   912  
   913     def getIPID_ICMP(self):
   914         if self.ip_ID_ICMP is None:
   915            return self.getIPID()
   916  
   917         answer = self.ip_ID_ICMP
   918         self.ip_ID_ICMP += self.ip_ID_ICMP_delta
   919         self.ip_ID_ICMP %= 0x10000
   920         # print("---> IP ID: %x" % answer)
   921         return answer
   922  
   923     def getTCPSequence(self):
   924         answer = self.tcp_ISN + self.tcp_ISN_stdDev # *random.random()
   925         self.tcp_ISN_stdDev *= -1
   926         answer = int(int(answer/self.tcp_ISN_GCD) * self.tcp_ISN_GCD)
   927         self.tcp_ISN += self.tcp_ISN_delta
   928         self.tcp_ISN %= 0x100000000
   929         # print("---> TCP Sequence: %d" % (answer % 0x100000000))
   930         return answer % 0x100000000
   931  
   932     def getTCPTimeStamp(self):
   933         answer = int(round(self.tcp_TS))
   934         self.tcp_TS += self.tcp_TS_delta
   935         self.tcp_TS %= 0x100000000
   936         # print("---> TCP Time Stamp: %x" % answer)
   937         return answer
   938  
   939     def sendPacket(self, onion):
   940         if not onion: return
   941         print("--> Packet sent:")
   942         #print(onion[0])
   943         #print()
   944         self.pcap.sendpacket(onion[O_ETH].get_packet())
   945  
   946     def addResponder(self, aResponder):
   947         self.responders.append(aResponder)
   948  
   949     def run(self):
   950         while 1:
   951            p = self.pcap.next()
   952            try:    in_onion = [self.decoder.decode(p[1])]
   953            except: in_onion = [self.decoder.decode(p[0])]
   954            try:
   955               while 1: in_onion.append(in_onion[-1].child())
   956            except:
   957               pass
   958  
   959            #print("-------------- Received: ", in_onion[0])
   960            for r in self.responders:
   961                if r.process(in_onion): break
   962  
   963  
   964  def main():
   965     def initResponders(machine):
   966         # cmd responder
   967         # machine.addResponder(UDPCommandResponder(machine).set_port(UDP_CMD_PORT))
   968  
   969         # nmap2 specific responders
   970         machine.addResponder(nmap2_SEQ1(machine))
   971         machine.addResponder(nmap2_SEQ2(machine))
   972         machine.addResponder(nmap2_SEQ3(machine))
   973         machine.addResponder(nmap2_SEQ4(machine))
   974         machine.addResponder(nmap2_SEQ5(machine))
   975         machine.addResponder(nmap2_SEQ6(machine))
   976         machine.addResponder(nmap2_ECN(machine))
   977         machine.addResponder(nmap2_T2(machine))
   978         machine.addResponder(nmap2_T3(machine))
   979         machine.addResponder(nmap2_T4(machine))
   980         machine.addResponder(nmap2_T5(machine))
   981         machine.addResponder(nmap2_T6(machine))
   982         machine.addResponder(nmap2_T7(machine))
   983         machine.addResponder(nmap2_ICMP_1(machine))
   984         machine.addResponder(nmap2_ICMP_2(machine))
   985         machine.addResponder(NMAP2UDPResponder(machine))
   986  
   987     from sys import argv, exit
   988     def usage():
   989         print("""
   990         if arg == '-h': usage()
   991         if arg == '--help': usage()
   992         if arg == '-f': Fingerprint = value
   993         if arg == '-p': IP = value
   994         if arg == '-m': MAC = value
   995         if arg == '-i': IFACE = value
   996         if arg == '-d': nmapOsDB = value
   997  
   998     where:
   999         arg = argv[i]
  1000         value = argv[i+1]
  1001         """)
  1002         exit()
  1003  
  1004     global Fingerprint, IFACE, MAC, IP, nmapOSDB
  1005     for i, arg in enumerate(argv):
  1006         try: value = argv[i+1]
  1007         except: value = None
  1008         if arg == '-h': usage()
  1009         if arg == '--help': usage()
  1010         if arg == '-f': Fingerprint = value
  1011         if arg == '-p': IP = value
  1012         if arg == '-m': MAC = value
  1013         if arg == '-i': IFACE = value
  1014         if arg == '-d': nmapOSDB = value
  1015  
  1016     print("Emulating: %r" % Fingerprint)
  1017     print("at %s / %s / %s" % (IFACE, MAC, IP))
  1018     machine = Machine(
  1019         Fingerprint,
  1020         IFACE,
  1021         IP,
  1022         MAC,
  1023         OPEN_TCP_PORTS,
  1024         OPEN_UDP_PORTS,
  1025         nmapOSDB=nmapOSDB)
  1026  
  1027     initResponders(machine)
  1028     machine.initGenericResponders()
  1029     machine.run()
  1030  
  1031  if __name__ == '__main__':
  1032     # Init the example's logger theme
  1033     logger.init()
  1034     main()
  1035  
  1036  # All Probes
  1037  # [x] SEQ
  1038  # [x] OPS
  1039  # [x] WIN
  1040  # [x] T1
  1041  # [x] T2
  1042  # [x] T3
  1043  # [x] T4
  1044  # [x] T5
  1045  # [x] T6
  1046  # [x] T7
  1047  # [x] IE
  1048  # [x] ECN
  1049  # [x] U1
  1050  
  1051  # All Tests
  1052  
  1053  # SEQ()
  1054  # [x] TCP ISN sequence predictability index (SP)
  1055  # [x] TCP ISN greatest common divisor (GCD)
  1056  # [x] TCP ISN counter rate (ISR)
  1057  # [x] IP ID sequence generation algorithm on TCP Open ports (TI)
  1058  #   [x] Z  - All zeros
  1059  #   [x] RD - Random: It increments at least once by at least 20000.
  1060  #   [-] Hex Value - fixed IP ID
  1061  #   [x] RI - Random positive increments. Any (delta_i > 1000, and delta_i % 256 != 0) or (delta_i > 256000 and delta_i % 256 == 0)
  1062  #   [x] BI - Broken increment. All delta_i % 256 = 0 and all delta_i <= 5120.
  1063  #   [x] I - Incremental. All delta_i < 10
  1064  #   [x] O - (Omitted, the test does not show in the fingerprint). None of the other
  1065  # [-] IP ID sequence generation algorithm on TCP closed ports (CI)
  1066  # [x] IP ID sequence generation algorithm on ICMP messages (II)
  1067  # [x] Shared IP ID sequence Boolean (SS)
  1068  # [x] TCP timestamp option algorithm (TS)
  1069  #   [x] U - unsupported (don't send TS)
  1070  #   [x] 0 - Zero
  1071  #   [x] 1 - 0-5.66 (2 Hz)
  1072  #   [x] 7 - 70-150 (100 Hz)
  1073  #   [x] 8 - 150-350 (200 Hz)
  1074  #   [x]   - avg_freq = sum(TS_diff/time_diff) . round(.5 + math.log(avg_freq)/math.log(2)))
  1075  #           time_diff = 0.11 segs
  1076  # OPS()
  1077  # [x] TCP options (O, O1-O6)
  1078  # WIN()
  1079  # [x] TCP initial window size (W, W1-W6)
  1080  # ECN, T1-T7
  1081  # [x] TCP options (O, O1-O6)
  1082  # [x] TCP initial window size (W, W1-W6)
  1083  # [x] Responsiveness (R)
  1084  # [x] IP don't fragment bit (DF)
  1085  # [x] IP initial time-to-live (T)
  1086  # [x] IP initial time-to-live guess (TG)
  1087  # [x] Explicit congestion notification (CC)
  1088  # [x] TCP miscellaneous quirks (Q)
  1089  # [x] TCP sequence number (S)
  1090  # [x] TCP acknowledgment number (A)
  1091  # [x] TCP flags (F)
  1092  # [x] TCP RST data checksum (RD)
  1093  # IE()
  1094  # [x] Responsiveness (R)
  1095  # [x] Don't fragment (ICMP) (DFI)
  1096  # [x] IP initial time-to-live (T)
  1097  # [x] IP initial time-to-live guess (TG)
  1098  # [x] ICMP response code (CD)
  1099  #-[x] IP Type of Service (TOSI)
  1100  #-[x] ICMP Sequence number (SI)
  1101  #-[x] IP Data Length (DLI)
  1102  # U1()
  1103  # [x] Responsiveness (R)
  1104  # [x] IP don't fragment bit (DF)
  1105  # [x] IP initial time-to-live (T)
  1106  # [x] IP initial time-to-live guess (TG)
  1107  # [x] IP total length (IPL)
  1108  # [x] Unused port unreachable field nonzero (UN)
  1109  # [x] Returned probe IP total length value (RIPL)
  1110  # [x] Returned probe IP ID value (RID)
  1111  # [x] Integrity of returned probe IP checksum value (RIPCK)
  1112  # [x] Integrity of returned probe UDP checksum (RUCK)
  1113  # [x] Integrity of returned UDP data (RUD)
  1114  # [-] ??? (TOS) Type of Service
  1115  # [-] ??? (RUL) Length of return UDP packet is correct
  1116  
  1117  # sudo nmap -O 127.0.0.2 -p 22,111,89
  1118  # sudo python nmapAnswerMachine.py -i eth0 -p 192.168.66.254 -f 'Sun Solaris 9 (SPARC)'