#!/usr/bin/python #Copyright 2008, William Stearns #Passer is a PASsive SERvice sniffer. #Home site http://www.stearns.org/passer/ #Dedicated to Mae Anne Laroche. #Released under the GPL. #======== Imports ======== import ipaddress import sys import re import string #Needed for python 2.5.2? import warnings #Needed for p0f? import unicodedata #Needed for removing control characters #This may be too restrictive. #from scapy import sniff, p0f, sr1, IP, ICMP, IPerror, TCPerror, UDPerror, ICMPerror #from scapy import * try: from scapy.all import * #Required for Scapy 2.0 and above except: from scapy import * #Scapy 1.0 #Following does not help with 'IndexError: Layer [Raw.load] not found' #from scapy.packet import Raw #Note, to get p0f working, one must: #sudo hack /usr/lib/python2.6/site-packages/scapy/modules/p0f.py #and add: #from scapy.all import * #And: #def p0f_correl(x,y): # d = 0 # # wwww can be "*" or "%nn" # #d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) #Change above line to: # d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and str(x[0]).isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) import os #Commented out p0f as it's not working at the moment #load_module("p0f") #======== Global arrays ======== #These two are used to discover servers. If we've seen a SYN go to a port, and a SYN/ACK back from it, #that's a pretty good sign it's a server. Not truly stateful, but a generally good guess. SynSentToTCPService = {} #Boolean dictionary: Have we seen a syn sent to this "IP,Proto_Port" pair yet? LiveTCPService = {} #Boolean dictionary: Have we seen a SYN/ACK come back (true) or a RST (False) from this "IP,Proto_Port" pair? #Next two are used to discover clients. If we've seen a SYN/ACK going to what appears to be a client port, and it #later responds with a FIN, we'll call that a live TCP client. SynAckSentToTCPClient = {} #Boolean dictionary: Have we seen a SYN/ACK sent to this "IP,Proto_Port" pair yet? LiveTCPClient = {} #Boolean dictionary: Have we seen a FIN from this client, indicating a 3 way handshake and successful conversation? NmapServerDescription = {} #String dictionary: What server is this "IP,Proto_Port" pair? These descriptions come from nmap-service-probes. ManualServerDescription = {} #Same as above, but locally found strings ClientDescription = {} #String dictionary: What client is on this "IP,Proto_Port"? NOTE: the port here is the _server_ port at the other end. So if #Firefox on 1.2.3.4 is making outbound connections to port 80 on remote servers, ClientDescription['1.2.3.4,TCP_80'] = "http/firefox" #Dictionary of Dictionaries of sets, replaces the specific dictionaries. First key is 2 letter record type, second key is IP address, final value (a set) is what we have seen for that record type and IP. GenDesc = {'DN': {}, 'IP': {}, 'MA': {}, 'NA': {}, 'PC': {}, 'PS': {}, 'RO': {}, 'TC': {}, 'TS': {}, 'UC': {}, 'US': {}} MacAddr = {} #String dictionary: For a given IP (key), what is its mac (value)? This is only queried and set in ReportId EtherManuf = {} #String dictionary: for a given key of the first three uppercase octets of a mac address ("00:01:0F"), who made this card? DNSRecord = {} #Dictionary of arrays of strings: For a given key of IPAddr,'A' or IPAddr,'PTR', what are it's corresponding hostname(s) (stored in an array)? HostIPs = {} #Dictionary of arrays: For a given fully qualified hostname, what IPs (array) are associated? ServiceFPs = {} #Dictionary of service fingerprints. Keys are straight int port numbers (no TCP or UDP), or 'all' for strings that need #to be matched against all ports. These are loaded from nmap's "nmap-service-probes", ignoring the probes since we're passive. #Values are lists of tuples, ala: [("Apache *server ready.", "Apache web"), ("VSFTPD FTP at your service", "vsftpd ftp")] #Note that the first object in a tuple is a _compiled regex_ rather than the printable strings I've used above. #A sample (non-compiled) version looks like: {80: [('^Server: Apache/', 'http/apachewebserver')]} SteamFriendsServers = ("69.28.148.250", "69.28.156.250", "72.165.61.161", "72.165.61.185", "72.165.61.186", "72.165.61.188", "68.142.64.164", "68.142.64.165", "68.142.64.166") KeepGoing = False #Dont change this - it's an internal value to make the code more readable. Change QuitOnShow instead. HonorQuit = True #Dont change this - it's an internal value to make the code more readable. Change QuitOnShow instead. #======== Following are primary ports we want to do a full report on ### IPv4/UDPv4/sunrpc=111 ### IPv4/UDPv4/ldap=389 ### IPv4/UDPv4/openvpn=1194 https://openvpn.net/index.php/open-source/documentation/howto.html ### IPv4/UDPv4/l2f_or_lt2p=1701 ### IPv4/UDPv4/pptp=1723 ### IPv4/UDPv4/biap-mp=1962 ### IPv4/UDPv4/rdp=3389 https://www.rdpsoft.com/blog/remote-desktop-protocol/rdp-udp-transport-azure/ ### IPv4/UDPv4/l2tp=4500 https://www.privateinternetaccess.com/helpdesk/kb/articles/what-ports-are-used-by-your-vpn-service ### IPv4/UDPv4/openvpn8080=8080 https://www.privateinternetaccess.com/helpdesk/kb/articles/what-ports-are-used-by-your-vpn-service ### IPv4/UDPv4/gotomeeting8200=8200 https://support.logmeininc.com/gotomeeting/help/optimal-firewall-configuration-g2m060010 ### IPv4/UDPv4/udp8888=8888 ### IPV4/UDPv4/hangouts https://community.arubanetworks.com/t5/Security/Configuring-Network-for-Google-Hangouts/td-p/59274 PriUDPPortNames = {"69": "tftp", "111": "sunrpc", "177": "xdmcp", "389": "ldap", "443": "udp_https", "500": "isakmp", "520": "rip", "1194": "openvpn", "1701": "l2tp1701", "1723": "pptp", "1853": "gotomeeting1853", "1962": "biap-mp", "2123": "gtp-control", "3389": "rdp", "3478": "skype3478", "3479": "skype3479", "3480": "skype3480", "3481": "skype3481", "4500": "l2tp", "6881": "bittorrent6881", "8080": "openvpn8080", "8200": "gotomeeting8200", "8888": "udp8888", "19305": "hangouts", "19306": "hangouts", "19307": "hangouts", "19308": "hangouts", "19309": "hangouts"} empty_payload_ports=('17', '19', '50174', '50597', '50902', '52498', '52576', '52620', '52775', '52956', '55180', '56089', '57347', '57563', '57694', '58034', '58153', '58861', '59024', '59413', '60463', '60799', '61016', '61651', '62473', '62915', '63137', '63556', '63571', '63878', '64727', '65154', '65251') #======== Following udp ports are low priority ones that we just log anyways ### IPv4/UDPv4/13 daytime https://gkbrk.com/wiki/daytime_protocol/ ### IPv4/UDPv4/17 qotd https://gkbrk.com/wiki/qotd_protocol/ ### IPv4/UDPv4/179 bgp ### IPv4/UDPv4/445 microsoft-ds ### IPv4/UDPv4/465 igmpv3lite https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-4015 ### IPv4/UDPv4/808 omirr/omirrd but payload looks like snmp. Hmm. ### IPv4/UDPv4/1080 does not appear to be used by socks ### IPv4/UDPv4/1099 rmiregistry ### IPv4/UDPv4/5093 sentinel-lm https://www.kb.cert.org/vuls/id/108790 ### IPv4/UDPv4/3128 assigned to squid, but not actually used by it ### IPv4/UDPv4/6000 lots of possibilities ### IPv4/UDPv4/8123 unknown udp8123 ### IPv4/UDPv4/9987 teamspeak3-voice https://www.speedguide.net/port.php?port=9987 ### IPv4/UDPv4/17185 vxworks-debug https://ics-cert.us-cert.gov/advisories/ICSA-10-214-01 ### IPv4/UDPv4/30718 lantronix https://www.slideshare.net/kost/vk-exploringtreasuresof77-fehlantronixconfidence2014 ### IPv4/UDPv4/47808 bacnet https://wiki.wireshark.org/Protocols/bacnet ### IPv4/UDPv4/44818 rockwell-encap http://literature.rockwellautomation.com/idc/groups/literature/documents/qr/comm-qr001_-en-e.pdf , https://ics-cert.us-cert.gov/advisories/ICSA-13-011-03 SecUDPPortNames = {"7": "echo", "13": "daytime", "17": "qotd", "19": "chargen", "179": "bgp", "192": "osu-nms", "445": "microsoft-ds", "465": "igmpv3lite", "623": "asf-rmcp_or_ipmi", "808": "omirr", "1080": "udpsocks", "1099": "rmiregistry", "1604": "darkcomet_rat_winframe_icabrowser", "3128": "udpsquid", "3283": "udp3283", "3386": "udp3386", "4738": "udp4738", "4800": "udp4800", "5006": "udp5006", "5008": "udp5008", "5093": "sentienl-lm", "5354": "mdnsresponder", "5632": "pcanywherestat", "6000": "udp6000", "6969": "acmsoda", "6970": "rtsp", "8123": "udp8123", "8301": "udp8301", "8302": "udp8302", "9050": "udp9050", "9600": "udp9600", "9987": "teamspeak3-voice", "16464": "udp16464", "17185": "vxworks-debug", "20000": "udp20000", "24223": "udp24223", "30718": "lantronix", "32015": "udp32015", "32764": "udp32764", "32770": "udp32770", "34436": "udp34436", "44818": "rockwell-encap", "46414": "udp46414", "47808": "bacnet", "50023": "udp50023", "51413": "transmission", "53007": "udp53007", "55020": "udp55020", "63520": "udp63520", "64211": "udp64211"} halflife_altport = ("1265", "20100", "21550", "27000", "27017", "27018", "27019", "27022", "27030", "27035", "27050", "27078", "27080", "28015", "28100", "45081") #For all of the following, see if the payload contains snmp. ### IPv4/UDPv4/ftp=21 ssh=22 telnet=23 smtp=25 tacacs=49 http=80 iso-tsap=102 pop3=110 imap=143 igmpv3lite=465 ldaps=636 omirr=808 telnets=992 imaps=993 pop3s=995 client snmp_altport = ("21", "22", "23", "25", "49", "80", "102", "110", "143", "465", "636", "808", "992", "993", "995") sip_altport = ("1000", "1024", "1025", "1030", "1031", "1032", "1033", "1034", "1035", "1036", "1037", "1038", "1039", "1040", "1050", "1111", "1560", "2000", "2020", "2030", "2050", "2222", "3000", "3020", "3040", "3050", "3333", "4000", "4040", "4050", "4060", "4444", "4880", "5000", "5010", "5011", "5015", "5020", "5022", "5025", "5030", "5033", "5035", "5040", "5044", "5045", "5050", "5055", "5060", "5061", "5062", "5063", "5064", "5065", "5066", "5067", "5068", "5069", "5070", "5071", "5072", "5073", "5074", "5075", "5076", "5077", "5078", "5079", "5080", "5081", "5082", "5083", "5084", "5085", "5086", "5087", "5088", "5089", "5090", "5091", "5092", "5095", "5096", "5097", "5098", "5099", "5100", "5160", "5166", "5510", "5520", "5530", "5540", "5550", "5555", "5560", "5570", "5580", "5590", "5600", "6010", "6011", "6015", "6020", "6022", "6025", "6030", "6033", "6035", "6040", "6044", "6045", "6050", "6055", "6060", "6065", "6066", "6070", "6075", "6077", "6080", "6082", "6085", "6088", "6089", "6090", "6095", "6099", "6500", "6590", "6610", "6620", "6630", "6640", "6650", "6660", "6666", "6670", "6680", "6690", "7000", "7060", "7070", "7080", "7090", "7160", "7170", "7777", "7780", "8000", "8060", "8070", "8090", "8160", "8190", "8890", "9000", "9070", "9080", "9090", "9099", "9160", "9988", "9999", "10000", "11111", "15060", "15160", "15260", "19060", "20000", "22222", "25060", "25160", "25260", "25270", "30000", "33333", "35060", "35160", "35360", "35370", "40000", "44444", "45060", "45070", "45160", "45170", "45670", "45770", "50000", "50600", "50601", "50602", "50603", "50604", "50605", "50606", "50607", "50608", "50609", "50845", "55051", "55060", "55070", "55160", "55555", "56060", "58070", "58080", "60000", "65060", "65476") #See "Reference ID (refid)" in https://www.ietf.org/rfc/rfc5905.txt known_ntp_refs = ('1PPS', 'ACTS', 'ATOM', 'BCS', 'CDMA', 'CHU', 'CTD', 'DCF', 'DCFP', 'DCFa', 'DCFp', 'DCFs', 'GAL', 'GCC', 'GNSS', 'GOES', 'GPS', 'GPS1', 'GPSD', 'GPSm', 'GPSs', 'GOOG', 'HBG', 'INIT', 'IRIG', 'JJY', 'kPPS', 'LOCL', 'LORC', 'MRS', 'MSF', 'MSL', 'NICT', 'NIST', 'NMC1', 'NMEA', 'NTS', 'OCXO', 'ONBR', 'PPS', 'PPS0', 'PPS1', 'PTB', 'PTP', 'PZF', 'RATE', 'ROA', 'SHM', 'SLK', 'SOCK', 'STEP', 'TAC', 'TDF', 'TRUE', 'UPPS', 'USIQ', 'USNO', 'UTC', 'WWV', 'WWVB', 'WWVH', 'XMIS', 'shm0', '', None) no_warn_name_tails = ('64-ptr.not.set.', '.adsl.', 'ptr-not-configured.cloudatcost.', '-bj-cnc.', '.ha.cnc.', 'domain.not.configured.', '.cto-go-a1k-01.', '.cust.', '.dedicated.', '.dhcp.', '.dsl.', '.fixed.', '.gnace701.', '.gnace702.', '.gnace703.', '.gnace704.', '.adsl-surfen.hetnet.', '.home.', '.hosted.', '.iplocal.', 'ipv6.', '.kdca.', '.lan.', 'localdomain.', 'localhost.', '.muc.', '.nvi.', 'hosted.by.pcextreme.', '.cust.dsl.teletu.', '.cust.vodafonedsl.') stream_ihs_discovery_header = 'FFFFFFFF214C5FA0'.decode('hex') www163com_payload = '03'.decode('hex') + "www" + '03'.decode('hex') + "163" + '03'.decode('hex') + "com" #\x03www\x03163\x03com meet_ports = ('19302', '19303', '19304', '19305', '19306', '19307', '19308', '19309') #https://support.google.com/a/answer/7582935?hl=en meet_hosts = ('2607:f8b0:4002:c08::7f', '2607:f8b0:400c:c00::7f', '2a00:1450:4013:c03::7f', '2a00:1450:400c:c08::7f', '2800:3f0:4003:c00::7f', '2a00:1450:400c:c08::7f', '2607:f8b0:4002:c07::7f', '64.233.186.127', '74.125.140.127', '74.125.143.127', '74.125.196.127', '74.125.134.127', '209.85.232.127', '209.85.232.127', '2a00:1450:4010:c01::7f', '209.85.232.127', '2607:f8b0:400d:c0d::7f', "66.102.1.127", "64.233.165.127", "74.125.200.127", "2a00:1450:400c:c06::7f") skype_ports = ('21105', '21546', '22795', '23353', '24484', '26079', '27252', '27944') skype_hosts = ('52.179.141.141', '100.112.42.45') a0_string = 'A' + '00'.decode('hex') Devel = True QuitOnShow = False SaveUnhandledAcks = False ShowCredentials = False #If True , we'll include passwords in the output lines. At the time of this writing, only the snmp community string is logged when True ReportNXDomain = False #If True , we'll output a line for items that come back with NXDOMAIN dns answers too. #For my internal use to look for new service strings #This payload logging is disabled when Devel == False #Quite likely a security risk, I don't recommend enabling it. ServerPayloadDir = '/var/tmp/passer-server/' ClientPayloadDir = '/var/tmp/passer-client/' passerVersion = "2.07" #======== Functions ======== def Debug(DebugStr): """Prints a note to stderr""" if Devel != False: sys.stderr.write(DebugStr + '\n') def ShowPacket(orig_packet, banner_string, quit_override_preference): """In development mode, displays details about an unknown packet, and exits if QuitOnShow (above) is True.""" UnhandledPacket(orig_packet) if Devel != False: Debug("======== " + str(banner_string)) Debug(str(orig_packet.show(dump=True))) ls(orig_packet) #This one's still spitting to stdout, not sure how to redirect to stderr Debug(str(orig_packet.answers)) Debug("Packet type: " + str(type(orig_packet))) if quit_override_preference and QuitOnShow: #quit_override_preference is either KeepGoing == false or HonorQuit == True quit() def remove_control_characters(s): """Strip out any control characters in the string.""" return "".join(ch for ch in unicode(s) if unicodedata.category(ch)[0]!="C") def ReturnLayers(p): """Return the layers in this packet from outer to inner. Sample use: list(ReturnLayers(p))""" yield p.name while p.payload: p = p.payload yield p.name #FIXME - remove this function def LogNewPayload(PayloadDir, PayloadFile, Payload): """Saves the payload from an ack packet to a file named after the server or client port involved.""" #Better yet, wrpcap("/path/to/pcap", list_of_packets) global Devel if Devel: if os.path.isdir(PayloadDir): if not Payload == "None": pfile = open(PayloadFile, 'a') pfile.write(Payload) pfile.close() def UnhandledPacket(Packet): """Save packets that have not been (completely) processed out to a pcap file for later analysis""" global UnhandledFile if UnhandledFile != None: UnhandledFile.write(Packet) def LoadMacData(MacFile): """Load Ethernet Mac address prefixes from standard locations (from ettercap, nmap, wireshark, and/or arp-scan).""" global EtherManuf More = '' if len(EtherManuf) > 0: More = ' more' LoadCount = 0 if os.path.isfile(MacFile): try: MacHandle = open(MacFile, 'r') for line in MacHandle: if (len(line) >= 8) and (line[2] == ':') and (line[5] == ':'): #uppercase incoming strings just in case one of the files uses lowercase MacHeader = line[:8].upper() Manuf = line[8:].strip() if not EtherManuf.has_key(MacHeader): EtherManuf[MacHeader] = Manuf LoadCount += 1 elif (len(line) >= 7) and (re.search('^[0-9A-F]{6}[ \t]', line) is not None): MacHeader = str.upper(line[0:2] + ':' + line[2:4] + ':' + line[4:6]) Manuf = line[7:].strip() if not EtherManuf.has_key(MacHeader): EtherManuf[MacHeader] = Manuf LoadCount += 1 MacHandle.close() if EtherManuf.has_key('00:00:00'): del EtherManuf['00:00:00'] #Not really Xerox LoadCount -= 1 Debug(str(LoadCount) + More + " mac prefixes loaded from " + str(MacFile)) return True except: Debug("Unable to load " + str(MacFile)) return False else: Debug("Unable to load " + str(MacFile)) return False def LoadNmapServiceFP(ServiceFileName): """Load nmap fingerprints from nmap-service-probes, usually in /usr/share/nmap.""" #File format details at http://nmap.org/vscan/vscan-fileformat.html global ServiceFPs LoadCount = 0 CompileSuccess = 0 CompileFail = 0 PortArray = [] if os.path.isfile(ServiceFileName): try: ServiceHandle = open(ServiceFileName, "r") for line in ServiceHandle: if (len(line) >= 5) and (line[0:6] == "Probe "): #print "==== PROBE ====" PortArray = [] #print len(PortArray), PortArray #len of empty array is 0 elif (len(line) >= 5) and (line[0:6] == "match "): #print "match" #print line InformationPresent = True #Sample line: # match srun m|^X\0\0\0$| p/Caucho Resin JSP Engine srun/ Remainder = line[6:].strip() # srun m|^X\0\0\0$| p/Caucho Resin JSP Engine srun/ MatchStart = Remainder.find(" m") # 4 ProtoString = Remainder[:MatchStart].replace(',', ';') # srun #At the moment, nmap-service-probes uses these separators: #3 m%, 2 m+, 126 m/, 29 m=, 2 m@, and 3509 m| #No flags on %, +, #Only flags should be "i" (case-insensitive) and "s" ("." can match newline) Separator = Remainder[MatchStart+2:MatchStart+3] # | MatchEnd = Remainder.find(Separator, MatchStart+3) # 16 MatchString = Remainder[MatchStart+3:MatchEnd] # ^X\0\0\0$ #Handle an "i" or "s" flag after separator #Debug("==== " + Remainder[MatchEnd+1:MatchEnd+4]) if MatchEnd + 1 == len(Remainder): InformationPresent = False #Debug("No information data for " + MatchString) elif Remainder[MatchEnd+1:MatchEnd+2] == " ": PPointer = MatchEnd + 2 MatchFlags = re.M #Debug(Remainder + ", no flags") elif Remainder[MatchEnd+1:MatchEnd+3] == "i ": PPointer = MatchEnd + 3 MatchFlags = re.M | re.I #Debug(Remainder + ", i flag") elif Remainder[MatchEnd+1:MatchEnd+3] == "s ": PPointer = MatchEnd + 3 MatchFlags = re.M | re.S #Debug(Remainder + ", s flag") elif (Remainder[MatchEnd+1:MatchEnd+4] == "is ") or (Remainder[MatchEnd+1:MatchEnd+4] == "si "): PPointer = MatchEnd + 4 MatchFlags = re.M | re.I | re.S #Debug(Remainder + ", i and s flag") #Following lines commented out as they're only needed for development #else: # Debug("Unrecognized nmap-service-probes flag combination") # Debug(str(MatchEnd + 1) + " " + str(len(Remainder))) # Debug(Remainder + ", unknown flags") # #quit() #Substitute ; for , in ProtoString and ServerDescription since we're using commas as field delimiters in output ServerDescription = Remainder[PPointer:].replace(',', ';') # p/Caucho Resin JSP Engine srun/ #The nmap-service-probes file uses a character set ("[...]") issue that python doesn't like. #If a "-" is used inside a character set, it should either be in the first or last position, #or used in a character range ("[.....a-z.....]"). The following move any dashes to first or #last position so re.compile is happy. MatchString = MatchString.replace("[\w-", "[-\w") #The dash needs to be at the end or it's treated as a range specifier MatchString = MatchString.replace("[\d-", "[-\d") #same MatchString = MatchString.replace("[\w\d-_.]", "[\w\d_.-]") #and so on... MatchString = MatchString.replace("[\w\d-_]", "[\w\d_-]") MatchString = MatchString.replace("[.-\w]", "[.\w-]") MatchString = MatchString.replace("[\s-\w.,]", "[\s\w.,-]") MatchString = MatchString.replace("[\w\d-.]", "[\w\d.-]") MatchString = MatchString.replace("[\d\.-\w]", "[\d\.\w-]") MatchString = MatchString.replace("[^-_A-Z0-9]", "[^_A-Z0-9-]") MatchString = MatchString.replace("[^-A-Z0-9]", "[^A-Z0-9-]") #If you get a rule that mismatches, find its "match" line in nmap-service-probes and pull out the "p/..../' signature name. #Copy an "elif..." section below and put the signature name (without "p/" and "/") inside the quotes after .find . if ServerDescription.find('Skype VoIP data channel') > -1: #This "14 bytes of random stuff" signature way misfires. pass elif ServerDescription.find('Microsoft Distributed Transaction Coordinator') > -1: #This "ERROR" signature matches other protocols. pass elif ServerDescription.find('Erlang Distribution Node') > -1: #This signature mismatches. pass elif ServerDescription.find('Lotus Domino Console') > -1: #This signature mismatches. pass elif ServerDescription.find('LANDesk remote management') > -1: #This signature mismatches. pass elif not InformationPresent: #There's a regex match, but no information about, skip. pass else: try: #We try to compile the MatchString now before inserting into ServiceFPs so the work only needs to be #done once. If this fails we fall down to the except and simply don't use the tuple. #Originally 413 out of 3671 match lines failed to compile because of "-" placement in character sets. #The problem, and a fixed version, have been reported to the nmap developers. #The use of "str" seems redundant, but we have occasionally gotten: #line 511: OutputDescription = OneTuple[1] #TypeError: expected a character buffer object SearchTuple = (re.compile(MatchString, MatchFlags), str(ProtoString + "://" + ServerDescription)) CompileSuccess += 1 if len(PortArray) == 0: #No ports declared yet; we'll place this search pair under the special port "all" if not ServiceFPs.has_key('all'): ServiceFPs['all'] = [] ServiceFPs['all'].append(SearchTuple) LoadCount += 1 else: #Register this search pair for every port requested for OnePort in PortArray: if not ServiceFPs.has_key(int(OnePort)): ServiceFPs[int(OnePort)] = [] ServiceFPs[int(OnePort)].append(SearchTuple) LoadCount += 1 except: #print "Failed to compile " + MatchString CompileFail += 1 elif (len(line) >= 5) and (line[0:6] == "ports "): PortArray = [] RawPortsString = line[6:].strip() #print "ports are ", RawPortsString for PortBlock in RawPortsString.split(","): #Each PortBlock is either an individual port or port range if PortBlock.find("-") > -1: #We have a port range PortRange = PortBlock.split("-") for OnePort in range(int(PortRange[0]), int(PortRange[1]) + 1): PortArray.append(OnePort) else: PortArray.append(PortBlock) #print len(PortArray), PortArray elif (len(line) >= 9) and (line[0:10] == "softmatch "): pass #softmatches look very weak at the moment; none give a productname. Skip for the moment. #print "softmatch" ServiceHandle.close() if CompileFail == 0: Debug(str(CompileSuccess) + " nmap service signatures successfully loaded.") else: Debug(str(CompileSuccess) + " nmap service signatures successfully loaded, unable to parse " + str(CompileFail) + " others.") return True except: Debug("Failed to load " + ServiceFileName) return False else: Debug("Unable to find " + ServiceFileName) return False def ip_addr_obj(raw_addr): """Returns an ip obj for the input string. The raw_addr string should already have leading and trailing whitespace removed before being handed to this function.""" try: if sys.version_info > (3, 0): raw_addr_string = str(raw_addr) else: raw_addr_string = unicode(raw_addr) except UnicodeDecodeError: raw_addr_string = '' #if Devel: # Debug('Cannot convert:' # Debug(raw_addr) # raise #else: # pass ip_obj = None if raw_addr_string != '' and not raw_addr_string.endswith(('.256', '.257', '.258', '.259', '.260')): #raw_addr_string.find('.256') == -1 try: ip_obj = ipaddress.ip_address(raw_addr_string) except ValueError: #See if it's in 2.6.0.0.9.0.0.0.5.3.0.1.B.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1 or 260090005301B7000000000000000001 format hex_string = raw_addr_string.replace('.', '') colon_hex_string = hex_string[0:4] + ':' + hex_string[4:8] + ':' + hex_string[8:12] + ':' + hex_string[12:16] + ':' + hex_string[16:20] + ':' + hex_string[20:24] + ':' + hex_string[24:28] + ':' + hex_string[28:32] try: ip_obj = ipaddress.ip_address(colon_hex_string) except ValueError: if Devel: Debug('IP Conversion problem with:') Debug(raw_addr_string) raise else: pass return ip_obj def explode_ip(ip_obj): """Converts the input IP object to its exploded form (type "unicode" in python2) ready for printing. If the IP/IP object is invalid, returns an empty string.""" if ip_obj is None: return '' else: return ip_obj.exploded def extract_len_string(len_encoded_string): """Assumes byte 0 is a length, followed by a string of that length (1-255 bytes). Returns that string and the remainder of the len_enocded_string after the first string has been removed. Example call: cpu, remainder = extract_len_string(hinfo_payload) gives back "ARMV7L" and "\x05LINUX" for another round of extraction.""" ret_str = '' str_len = ord(len_encoded_string[0]) if str_len > 0: ret_str = str(len_encoded_string[1:str_len+1]) string_tail = len_encoded_string[str_len+1:] return (ret_str, string_tail) #def mac_of_ipaddr(ipv6addr): # """For a supplied IPv6 address in EUI-64 format, return the mac address of the system that's behind it. For an address not in that format, return ''.""" def RememberDNS(IPAddr, Hostname, RecType): """Remember dns objects in DNSRecord and HostIPs. RecType is 'A', 'AAAA', 'PTR', or 'CNAME'.""" global DNSRecord global HostIPs if (Hostname == '') or (IPAddr == '::'): return if not DNSRecord.has_key(IPAddr + "," + RecType): #If we haven't seen this hostname for this IPAddr, DNSRecord[IPAddr + "," + RecType] = [Hostname] #make an array with just this hostname elif not Hostname in DNSRecord[IPAddr + "," + RecType]: #If we _do_ have existing hostnames for this IP, but this new Hostname isn't one of them DNSRecord[IPAddr + "," + RecType].append(Hostname) #Add this Hostname to the list if not HostIPs.has_key(Hostname): if not isFQDN(Hostname): #We don't want to remember ips for names like "www", "ns1.mydom", "localhost", etc. return HostIPs[Hostname] = [] #else: #Since we've found "Hostname" as a key, we don't need to check if it's an FQDN again, we already checked once. if not IPAddr in HostIPs[Hostname]: #If we haven't seen this IP address for this hostname, HostIPs[Hostname].append(IPAddr) #Remember this new IP address for this hostname. def ReportId(Type, CompressedIPAddr, Proto, State, Description): """Print and log a new piece of network information.""" #Can't use : for separator, IPv6, similarly '.' for ipv4 #Can't use "/" because of filesystem #Don't want to use space because of filesystem # Type, IPAddr, Proto State Optional description (may be empty) # 'IP', IPaddr, 'IP', dead or live, p0f OS description # 'MA', IPaddr, 'Ethernet', MacAddr, ManufDescription # 'TC', IPaddr, 'TCP_'Port, closed or open, client description # 'TS', IPaddr, 'TCP_'Port, closed or listening, server description # 'UC', IPaddr, 'UDP_'Port, open or closed, udp client port description # 'US', IPaddr, 'UDP_'Port, open or closed, udp server port description # 'DN', IPaddr, 'A' or 'PTR', hostname, possible extra info # 'RO', IPaddr, 'TTLEx', router, possible extra info # 'PC', IPaddr, 'PROTO_'PNum open, protocol name # 'PS', IPaddr, 'PROTO_'PNum open, protocol name global ServerDescription global ClientDescription global MacAddr global EtherManuf global LogFile global GenDesc IPAddr = explode_ip(ip_addr_obj(CompressedIPAddr)) Location = IPAddr + "," + Proto Description = Description.replace('\n', '').replace('\r', '').replace(',', ' ') ShouldPrint = True if not GenDesc.has_key(Type): GenDesc[Type] = {} if Type == "TS": pass #Only assign this (and the others in this function) if Description non-null #if (Description != ''): # #FIXME - assign externally to the right *ServerDescription # ServerDescription[Location] = Description elif Type == "TC": if Description != '': ClientDescription[Location] = Description elif Type == "US": if not GenDesc[Type].has_key(Location): GenDesc[Type][Location] = set() if State + ',' + Description in GenDesc[Type][Location]: ShouldPrint = False #Don't print if we've already printed it with this state + description else: GenDesc[Type][Location].add(State + ',' + Description) if Description != '': ManualServerDescription[Location] = Description elif Type == "UC": if not GenDesc[Type].has_key(Location): GenDesc[Type][Location] = set() if State + ',' + Description in GenDesc[Type][Location]: ShouldPrint = False #Don't print if we've already printed it with this state + description else: GenDesc[Type][Location].add(State + ',' + Description) if Description != '': ClientDescription[Location] = Description elif Type in ("IP", "NA", "PC", "PS"): if not GenDesc[Type].has_key(Location): GenDesc[Type][Location] = set() if State + ',' + Description in GenDesc[Type][Location]: ShouldPrint = False #Don't print if we've already printed it with this state + description else: GenDesc[Type][Location].add(State + ',' + Description) elif Type == "DN": #FIXME - perhaps description could indicate low TTL? <300? <150? if Proto in ('A', 'AAAA', 'CNAME', 'PTR') and State == '': ShouldPrint = False elif DNSRecord.has_key(Location) and State in DNSRecord[Location]: ShouldPrint = False else: RememberDNS(IPAddr, State, Proto) elif Type == "RO": if Description == '': description_string = Proto #This holds the type of packet that causes us to believe it's a router, like "RouterAdv" else: description_string = Description if not GenDesc[Type].has_key(IPAddr): #If we ever need to test if an IP is a router, use GenDesc['RO'].has_key(IPAddr) GenDesc[Type][IPAddr] = set() if description_string in GenDesc[Type][IPAddr]: ShouldPrint = False #Don't print if we've already printed it with this description else: GenDesc[Type][IPAddr].add(description_string) elif Type == "MA": State = State.upper() if IPAddr in ('', '::', '0000:0000:0000:0000:0000:0000:0000:0000'): ShouldPrint = False #Not registering :: as a null IP address elif (MacAddr.has_key(IPAddr)) and (MacAddr[IPAddr] == State): ShouldPrint = False #Already known, no need to reprint else: MacAddr[IPAddr] = State if EtherManuf.has_key(State[:8]): Description = EtherManuf[State[:8]].replace(',', ' ') if ShouldPrint: try: OutString = Type + "," + IPAddr + "," + Proto + "," + State + "," + Description print(OutString) if LogFile != None: LogFile.write(OutString + '\n') LogFile.flush() except UnicodeDecodeError: pass def isFQDN(Hostname): """Boolean function: Checks to se if a hostname ends in a TLD. Not a strict check, just some quick checks.""" #https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains #'..', if len(Hostname) < 5: #Shortest I can think of is "h.uk.", technically a domain, but still a dns object Debug("Hostname " + Hostname + " too short, ignoring.") return False elif not Hostname.endswith('.'): Debug("Hostname " + Hostname + "doesn't end in '.', ignoring.") return False elif len(Hostname) >= 6 and Hostname.endswith(('.aaa.', '.abb.', '.abc.', '.aig.', '.art.', '.aws.', '.axa.', '.bar.', '.bet.', '.bid.', '.bio.', '.biz.', '.bot.', '.box.', '.bzh.', '.cab.', '.cat.', '.cbs.', '.ceo.', '.com.', '.dev.', '.dog.', '.eco.', '.edu.', '.eus.', '.fit.', '.frl.', '.fun.', '.fyi.', '.gal.', '.gdn.', '.gop.', '.gov.', '.hiv.', '.hot.', '.hkt.', '.ibm.', '.ink.', '.int.', '.ist.', '.jio.', '.jmp.', '.jnj.', '.jot.', '.kim.', '.krd.', '.law.', '.lds.', '.llc.', '.lol.', '.ltd.', '.men.', '.mil.', '.moe.', '.net.', '.ngo.', '.now.', '.nrw.', '.ntt.', '.nyc.', '.one.', '.ong.', '.onl.', '.org.', '.ovh.', '.pet.', '.pro.', '.pub.', '.pwc.', '.red.', '.rio.', '.rip.', '.run.', '.scb.', '.sex.', '.ski.', '.srl.', '.tel.', '.thd.', '.top.', '.uno.', '.uol.', '.vet.', '.vip.', '.win.', '.wow.', '.wtc.', '.wtf.', '.xin.', '.xxx.', '.xyz.', '.you.', '.aco.', '.ads.', '.aeg.', '.afl.', '.anz.', '.aol.', '.app.', '.bbc.', '.bbt.', '.bcg.', '.bcn.', '.bms.', '.bmw.', '.bnl.', '.bom.', '.boo.', '.buy.', '.cal.', '.cam.', '.car.', '.cba.', '.cbn.', '.ceb.', '.cfa.', '.cfd.', '.crs.', '.csc.', '.dad.', '.day.', '.dds.', '.dhl.', '.diy.', '.dnp.', '.dot.', '.dtv.', '.dvr.', '.eat.', '.esq.', '.fan.', '.fly.', '.foo.', '.fox.', '.ftr.', '.gap.', '.gea.', '.gle.', '.gmo.', '.gmx.', '.goo.', '.got.', '.hbo.', '.how.', '.htc.', '.ice.', '.icu.', '.ifm.', '.ing.', '.itv.', '.iwc.', '.jcb.', '.jcp.', '.jlc.', '.jll.', '.joy.', '.kfh.', '.kia.', '.kpn.', '.lat.', '.lpl.', '.man.', '.mba.', '.mcd.', '.med.', '.meo.', '.mit.', '.mlb.', '.mls.', '.mma.', '.moi.', '.mom.', '.mov.', '.msd.', '.mtn.', '.mtr.', '.nab.', '.nba.', '.nec.', '.new.', '.nfl.', '.nhk.', '.nra.', '.obi.', '.off.', '.ooo.', '.ott.', '.pay.', '.pid.', '.pin.', '.pnc.', '.pru.', '.qvc.', '.ren.', '.ril.', '.rwe.', '.sap.', '.sas.', '.sbi.', '.sbs.', '.sca.', '.ses.', '.sew.', '.sfr.', '.sky.', '.soy.', '.srt.', '.stc.', '.tab.', '.tax.', '.tci.', '.tdk.', '.tjx.', '.trv.', '.tui.', '.tvs.', '.ubs.', '.ups.', '.vig.', '.vin.', '.wed.', '.wme.', '.yun.', '.zip.')): return True elif len(Hostname) >= 7 and Hostname.endswith(('.BIND.', '.aarp.', '.able.', '.aero.', '.aigo.', '.amex.', '.arab.', '.arpa.', '.asia.', '.audi.', '.baby.', '.band.', '.bank.', '.beer.', '.best.', '.bike.', '.blog.', '.blue.', '.buzz.', '.cafe.', '.call.', '.camp.', '.care.', '.casa.', '.chat.', '.city.', '.club.', '.cool.', '.coop.', '.date.', '.diet.', '.fail.', '.fans.', '.farm.', '.fido.', '.film.', '.fish.', '.food.', '.free.', '.game.', '.gent.', '.gift.', '.gmbh.', '.golf.', '.guru.', '.haus.', '.help.', '.host.', '.hsbc.', '.ieee.', '.info.', '.itau.', '.jobs.', '.kiwi.', '.land.', '.lgbt.', '.life.', '.link.', '.live.', '.loan.', '.love.', '.ltda.', '.menu.', '.mobi.', '.moda.', '.name.', '.news.', '.nike.', '.pics.', '.pink.', '.plus.', '.porn.', '.prod.', '.qpon.', '.rent.', '.rest.', '.ruhr.', '.sale.', '.scot.', '.sexy.', '.shop.', '.show.', '.sina.', '.site.', '.surf.', '.taxi.', '.team.', '.tech.', '.test.', '.tips.', '.town.', '.vote.', '.wang.', '.wien.', '.wiki.', '.wine.', '.work.', '.yoga.', '.zara.', '.zero.', '.zone.', '.adac.', '.akdn.', '.ally.', '.army.', '.arte.', '.asda.', '.auto.', '.bbva.', '.bing.', '.bofa.', '.bond.', '.book.', '.cars.', '.case.', '.cash.', '.cbre.', '.cern.', '.citi.', '.cyou.', '.data.', '.dclk.', '.deal.', '.dell.', '.desi.', '.dish.', '.docs.', '.doha.', '.duck.', '.duns.', '.dvag.', '.erni.', '.fage.', '.fast.', '.fiat.', '.fire.', '.flir.', '.ford.', '.fund.', '.gbiz.', '.ggee.', '.gold.', '.goog.', '.guge.', '.hair.', '.hdfc.', '.here.', '.hgtv.', '.icbc.', '.imdb.', '.immo.', '.java.', '.jeep.', '.jprs.', '.kddi.', '.kpmg.', '.kred.', '.lego.', '.lidl.', '.like.', '.limo.', '.loft.', '.luxe.', '.maif.', '.meet.', '.meme.', '.mini.', '.mint.', '.moto.', '.mtpc.', '.navy.', '.next.', '.nico.', '.ollo.', '.open.', '.page.', '.pars.', '.pccw.', '.ping.', '.play.', '.pohl.', '.post.', '.prof.', '.raid.', '.read.', '.reit.', '.rich.', '.rmit.', '.room.', '.rsvp.', '.safe.', '.sapo.', '.sarl.', '.save.', '.saxo.', '.scor.', '.seat.', '.seek.', '.shaw.', '.shia.', '.silk.', '.skin.', '.sncf.', '.sohu.', '.song.', '.sony.', '.spot.', '.star.', '.talk.', '.teva.', '.tiaa.', '.toys.', '.tube.', '.vana.', '.visa.', '.viva.', '.vivo.', '.voto.', '.weir.', '.xbox.')): return True elif len(Hostname) >= 8 and Hostname.endswith(('.archi.', '.audio.', '.bingo.', '.black.', '.bosch.', '.build.', '.cisco.', '.click.', '.cloud.', '.coach.', '.codes.', '.cymru.', '.deals.', '.delta.', '.dodge.', '.earth.', '.edeka.', '.email.', '.faith.', '.fedex.', '.games.', '.green.', '.group.', '.gucci.', '.guide.', '.homes.', '.horse.', '.house.', '.hyatt.', '.irish.', '.iveco.', '.jetzt.', '.koeln.', '.kyoto.', '.legal.', '.lilly.', '.local.', '.lotto.', '.media.', '.miami.', '.money.', '.movie.', '.ninja.', '.nokia.', '.onion.', '.osaka.', '.paris.', '.parts.', '.party.', '.photo.', '.pizza.', '.place.', '.poker.', '.press.', '.promo.', '.rocks.', '.rugby.', '.shoes.', '.solar.', '.space.', '.sport.', '.store.', '.study.', '.style.', '.sucks.', '.swiss.', '.tatar.', '.tirol.', '.today.', '.tokyo.', '.tools.', '.tours.', '.trade.', '.trust.', '.tushu.', '.vegas.', '.video.', '.wales.', '.watch.', '.weibo.', '.works.', '.world.', '.yahoo.', '.zippo.', '.actor.', '.adult.', '.aetna.', '.amfam.', '.amica.', '.apple.', '.autos.', '.azure.', '.baidu.', '.beats.', '.bible.', '.boats.', '.boots.', '.canon.', '.cards.', '.chase.', '.cheap.', '.chloe.', '.citic.', '.crown.', '.dabur.', '.dance.', '.drive.', '.dubai.', '.epost.', '.epson.', '.final.', '.forex.', '.forum.', '.gallo.', '.gifts.', '.gives.', '.glade.', '.glass.', '.globo.', '.gmail.', '.gripe.', '.honda.', '.ikano.', '.intel.', '.lamer.', '.lease.', '.lexus.', '.linde.', '.lipsy.', '.lixil.', '.loans.', '.locus.', '.lotte.', '.lupin.', '.macys.', '.mango.', '.mopar.', '.nadex.', '.nexus.', '.nikon.', '.nowtv.', '.omega.', '.phone.', '.praxi.', '.prime.', '.quest.', '.radio.', '.rehab.', '.reise.', '.ricoh.', '.rodeo.', '.salon.', '.sener.', '.seven.', '.sharp.', '.shell.', '.skype.', '.sling.', '.smart.', '.smile.', '.stada.', '.tires.', '.tmall.', '.toray.', '.total.', '.tunes.', '.ubank.', '.vista.', '.vodka.', '.volvo.', '.weber.', '.xerox.')): return True elif len(Hostname) >= 9 and Hostname.endswith(('.abarth.', '.abbott.', '.abbvie.', '.agency.', '.author.', '.bayern.', '.berlin.', '.casino.', '.center.', '.church.', '.clinic.', '.coffee.', '.condos.', '.coupon.', '.dating.', '.dealer.', '.degree.', '.dental.', '.design.', '.energy.', '.estate.', '.events.', '.expert.', '.family.', '.global.', '.google.', '.gratis.', '.health.', '.hermes.', '.hiphop.', '.hockey.', '.hotels.', '.hughes.', '.insure.', '.intuit.', '.joburg.', '.kaufen.', '.lawyer.', '.london.', '.luxury.', '.market.', '.mattel.', '.mobile.', '.monash.', '.moscow.', '.museum.', '.natura.', '.online.', '.photos.', '.quebec.', '.racing.', '.realty.', '.repair.', '.report.', '.review.', '.rogers.', '.school.', '.social.', 'stream.', '.studio.', '.supply.', '.sydney.', '.taipei.', '.tattoo.', '.tienda.', '.travel.', '.viajes.', '.vision.', '.voting.', '.webcam.', '.yandex.', '.active.', '.africa.', '.airbus.', '.airtel.', '.alipay.', '.alsace.', '.alstom.', '.anquan.', '.aramco.', '.beauty.', '.bharti.', '.blanco.', '.bostik.', '.boston.', '.broker.', '.camera.', '.career.', '.caseih.', '.chanel.', '.chrome.', '.circle.', '.claims.', '.comsec.', '.credit.', '.cruise.', '.datsun.', '.direct.', '.doctor.', '.dunlop.', '.dupont.', '.durban.', '.emerck.', '.flickr.', '.futbol.', '.gallup.', '.garden.', '.george.', '.giving.', '.imamat.', '.jaguar.', '.juegos.', '.kinder.', '.kindle.', '.kosher.', '.lancia.', '.latino.', '.lefrak.', '.living.', '.locker.', '.madrid.', '.maison.', '.makeup.', '.mobily.', '.mormon.', '.mutual.', '.nagoya.', '.nissan.', '.nissay.', '.norton.', '.nowruz.', '.office.', '.olayan.', '.oracle.', '.orange.', '.otsuka.', '.pfizer.', '.physio.', '.piaget.', '.pictet.', '.reisen.', '.rocher.', '.ryukyu.', '.safety.', '.sakura.', '.sanofi.', '.schule.', '.secure.', '.select.', '.shouji.', '.soccer.', '.suzuki.', '.swatch.', '.taobao.', '.target.', '.tennis.', '.tjmaxx.', '.tkmaxx.', '.toyota.', '.unicom.', '.viking.', '.villas.', '.virgin.', '.voyage.', '.vuelos.', '.walter.', '.warman.', '.xihuan.', '.xperia.', '.yachts.', '.zappos.')): return True elif len(Hostname) >= 10 and Hostname.endswith(('.abogado.', '.academy.', '.audible.', '.bugatti.', '.capital.', '.caravan.', '.college.', '.cologne.', '.comcast.', '.company.', '.cooking.', '.country.', '.coupons.', '.cricket.', '.cruises.', '.digital.', '.domains.', '.exposed.', '.express.', '.fashion.', '.ferrari.', '.flights.', '.frogans.', '.gallery.', '.hamburg.', '.hosting.', '.invalid.', '.jewelry.', '.kitchen.', '.limited.', '.markets.', '.network.', '.neustar.', '.organic.', '.origins.', '.panerai.', '.recipes.', '.rentals.', '.reviews.', '.sandvik.', '.science.', '.shiksha.', '.support.', '.systems.', '.tickets.', '.wanggou.', '.weather.', '.website.', '.wedding.', '.whoswho.', '.winners.', '.agakhan.', '.alibaba.', '.android.', '.athleta.', '.auction.', '.auspost.', '.avianca.', '.banamex.', '.bauhaus.', '.bentley.', '.bestbuy.', '.booking.', '.brother.', '.careers.', '.cartier.', '.channel.', '.chintai.', '.citadel.', '.clubmed.', '.compare.', '.contact.', '.corsica.', '.courses.', '.dentist.', '.farmers.', '.ferrero.', '.finance.', '.fishing.', '.fitness.', '.florist.', '.flowers.', '.forsale.', '.fujitsu.', '.genting.', '.godaddy.', '.guitars.', '.hangout.', '.hitachi.', '.holiday.', '.hoteles.', '.hotmail.', '.hyundai.', '.iselect.', '.ismaili.', '.juniper.', '.komatsu.', '.lacaixa.', '.lancome.', '.lanxess.', '.lasalle.', '.latrobe.', '.leclerc.', '.liaison.', '.lincoln.', '.metlife.', '.monster.', '.netbank.', '.netflix.', '.okinawa.', '.oldnavy.', '.philips.', '.pioneer.', '.politie.', '.realtor.', '.rexroth.', '.samsung.', '.schmidt.', '.schwarz.', '.shriram.', '.singles.', '.spiegel.', '.staples.', '.starhub.', '.statoil.', '.storage.', '.surgery.', '.temasek.', '.theater.', '.theatre.', '.tiffany.', '.toshiba.', '.trading.', '.walmart.', '.watches.', '.windows.', '.xfinity.', '.yamaxun.', '.youtube.', '.zuerich.')): #early-registration.of.surfnet.invalid. return True elif len(Hostname) >= 11 and Hostname.endswith(('.airforce.', '.attorney.', '.barclays.', '.brussels.', '.business.', '.capetown.', '.catering.', '.cleaning.', '.computer.', '.delivery.', '.deloitte.', '.diamonds.', '.discount.', '.discover.', '.download.', '.etisalat.', '.everbank.', '.feedback.', '.goodyear.', '.holdings.', '.istanbul.', '.lighting.', '.maserati.', '.mckinsey.', '.partners.', '.pharmacy.', '.pictures.', '.plumbing.', '.property.', '.reliance.', '.saarland.', '.security.', '.services.', '.showtime.', '.software.', '.training.', '.ventures.', '.xn--p1ai.', '.yokohama.', '.abudhabi.', '.allstate.', '.barefoot.', '.bargains.', '.baseball.', '.boutique.', '.bradesco.', '.broadway.', '.budapest.', '.builders.', '.catholic.', '.chrysler.', '.cipriani.', '.cityeats.', '.clinique.', '.clothing.', '.commbank.', '.democrat.', '.engineer.', '.ericsson.', '.esurance.', '.exchange.', '.fidelity.', '.firmdale.', '.football.', '.frontier.', '.grainger.', '.graphics.', '.guardian.', '.hdfcbank.', '.helsinki.', '.hospital.', '.infiniti.', '.ipiranga.', '.jpmorgan.', '.lundbeck.', '.marriott.', '.memorial.', '.mortgage.', '.movistar.', '.observer.', '.redstone.', '.samsclub.', '.shopping.', '.softbank.', '.stcgroup.', '.supplies.', '.symantec.', '.telecity.', '.uconnect.', '.vanguard.', '.verisign.', '.woodside.', '.xn--90ae.', '.xn--node.', '.xn--qxam.')): return True elif len(Hostname) >= 12 and Hostname.endswith(('.alfaromeo.', '.amsterdam.', '.barcelona.', '.christmas.', '.community.', '.directory.', '.education.', '.equipment.', '.homesense.', '.institute', '.insurance.', '.marketing.', '.melbourne.', '.solutions.', '.vacations.', '.xn--j1amh.', '.xn--p1acf.', '.accenture.', '.allfinanz.', '.analytics.', '.aquarelle.', '.bloomberg.', '.fairwinds.', '.financial.', '.firestone.', '.fresenius.', '.frontdoor.', '.fujixerox.', '.furniture.', '.goldpoint.', '.goodhands.', '.hisamitsu.', '.homedepot.', '.homegoods.', '.honeywell.', '.institute.', '.kuokgroup.', '.ladbrokes.', '.lancaster.', '.landrover.', '.lifestyle.', '.marshalls.', '.mcdonalds.', '.microsoft.', '.montblanc.', '.panasonic.', '.passagens.', '.pramerica.', '.richardli.', '.scjohnson.', '.shangrila.', '.statebank.', '.statefarm.', '.stockholm.', '.travelers.', '.xn--90ais.', '.xn--c1avg.', '.xn--d1alf.', '.xn--e1a4c.', '.xn--fhbei.', '.xn--j1aef.', '.xn--l1acc.', '.xn--ngbrx.', '.xn--nqv7f.', '.xn--tckwe.', '.xn--vhquv.', '.yodobashi.')): return True elif len(Hostname) >= 13 and Hostname.endswith(('.accountant.', '.bnpparibas.', '.consulting.', '.extraspace.', '.healthcare.', '.immobilien.', '.management.', '.newholland.', '.properties.', '.restaurant.', '.technology.', '.vlaanderen.', '.apartments.', '.associates.', '.basketball.', '.boehringer.', '.capitalone.', '.creditcard.', '.cuisinella.', '.eurovision.', '.foundation.', '.industries.', '.mitsubishi.', '.nationwide.', '.nextdirect.', '.onyourside.', '.protection.', '.prudential.', '.realestate.', '.republican.', '.schaeffler.', '.swiftcover.', '.tatamotors.', '.telefonica.', '.vistaprint.', '.volkswagen.', '.xn--30rr7y.', '.xn--3pxu8k.', '.xn--45q11c.', '.xn--4gbrim.', '.xn--55qx5d.', '.xn--5tzm5g.', '.xn--80aswg.', '.xn--90a3ac.', '.xn--9dbq2a.', '.xn--9et52u.', '.xn--c2br7g.', '.xn--cg4bki.', '.xn--czrs0t.', '.xn--czru2d.', '.xn--fiq64b.', '.xn--fiqs8s.', '.xn--fiqz9s.', '.xn--io0a7i.', '.xn--kput3i.', '.xn--mxtq1m.', '.xn--o3cw4h.', '.xn--pssy2u.', '.xn--unup4y.', '.xn--wgbh1c.', '.xn--wgbl6a.', '.xn--y9a3aq.')): return True elif len(Hostname) >= 14 and Hostname.endswith(('.accountants.', '.barclaycard.', '.blockbuster.', '.calvinklein.', '.engineering.', '.enterprises.', '.lamborghini.', '.photography.', '.productions.', '.williamhill.', '.university.', '.xn--6frz82g.', '.xn--9krt00a.', '.xn--g2xx48c.', '.xn--kpry57d.', '.xn--q9jyb4c.', '.xn--rovu88b.', '.blackfriday.', '.bridgestone.', '.contractors.', '.creditunion.', '.foodnetwork.', '.investments.', '.kerryhotels.', '.motorcycles.', '.olayangroup.', '.playstation.', '.progressive.', '.redumbrella.', '.rightathome.', '.xn--11b4c3d.', '.xn--1ck2e1b.', '.xn--1qqw23a.', '.xn--3bst00m.', '.xn--3ds443g.', '.xn--42c2d9a.', '.xn--45brj9c.', '.xn--55qw42g.', '.xn--80ao21a.', '.xn--cck2b3b.', '.xn--czr694b.', '.xn--d1acj3b.', '.xn--efvy88h.', '.xn--estv75g.', '.xn--fct429k.', '.xn--fjq720a.', '.xn--flw351e.', '.xn--gecrj9c.', '.xn--gk3at1e.', '.xn--h2brj9c.', '.xn--hxt814e.', '.xn--imr513n.', '.xn--j6w193g.', '.xn--jvr189m.', '.xn--kprw13d.', '.xn--kpu716f.', '.xn--mgbtx2b.', '.xn--mix891f.', '.xn--nyqy26a.', '.xn--pbt977c.', '.xn--pgbs0dh.', '.xn--rhqv96g.', '.xn--s9brj9c.', '.xn--ses554g.', '.xn--t60b56a.', '.xn--vuq861b.', '.xn--w4rs40l.', '.xn--xhq521b.', '.xn--zfr164b.')): return True elif len(Hostname) >= 15 and Hostname.endswith(('.construction.', '.versicherung.', '.xn--mgbt3dhd.', '.xn--ngbc5azd.', '.lplfinancial.', '.pamperedchef.', '.scholarships.', '.xn--3e0b707e.', '.xn--80adxhks.', '.xn--80asehdb.', '.xn--8y0a063a.', '.xn--gckr3f0f.', '.xn--mgb9awbf.', '.xn--mgbab2bd.', '.xn--mgbpl2fh.', '.xn--mk1bu44c.', '.xn--ngbe9e0a.', '.xn--ogbpf8fl.', '.xn--qcka1pmc.')): return True elif len(Hostname) >= 16 and Hostname.endswith(('.international.', '.lifeinsurance.', '.orientexpress.', '.spreadbetting.', '.travelchannel.', '.wolterskluwer.', '.xn--eckvdtc9d.', '.xn--fpcrj9c3d.', '.xn--fzc2c9e2c.', '.xn--tiq49xqyj.', '.xn--yfro4i67o.', '.xn--ygbi2ammx.')): return True elif len(Hostname) >= 17 and Hostname.endswith(('.cancerresearch.', '.weatherchannel.', '.xn--mgbb9fbpob.', '.afamilycompany.', '.americanfamily.', '.bananarepublic.', '.cookingchannel.', '.kerrylogistics.', '.xn--54b7fta0cc.', '.xn--6qq986b3xl.', '.xn--80aqecdr1a.', '.xn--b4w605ferd.', '.xn--fiq228c5hs.', '.xn--jlq61u9w7b.', '.xn--mgba3a3ejt.', '.xn--mgbaam7a8h.', '.xn--mgbayh7gpa.', '.xn--mgbbh1a71e.', '.xn--mgbca7dzdo.', '.xn--mgbi4ecexp.', '.xn--mgbx4cd0ab.')): return True elif len(Hostname) >= 18 and Hostname.endswith(('.americanexpress.', '.kerryproperties.', '.sandvikcoromant.', '.xn--i1b6b1a6a2e.', '.xn--kcrx77d1x4a.', '.xn--lgbbat1ad8j.', '.xn--mgba3a4f16a.', '.xn--mgbaakc7dvf.', '.xn--mgbc0a9azcg.', '.xn--nqv7fs00ema.')): return True elif len(Hostname) >= 19 and Hostname.endswith(('.xn--fzys8d69uvgm.', '.xn--mgba7c0bbn0a.', '.xn--xkc2al3hye2a.')): return True elif len(Hostname) >= 20 and Hostname.endswith(('.xn--3oq18vl8pn36a.', '.xn--5su34j936bgsg.', '.xn--bck1b9a5dre4c.', '.xn--mgbai9azgqp6j.', '.xn--mgberp4a5d4ar.', '.xn--xkc2dl3a5ee0h.')): return True elif len(Hostname) >= 21 and Hostname.endswith(('.northwesternmutual.', '.travelersinsurance.')): return True elif len(Hostname) >= 23 and Hostname.endswith(('.xn--w4r85el8fhu5dnra.')): return True elif len(Hostname) >= 25 and Hostname.endswith(('.xn--clchc0ea0b2g2a9gcd.')): return True elif len(Hostname) >= 26 and Hostname.endswith(('.xn--vermgensberater-ctb.')): return True elif len(Hostname) >= 27 and Hostname.endswith(('.xn--vermgensberatung-pwb.')): return True elif re.search('\.[a-z][a-z]\.$', Hostname) is not None: #ends in 2 letter TLD return True else: if not Hostname.endswith(no_warn_name_tails): Debug("Hostname " + Hostname + " has invalid TLD, ignoring.") return False def process_udp_dns_response(src_ip, dst_ip, src_service, src_port, p_dns, orig_packet): #FIXME - Also report the TLD from one of the query answers to show what it's willing to answer for? ReportId("US", src_ip, "UDP_" + src_port, "open", "dns/server") #Now we extract dns answers. First, check that there's no dns error: ### rcode=0 No Error if p_dns.rcode == 0: DNSBlocks = [] CNAMERecs = [] #We hold onto all cnames until we've processed all PTR's and A's here if p_dns.ancount > 0: #If we have at least one answer from the answer block, process it DNSBlocks.append(p_dns.an) if p_dns.arcount > 0: #Likewise for the "additional" block DNSBlocks.append(p_dns.ar) for OneAn in DNSBlocks: #Thanks to Philippe Biondi for showing me how to extract additional records. #Debug("Start dns extract" + str(p_dns.ancount)) #OneAn = p_dns.an #while OneAn is not NoPayload: #This doesn't seem to stop at the end of the list; incorrect syntax. while isinstance(OneAn, DNSRR): #Somewhat equivalent: while not isinstance(an, NoPayload): #Type codes can be found in http://www.rfc-editor.org/rfc/rfc1035.txt #print "Type: " + str(type(OneAn)) #All of type scapy.DNSRR #Note: rclass 32769 appears to show up in mdns records from apple if OneAn.rclass in (1, 32769): if OneAn.type == 1: #"IN" class and "A" type answer DNSIPAddr = OneAn.rdata DNSHostname = OneAn.rrname.lower() ReportId("DN", DNSIPAddr, "A", DNSHostname, "") elif OneAn.type == 2: #"IN" class and "NS" answer pass #Perhaps later #Like cnames, this is object -> nameserver hostname, so these would need to be queued like cnames until we're done with A's and PTR's. elif OneAn.type == 5: #"IN" class and "CNAME" answer CNAMERecs.append(OneAn) #Remember the record; we'll process these after the PTR's and A's elif OneAn.type == 6: #"IN" class and "SOA" answer pass #Not immediately useful, perhaps later elif OneAn.type == 12: #"IN" class and "PTR" type answer DNSHostname = OneAn.rdata.lower() #For input of '182.111.59.66.in-addr.arpa.' : DNSIPAddr = OneAn.rrname.upper() # '182.111.59.66.IN-ADDR.ARPA.' DNSIPAddr = DNSIPAddr.replace(".IN-ADDR.ARPA.", "") # '182.111.59.66' DNSIPAddr = DNSIPAddr.replace(".IP6.ARPA.", "") # (Strip off the suffix used for ipv6) DNSIPAddr = DNSIPAddr.split('.') # ['182', '111', '59', '66'] DNSIPAddr.reverse() # ['66', '59', '111', '182'] DNSIPAddr = string.join(DNSIPAddr, '.') # '66.59.111.182' #Check that we end up with a legal IP address before continuing; we're getting garbage. if re.search('^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$', DNSIPAddr) is not None: #Legal IPv4 address ReportId("DN", DNSIPAddr, "PTR", DNSHostname, "") elif re.search('^[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]\.[0-9A-F]$', DNSIPAddr) is not None: #Legal IPv6 address such as 0.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.2.4.E.1.4.0.0.2.2.F.7.0.6.2.ip6.arpa. ReportId("DN", DNSIPAddr, "PTR", DNSHostname, "") elif OneAn.rrname.endswith('.local.'): ReportId("DN", src_ip, "PTR", str(OneAn.rrname), str(OneAn.rdata)) else: Debug("Odd PTR rrname: " + OneAn.rrname) elif OneAn.type == 13: #"IN" class and "HINFO" answer https://tools.ietf.org/html/rfc1035 if OneAn.rrname.endswith('.local.'): cpu_name, remainder = extract_len_string(OneAn.rdata) os_name, remainder = extract_len_string(remainder) ReportId("DN", src_ip, "HINFO", str(OneAn.rrname), "cpu=" + cpu_name + " os=" + os_name) else: pass #Possibly later, save as raw text UnhandledPacket(orig_packet) elif OneAn.type == 15: #"IN" class and "MX" answer pass #Possibly later elif OneAn.type == 16: #"IN" class and "TXT" answer if OneAn.rrname.endswith('.local.'): #Note, this is technically a TXT record, but it's converting an IP address into a hostname (possibly with other stuff), so I'm calling it a PTR ReportId("DN", src_ip, "PTR", str(OneAn.rrname), str(OneAn.rdata)) else: pass #Possibly later, save as raw text UnhandledPacket(orig_packet) elif OneAn.type == 17: #"IN" class and "RP" answer. dns_object = str(OneAn.rrname) resp_person = OneAn.rdata readable_person = '' first_word, remainder = extract_len_string(resp_person) while first_word: readable_person += first_word + '.' first_word, remainder = extract_len_string(remainder) ReportId("NA", '0.0.0.0', "RP", dns_object, readable_person) elif OneAn.type == 28: #"IN" class and "AAAA" answer DNSIPAddr = OneAn.rdata.upper() DNSHostname = OneAn.rrname.lower() ReportId("DN", DNSIPAddr, "AAAA", DNSHostname, "") elif OneAn.type == 33: #"IN" class and "SRV" answer if OneAn.rrname.endswith('.local.'): ReportId("DN", src_ip, "SRV", str(OneAn.rrname), '') #Too much garbage in OneAn.rdata to include it as additional info: str(OneAn.rdata).strip(' \t\r\n\0') else: pass #Possibly later, save as raw text UnhandledPacket(orig_packet) elif OneAn.type == 39: #"IN" class and "DNAME" answer https://tools.ietf.org/html/rfc6672 #Similar to a CNAME, but for an entire tree. A RHS substitution for the request (replace "example.com" with "example.net") pass elif OneAn.type == 41: #"IN" class and "OPT" answer used to support EDNS https://tools.ietf.org/html/rfc6891 pass elif OneAn.type == 52: #"IN" class and "TLSA" answer https://tools.ietf.org/html/rfc6698 pass else: ShowPacket(orig_packet, "IN, but unhandled type", HonorQuit) Debug("PUDR: IN, but unhandled type: " + str(OneAn.type)) elif (OneAn.rclass == 0) and (OneAn.type == 255): #"Reserved" class and "ANY" answer. WTF? UnhandledPacket(orig_packet) elif (OneAn.rclass == 0): #"Reserved" class UnhandledPacket(orig_packet) elif (OneAn.rclass == 3) and (OneAn.type == 16): #Chaos/CH domain and type TXT if OneAn.rrname.upper() == 'VERSION.BIND.': ReportId("DN", src_ip, "TXT", 'Chaos/' + OneAn.rrname, OneAn.rdata) ReportId("UC", dst_ip, "TXT", 'open', 'dns/client Chaos/' + OneAn.rrname) else: ShowPacket(orig_packet, "DNS Chaos/OTHER answer", HonorQuit) elif (OneAn.rclass == 3) and (OneAn.type == 2) and OneAn.rrname.upper() == 'VERSION.BIND.': #Chaos/CH domain and type NS pass elif (OneAn.rclass == 254) and (OneAn.type == 5): #254 => QCLASS NONE and type=CNAME UnhandledPacket(orig_packet) elif (OneAn.rclass == 256) and (OneAn.type == 256): #WTF? UnhandledPacket(orig_packet) else: ShowPacket(orig_packet, "unhandled rclass", HonorQuit) Debug("PUDR: unhandled type: " + str(OneAn.type)) #Move to the next DNS object in the "an" block OneAn = OneAn.payload for OneCNAME in CNAMERecs: #Now that we have all A/PTR's, go back and turn cname records into pseudo-A's if isinstance(OneCNAME, DNSRR): Alias = OneCNAME.rrname.lower() Existing = OneCNAME.rdata.lower() if isFQDN(Alias) and isFQDN(Existing): if HostIPs.has_key(Existing): for OneIP in HostIPs[Existing]: #Loop through each of the IPs for the canonical name, and ReportId("DN", OneIP, "CNAME", Alias, "") #report them as kind-of A records for the Alias. #FIXME - change last field to Existing? #If we don't have a A/PTR record for "Existing", just ignore it. Hopefully we'll get the Existing A/PTR in the next few answers, and will re-ask for the CNAME later, at which point we'll get a full cname record. #else: # Debug("CNAME " + Alias + " -> " + Existing + " requested, but no IP's for the latter, skipping.") else: Debug("One of " + Alias + " and " + Existing + " isn't an FQDN, skipping cname processing.") ### rcode=1 FormErr: server responding to an improperly formatted request elif p_dns.rcode == 1: pass ### rcode=2 ServFail: domain exists, root nameservers list authoritative name servers, but authNS's won't answer queries elif p_dns.rcode == 2: pass ### rcode=3 NXDOMAIN: root nameservers don't have any listing (domain doesn't exist or is on hold) elif p_dns.rcode == 3: if ReportNXDomain: DNSBlocks = [] if p_dns.qdcount == 1: #If we have one question from the question record, process it DNSBlocks.append(p_dns.qd) else: ShowPacket(orig_packet, "DNS Answer with NXDOMAIN, qdcount not equal to 1", KeepGoing) for OneAn in DNSBlocks: if isinstance(OneAn, DNSQR): if OneAn.qclass in (1, 32769): #FIXME - add more DNS record types if OneAn.qtype == 1: #"IN" class and "A" type answer DNSQuery = OneAn.qname.lower() ReportId("DN", "0.0.0.0", "A", DNSQuery, "NXDOMAIN") elif OneAn.qtype == 2: #"IN" class and "NS" type answer DNSQuery = OneAn.qname.lower() ReportId("DN", "0.0.0.0", "NS", DNSQuery, "NXDOMAIN") elif OneAn.qtype == 12: #"IN" class and "PTR" type answer DNSQuery = OneAn.qname.lower() ReportId("DN", "0.0.0.0", "PTR", DNSQuery, "NXDOMAIN") elif OneAn.qtype == 15: #"IN" class and "MX" type answer DNSQuery = OneAn.qname.lower() ReportId("DN", "0.0.0.0", "MX", DNSQuery, "NXDOMAIN") elif OneAn.qtype == 28: #"IN" class and "AAAA" type answer DNSQuery = OneAn.qname.lower() ReportId("DN", "0000:0000:0000:0000:0000:0000:0000:0000", "AAAA", DNSQuery, "NXDOMAIN") else: ShowPacket(orig_packet, "DNS Answer with NXDOMAIN", KeepGoing) else: UnhandledPacket(orig_packet) ### rcode=4 Not implemented elif p_dns.rcode == 4: UnhandledPacket(orig_packet) ### rcode=5 Query refused elif p_dns.rcode == 5: pass ### rcode=7 YXRRSET - RRset exists when it should not. elif p_dns.rcode == 7: pass ### rcode=8 NXRRSet - RRset that should exist does not. elif p_dns.rcode == 8: pass ### rcode=9 Not authoritative https://tools.ietf.org/html/rfc2136 (note, also used as Not Authorized in TSIG update response https://tools.ietf.org/html/rfc2845 ) elif p_dns.rcode == 9: pass else: #rcode indicates an error ShowPacket(orig_packet, "process_udp_dns_response/unhandled rcode", HonorQuit) def processpacket(p): """Extract information from a single packet off the wire.""" global SynSentToTCPService global SynAckSentToTCPClient global LiveTCPService global LiveTCPClient global NmapServerDescription global ManualServerDescription global ClientDescription global ServiceFPs global SipPhoneMatch global Devel global HostIPs #Extract dest mac IF this is an ethernet/ipv4 or ethernet/ipv6 packet if (p.haslayer(Ether) and (isinstance(p[Ether], Ether)) and ((p[Ether].type == 0x0800) or (p[Ether].type == 0x8100) or (p[Ether].type == 0x86DD))): sMAC = p[Ether].src dMAC = p[Ether].dst else: #We mostly use the src and dst macs to check for broadcasts/multicasts. If we're not on ethernet, leave it blank. sMAC = '' dMAC = '' ### Spanning Tree Protocol if isinstance(p, Dot3) and p.haslayer(LLC) and isinstance(p[LLC], LLC): pass #Nothing really to learn from it. ### 802.3 without LLC elif isinstance(p, Dot3): pass #Nothing really to learn from it. ### Need more details on how to handle. elif p.haslayer(Ether) and p[Ether] is None: ShowPacket(p, "non-ethernet packet: " + str(type(p)), HonorQuit) ### ARP elif (p.haslayer(Ether) and p[Ether].type == 0x0806) and p.haslayer(ARP) and isinstance(p[ARP], ARP): #ARP #pull arp data from here instead of tcp/udp packets, as these are all local if p[ARP].op == 1: #1 is request ("who-has") pass elif p[ARP].op == 2: #2 is reply ("is-at") if (p[ARP].psrc != None) and (p[ARP].hwsrc != None): IPAddr = p[ARP].psrc MyMac = p[ARP].hwsrc.upper() ReportId("MA", IPAddr, 'Ethernet', MyMac, '') else: UnhandledPacket(p) else: UnhandledPacket(p) ### ARP, truncated elif p.haslayer(Ether) and p[Ether].type == 0x0806: #2054: ARP, apparently truncated UnhandledPacket(p) ### IPv4 elif ((p.haslayer(CookedLinux) and p[CookedLinux].proto == 0x800) or (p.haslayer(Ether) and ((p[Ether].type == 0x0800) or (p[Ether].type == 0x8100))) or not p.haslayer(Ether)) and p.haslayer(IP) and isinstance(p[IP], IP): if p[IP].version != 4: ShowPacket(p, "IPV4 packet with version != 4", HonorQuit) sIP = str(p[IP].src) dIP = str(p[IP].dst) #Best to get these from arps instead; if we get them from here, we get router macs for foreign addresses. #ReportId("MA", sIP, "Ethernet", sMAC, '') #ReportId("MA", dIP, "Ethernet", dMAC, '') if p.getlayer(Raw): Payload = p.getlayer(Raw).load else: Payload = "" ### IPv4/IP if p[IP].proto == 0: ShowPacket(p, "IPv4/Protocol 0", HonorQuit) ### IPv4/ICMPv4 elif (p[IP].proto == 1) and p.haslayer(ICMP) and isinstance(p[ICMP], ICMP): Type = p[ICMP].type Code = p[ICMP].code ### IPv4/ICMPv4/Echo Reply=0 if Type == 0: ReportId("IP", sIP, "IP", "live", 'icmp echo reply') ### IPv4/ICMPv4/Unreachable=3 elif (Type == 3) and isinstance(p[IPerror], IPerror): #Unreachable, check that we have an actual embedded packet if type(p[IPerror]) != IPerror: ShowPacket(p, "IPv4/ICMPv4/Unreachable=type3/Not IPError: " + str(type(p[IPerror])), HonorQuit) OrigdIP = p[IPerror].dst if Code == 0: #Net unreachable ReportId("IP", OrigdIP, "IP", "dead", 'net unreachable') ReportId("RO", sIP, "NetUn", "router", "") elif Code == 1: #Host unreachable ReportId("IP", OrigdIP, "IP", "dead", 'host unreachable') ReportId("RO", sIP, "HostUn", "router", "") elif Code == 2: #Protocol unreachable ReportId("RO", sIP, "ProtoUn", "router", "") elif (Code == 3) and (p[IPerror].proto == 17) and p.haslayer(UDPerror) and isinstance(p[UDPerror], UDPerror): #Port unreachable and embedded protocol = 17, UDP, as it should be DNSServerLoc = p[IPerror].src + ",UDP_53" if (p[UDPerror].sport == 53) and (ManualServerDescription.has_key(DNSServerLoc)) and (ManualServerDescription[DNSServerLoc] == "dns/server"): #If orig packet coming from 53 and coming from a dns server, don't do anything (closed port on client is a common effect) #Don't waste time on port unreachables going back to a dns server; too common, and ephemeral anyways. pass else: #If orig packet coming from something other than 53, or coming from 53 and NOT coming from a dns server, log as closed OrigDPort = str(p[UDPerror].dport) OrigDstService = OrigdIP + ",UDP_" + OrigDPort ReportId("US", OrigdIP, "UDP_" + OrigDPort, "closed", "port unreachable") elif (Code == 3) and (p[IPerror].proto == 6) and isinstance(p[TCPerror], TCPerror): #Port unreachable and embedded protocol = 6, TCP, which it shouldn't. May be the same firewall providing the TCP FR's #Now we _could_ claim the machine sending the error is a linux firewall. pass elif Code == 6: #Net unknown ReportId("IP", OrigdIP, "IP", "dead", 'net unknown') elif Code == 7: #Host unknown ReportId("IP", OrigdIP, "IP", "dead", 'host unknown') elif Code == 9: #Network Administratively Prohibited pass #Can't tell much from this type of traffic. Possibly list as firewall? elif Code == 10: #Host Administratively Prohibited pass elif Code == 11: #Network unreachable for TOS pass elif Code == 12: #Host unreachable for TOS pass elif Code == 13: #Communication Administratively prohibited pass else: ShowPacket(p, "IPv4/ICMPv4/Type=3/unhandled code: " + str(Code), HonorQuit) ### IPv4/ICMPv3/Source Quench=4 https://tools.ietf.org/html/rfc6633 - ipv4 source quench deprecated since 2012, does not exist in ipv6 elif (Type == 4): UnhandledPacket(p) ### IPv4/ICMPv4/Redirect=5 elif (Type == 5) and isinstance(p[IPerror], IPerror): #Unreachable, check that we have an actual embedded packet if type(p[IPerror]) != IPerror: ShowPacket(p, "IPv4/ICMPv4/Redirect=type5/Not IPError: " + str(type(p[IPerror])), HonorQuit) elif Code in (0, 1, 2, 3): #Network, Host, TOS+Network, TOS+Host ReportId("RO", sIP, "Redirect", "router", "attempted_router") better_router = p[ICMP].gw ReportId("RO", better_router, "Redirect", "router", "recommended_router") else: UnhandledPacket(p) ### IPv4/ICMPv4/Echo Request=8 elif Type == 8: #FIXME - check payload for ping sender type, perhaps pass ### IPv4/ICMPv4/Router Advertisement=9 https://tools.ietf.org/html/rfc1256 elif Type == 9: ReportId("RO", sIP, "RouterAdv", "router", '') ### IPv4/ICMPv4/Time exceeded=11 elif Type == 11: if Code == 0: #TTL exceeded #FIXME - put original target IP as column 5? ReportId("RO", sIP, "TTLEx", "router", "") else: UnhandledPacket(p) else: UnhandledPacket(p) ShowPacket(p, 'Unhandled ipv4 ICMP packet', HonorQuit) elif (p[IP].proto == 1): UnhandledPacket(p) ### IPv4/IGMPv4 elif p[IP].proto == 2: #IGMP UnhandledPacket(p) ### IPv4/TCPv4 elif p[IP].proto == 6 and p.haslayer(TCP) and isinstance(p[TCP], TCP): #TCP sport = str(p[TCP].sport) dport = str(p[TCP].dport) #print p[IP].src + ":" + sport + " -> ", p[IP].dst + ":" + dport, if (p[TCP].flags & 0x17) == 0x12: #SYN/ACK (RST and FIN off) CliService = dIP + ",TCP_" + sport if not SynAckSentToTCPClient.has_key(CliService): SynAckSentToTCPClient[CliService] = True #If we've seen a syn sent to this port and have either not seen any SA/R, or we've seen a R in the past: #The last test is for a service that was previously closed and is now open; report each transition once. Service = sIP + ",TCP_" + sport if SynSentToTCPService.has_key(Service) and ((not LiveTCPService.has_key(Service)) or (not LiveTCPService[Service])): LiveTCPService[Service] = True ReportId("TS", sIP, "TCP_" + sport, "listening", '') elif (p[TCP].flags & 0x17) == 0x02: #SYN (ACK, RST, and FIN off) Service = dIP + ",TCP_" + dport if not SynSentToTCPService.has_key(Service): SynSentToTCPService[Service] = True #Debug("trying to fingerprint " + sIP) #ZZZZ # try: #p0fdata = p0f(p) ##FIXME - reasonably common occurence, don't whine, just fix it. ##if (len(p0fdata) >1): ## Debug("More than one OS fingerprint for " + sIP + ", using the first.") #if (len(p0fdata) >=1): # PDescription = p0fdata[0][0] + " " + p0fdata[0][1] + " (" + str(int(p0fdata[0][2]) + 1) #FIXME - Grabbing just the first candidate, may need to compare correlation values; provided? # if (p0fdata[0][2] == 0): # PDescription = PDescription + " hop away)" # else: # PDescription = PDescription + " hops away)" # #[N][2] param appears to be distance away in hops (but add 1 to this to get real hop count?) # PDescription = PDescription.replace(',', ';') #Commas are delimiters in output # ReportId("IP", sIP, "IP", "live", PDescription) # except: # PDescription = 'p0f failure' # Debug("P0f failure in " + sIP + ":" + sport + " -> " + dIP + ":" + dport) # ReportId("IP", sIP, "IP", "live", PDescription) elif (p[TCP].flags & 0x07) == 0x01: #FIN (SYN/RST off) CliService = sIP + ",TCP_" + dport if SynAckSentToTCPClient.has_key(CliService) and ((not LiveTCPClient.has_key(CliService)) or (not LiveTCPClient[CliService])): LiveTCPClient[CliService] = True ReportId("TC", sIP, "TCP_" + dport, "open", '') elif (p[TCP].flags & 0x07) == 0x04: #RST (SYN and FIN off) #FIXME - handle rst going in the other direction? Service = sIP + ",TCP_" + sport if SynSentToTCPService.has_key(Service) and ((not LiveTCPService.has_key(Service)) or LiveTCPService[Service]): LiveTCPService[Service] = False ReportId("TS", sIP, "TCP_" + sport, "closed", '') elif ((p[TCP].flags & 0x3F) == 0x15) and (sport == "113"): #FIN, RST, ACK (SYN, PSH, URG off) #This may be a firewall or some other device stepping in for 113 with a FIN/RST. pass elif (p[TCP].flags & 0x17) == 0x10: #ACK (RST, SYN, and FIN off) #FIXME - check for UnhandledPacket placement in ACK FromPort = sIP + ",TCP_" + sport ToPort = dIP + ",TCP_" + dport if LiveTCPService.has_key(FromPort) and LiveTCPService[FromPort] and (LiveTCPService.has_key(ToPort)) and LiveTCPService[ToPort]: ShowPacket(p, "IPv4/TCPv4/ACK (RST, SYN, FIN off) Logic failure: both " + FromPort + " and " + ToPort + " are listed as live services.", HonorQuit) elif LiveTCPService.has_key(FromPort) and LiveTCPService[FromPort]: #If the "From" side is a known TCP server: if not NmapServerDescription.has_key(FromPort): #Check nmap fingerprint strings for this server port if ServiceFPs.has_key(int(sport)): for OneTuple in ServiceFPs[int(sport)]: MatchObj = OneTuple[0].search(Payload) if MatchObj != None: #Debugging: #FIXME - removeme once understood: #File "/home/wstearns/med/programming/python/passer/passer.py", line 504, in processpacket #OutputDescription = OutputDescription.replace('$' + str(Index), MatchObj.group(Index)) #TypeError: expected a character buffer object if OneTuple[1] is None: Debug("Null description for " + OneTuple[0]) #quit() OutputDescription = OneTuple[1] if len(MatchObj.groups()) >= 1: #We have subexpressions matched, these need to be inserted into the description string for Index in range(1, len(MatchObj.groups())+1): #Example: Replace "$1" with MatchObj.group(1) OutputDescription = OutputDescription.replace('$' + str(Index), str(MatchObj.group(Index))) ReportId("TS", sIP, "TCP_" + sport, "listening", OutputDescription) NmapServerDescription[sIP + ",TCP_" + sport] = OutputDescription break #Exit for loop, no need to check any more fingerprints now that we've found a match if not NmapServerDescription.has_key(FromPort): #If the above loop didn't find a server description if ServiceFPs.has_key('all'): #Now recheck against regexes not associated with a specific port (port 'all'). for OneTuple in ServiceFPs['all']: MatchObj = OneTuple[0].search(Payload) if MatchObj != None: OutputDescription = OneTuple[1] if len(MatchObj.groups()) >= 1: #We have subexpressions matched, these need to be inserted into the description string for Index in range(1, len(MatchObj.groups())+1): OutputDescription = OutputDescription.replace('$' + str(Index), MatchObj.group(Index)) ReportId("TS", sIP, "TCP_" + sport, "listening", OutputDescription) NmapServerDescription[sIP + ",TCP_" + sport] = OutputDescription break if not ManualServerDescription.has_key(FromPort): if (sport == "22") and (Payload != None) and (Payload.find('SSH-') > -1): if (Payload.find('SSH-1.99-OpenSSH_') > -1) or (Payload.find('SSH-2.0-OpenSSH_') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "ssh/openssh") ManualServerDescription[sIP + ",TCP_" + sport] = "ssh/openssh" elif Payload.find('SSH-1.5-') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "ssh/generic") ManualServerDescription[sIP + ",TCP_" + sport] = "ssh/generic" #LogNewPayload(ServerPayloadDir, FromPort, Payload) else: if SaveUnhandledAcks: UnhandledPacket(p) #LogNewPayload(ServerPayloadDir, FromPort, Payload) elif (sport == "25") and (Payload != None) and (Payload.find(' ESMTP Sendmail ') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "smtp/sendmail") ManualServerDescription[sIP + ",TCP_" + sport] = "smtp/sendmail" elif (sport == "25") and (Payload != None) and (Payload.find(' - Welcome to our SMTP server ESMTP') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "smtp/generic") ManualServerDescription[sIP + ",TCP_" + sport] = "smtp/generic" if SaveUnhandledAcks: UnhandledPacket(p) #LogNewPayload(ServerPayloadDir, FromPort, Payload) #Check for port 80 and search for "Server: " once elif (sport == "80") and (Payload != None) and (Payload.find('Server: ') > -1): if Payload.find('Server: Apache') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/apache") ManualServerDescription[sIP + ",TCP_" + sport] = "http/apache" elif Payload.find('Server: Embedded HTTP Server') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/embedded") ManualServerDescription[sIP + ",TCP_" + sport] = "http/embedded" elif Payload.find('Server: gws') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/gws") ManualServerDescription[sIP + ",TCP_" + sport] = "http/gws" elif Payload.find('Server: KFWebServer') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/kfwebserver") ManualServerDescription[sIP + ",TCP_" + sport] = "http/kfwebserver" elif Payload.find('Server: micro_httpd') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/micro-httpd") ManualServerDescription[sIP + ",TCP_" + sport] = "http/micro-httpd" elif Payload.find('Server: Microsoft-IIS') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/iis") ManualServerDescription[sIP + ",TCP_" + sport] = "http/iis" elif Payload.find('Server: lighttpd') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/lighttpd") ManualServerDescription[sIP + ",TCP_" + sport] = "http/lighttpd" elif Payload.find('Server: MIIxpc') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/mirrorimage") ManualServerDescription[sIP + ",TCP_" + sport] = "http/mirrorimage" elif Payload.find('Server: mini_httpd') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/mini-httpd") ManualServerDescription[sIP + ",TCP_" + sport] = "http/mini-httpd" elif Payload.find('Server: nc -l -p 80') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/nc") ManualServerDescription[sIP + ",TCP_" + sport] = "http/nc" elif Payload.find('Server: nginx/') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/nginx") ManualServerDescription[sIP + ",TCP_" + sport] = "http/nginx" elif Payload.find('Server: Nucleus') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/nucleus") ManualServerDescription[sIP + ",TCP_" + sport] = "http/nucleus" elif Payload.find('Server: RomPager') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/rompager") ManualServerDescription[sIP + ",TCP_" + sport] = "http/rompager" elif Payload.find('Server: Server') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/server") ManualServerDescription[sIP + ",TCP_" + sport] = "http/server" elif Payload.find('Server: Sun-ONE-Web-Server/') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/sun-one") ManualServerDescription[sIP + ",TCP_" + sport] = "http/sun-one" elif Payload.find('Server: TrustRank Frontend') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/trustrank") ManualServerDescription[sIP + ",TCP_" + sport] = "http/trustrank" elif Payload.find('Server: YTS/') > -1: ReportId("TS", sIP, "TCP_" + sport, "listening", "http/yahoo") ManualServerDescription[sIP + ",TCP_" + sport] = "http/yahoo" elif (Payload.find('HTTP/1.0 404 Not Found') > -1) or (Payload.find('HTTP/1.1 200 OK') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "http/generic") ManualServerDescription[sIP + ",TCP_" + sport] = "http/generic" if SaveUnhandledAcks: UnhandledPacket(p) #LogNewPayload(ServerPayloadDir, FromPort, Payload) else: if SaveUnhandledAcks: UnhandledPacket(p) #LogNewPayload(ServerPayloadDir, FromPort, Payload) elif (sport == "110") and (Payload != None) and (Payload.find('POP3 Server Ready') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "pop3/generic") ManualServerDescription[sIP + ",TCP_" + sport] = "pop3/generic" elif (sport == "143") and (Payload != None) and (Payload.find('* OK dovecot ready') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "imap/dovecot") ManualServerDescription[sIP + ",TCP_" + sport] = "imap/dovecot" elif (sport == "143") and (Payload != None) and (Payload.find(' IMAP4rev1 ') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "imap/generic") ManualServerDescription[sIP + ",TCP_" + sport] = "imap/generic" if SaveUnhandledAcks: UnhandledPacket(p) #LogNewPayload(ServerPayloadDir, FromPort, Payload) elif (sport == "783") and (Payload != None) and (Payload.find('SPAMD/1.1 ') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "spamd/spamd") ManualServerDescription[sIP + ",TCP_" + sport] = "spamd/spamd" elif ((sport == "3128") or (sport == "80")) and (Payload != None) and (Payload.find('Via: ') > -1) and (Payload.find(' (squid/') > -1): ReportId("TS", sIP, "TCP_" + sport, "listening", "proxy/squid") ManualServerDescription[sIP + ",TCP_" + sport] = "proxy/squid" else: if SaveUnhandledAcks: UnhandledPacket(p) #LogNewPayload(ServerPayloadDir, FromPort, Payload) elif LiveTCPService.has_key(ToPort) and LiveTCPService[ToPort]: #If the "To" side is a known TCP server: ClientKey = sIP + ",TCP_" + dport #Note: CLIENT ip and SERVER port if not ClientDescription.has_key(ClientKey): if (dport == "22") and (Payload != None) and (Payload.find('SSH-Latency-Measurement') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "ssh/smokeping-latency-scanner") elif (dport == "22") and (Payload != None) and (Payload.find('SSH-2.-check_ssh_1.5') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "ssh/nagios-check_ssh") elif (dport == "22") and (Payload != None) and ((Payload.find('SSH-2.0-OpenSSH_') > -1) or (Payload.find('SSH-1.5-OpenSSH_') > -1)): ReportId("TC", sIP, "TCP_" + dport, "open", "ssh/openssh") #As cute as it is to catch this, it miscatches any relay that's carrying a pine-generated mail. #elif (dport == "25") and (Payload != None) and (Payload.find('Message-ID: -1): # ReportId("TC", sIP, "TCP_" + dport, "open", "smtp/pine") elif ((dport == "80") or (dport == "3128")) and (Payload != None) and (Payload.find('User-Agent: libwww-perl/') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "http/libwww-perl") elif ((dport == "80") or (dport == "3128")) and (Payload != None) and (Payload.find('User-Agent: Lynx') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "http/lynx") elif ((dport == "80") or (dport == "3128")) and (Payload != None) and (Payload.find('User-Agent: Mozilla') > -1) and (Payload.find(' Firefox/') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "http/firefox") elif ((dport == "80") or (dport == "3128")) and (Payload != None) and (Payload.find('User-Agent: Wget/') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "http/wget") elif (dport == "143") and (Payload != None) and (Payload.find('A0001 CAPABILITY') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "imap/generic") #LogNewPayload(ClientPayloadDir, ClientKey, Payload) elif (dport == "783") and (Payload != None) and (Payload.find('PROCESS SPAMC') > -1): ReportId("TC", sIP, "TCP_" + dport, "open", "spamd/spamc") else: if SaveUnhandledAcks: UnhandledPacket(p) #LogNewPayload(ClientPayloadDir, ClientKey, Payload) #else: #Neither port pair is known as a server # ShowPacket(p, "IPv4/TCPv4/ACK (RST, SYN, FIN off)/Neither port pair is known as a server", HonorQuit) # #Following is debugging at best; it should only show up early on as the sniffer listens to conversations for which it didn't hear the SYN/ACK # #print "note: neither " + FromPort + " nor " + ToPort + " is listed as a live service." elif (p[TCP].flags & 0x17) == 0x00: #(ACK, RST, SYN, and FIN off) #FIXME save as malicious UnhandledPacket(p) ReportId("TC", sIP, "TCP_" + dport, "open", "TCP Null flag scanner") elif (p[TCP].flags & 0x17) == 0x03: #SYN/FIN (ACK and RST off) #FIXME save as malicious UnhandledPacket(p) ReportId("TC", sIP, "TCP_" + dport, "open", "TCP SYN/FIN flag scanner") else: #Other TCP flag combinations here ShowPacket(p, "IPv4/TCPv4/Unhandled TCP flag combination", HonorQuit) ### IPv4/TCPv4, probably truncated/fragmented elif p[IP].proto == 6: #TCP, but haslayer fails. Quite possibly a fragment; either way we can't do anything with it. ShowPacket(p, "IPv4/TCPv4/no TCP layer", HonorQuit) ### IPv4/UDPv4 elif p[IP].proto == 17 and p.haslayer(UDP): #old form: (type(p[UDP]) == UDP): #UDP. We have to check the object type as well as we do get (corrupted? truncated?) packets with type 17 that aren't udp: AttributeError: 'NoneType' object has no attribute 'sport' #Change over to p.getlayer(ICMPv6DestUnreach) ? We're getting crashes on elif p[IP].proto == 17 and (type(p[UDP]) == UDP): #FIXME - possibly run udp packets through ServiceFPs as well? #FIXME - use this *_layer = p.getlayer(*) format in all sections #FIXME - pull all the extractions as high as possible in their section so we only look them up once udp_layer = p.getlayer(UDP) sport = str(udp_layer.sport) #Formerly sport=str(p[UDP].sport) dport = str(udp_layer.dport) SrcService = sIP + ",UDP_" + sport DstService = dIP + ",UDP_" + dport SrcClient = sIP + ",UDP_" + dport #Multicast DNS: http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt #- usually sent to 224.0.0.251 (or FF02::FB) (link-local multicast). # - if ".local." in query, these MUST be the target IPs # - non-local queries may be sent to these or normal dns servers # - rdns queries for "254.169.in-addr.arpa." MUST be sent to 224.0.0.251 # - rdns queries for "8.e.f.ip6.arpa.", "9.e.f.ip6.arpa.","a.e.f.ip6.arpa.", and "b.e.f.ip6.arpa." MUST be sent to the IPv6 mDNS link-local multicast address FF02::FB. #- sent to udp port 5353 #- generic clients may use "single-dns-object.local.", such as "sparrow.local." #- responses have IP TTL = 255 to check that packet originated on-lan #Handle easily categorized services early if dport in PriUDPPortNames: #Client talking to server ReportId("UC", sIP, "UDP_" + dport, "open", str(PriUDPPortNames[dport]) + "/client") elif sport in PriUDPPortNames: #server talking to client ReportId("US", sIP, "UDP_" + sport, "open", str(PriUDPPortNames[sport]) + "/server") ### IPv4/UDPv4/Multicast DNS, placed next to normal dns, out of numerical order elif (sport == "5353") and (dport == "5353") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].qr == 1): #qr == 1 is a response process_udp_dns_response(sIP, dIP, SrcService, sport, p[DNS], p) elif (sport == "5353") and (dport == "5353") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].qr == 0): #qr == 0 is a request pass elif (sport == "5353") and (dport == "5353") and not p.haslayer(DNS): #No dns layer for some reason UnhandledPacket(p) elif (dport == "5353") and ((p[IP].ttl == 1) or (p[IP].ttl == 2) or (p[IP].ttl == 255)): #2 may not be rfc-legal, but I'm seeing it on the wire. if dIP == "224.0.0.251": ReportId("UC", sIP, "UDP_" + dport, "open", "mdns/broadcastclient") else: ReportId("UC", sIP, "UDP_" + dport, "open", "mdns/client") #Extract dns answers like with 53; change elif to if and add 5353 to ports on next if? #At the moment, no; scapy does not appear to parse 5353 as dns. #else: # UnhandledPacket(p) elif (sport != "5353") and (dport == "5353") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].qr == 0) and p[IP].ttl != 255: #query from outside local lan; see https://tools.ietf.org/html/rfc6762 section 5.5 ReportId("UC", sIP, "UDP_" + dport, "open", "mdns-off-network/client") elif (sport == "5353") and (dport != "5353") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].qr == 1): #response to outside local lan; see https://tools.ietf.org/html/rfc6762 section 5.5 ReportId("US", sIP, "UDP_" + sport, "open", "mdns-off-network/server") #FIXME - add check for "if isinstance(p[DNS], whatevertype): here and at all p[] accesses. elif (sport == "53") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].qr == 1): #qr == 1 is a response. Note, case where sport=53 but no dns layer is at the end of the udp ports process_udp_dns_response(sIP, dIP, SrcService, sport, p[DNS], p) elif (dport == "53") and p.haslayer(DNS) and isinstance(p[DNS], DNS) and (p[DNS].qr == 0): #dns query ReportId("UC", sIP, "UDP_" + dport, "open", "dns/client") ### IPV4/UDPv4/bootp_dhcp=67 elif (sport == "67") and (dport == "68"): #Bootp/dhcp server talking to client ReportId("US", sIP, "UDP_" + sport, "open", "bootpordhcp/server") elif (sport == "68") and (dport == "67"): #Bootp/dhcp client talking to server #FIXME - pull ID field out as a name to report if sIP != "0.0.0.0": #If the client is simply renewing an IP, remember it. ReportId("UC", sIP, "UDP_" + dport, "open", "bootpordhcp/client") for one_opt in p[DHCP].options: #Can't directly access p.haslayer(DHCPOptions) because it's a list of tuples. https://stackoverflow.com/questions/22152130/how-can-i-get-option-number-from-an-dhcp-header-in-scapy if one_opt[0] == 'hostname': Debug("Found DHCPOptions hostname field") ReportId("NA", sIP, "DHCP", one_opt[1], "dhcp") #else: #If you want to record which macs are asking for addresses, do it here. # pass ### IPV4/UDPv4/udp_http=80 nmap quic scan elif (dport == "80") and (Payload == '\r12345678Q999' + '00'.decode('hex')): ReportId("UC", sIP, "UDP_" + dport, "open", "udp-http/client nmap QUIC scan") ### IPV4/UDPv4/udp_http=80 with empty payload elif (dport == "80") and ((Payload == None) or (Payload == '')): ReportId("UC", sIP, "UDP_" + dport, "open", "null-udp-http/client") ### IPV4/UDPv4/ntp=123 elif (dport == "123") and (dIP in ("216.115.23.75", "216.115.23.76", "69.59.240.75")): ReportId("UC", sIP, "UDP_" + dport, "open", "ntp/vonageclient") elif (sport == "123") and (sIP in ("216.115.23.75", "216.115.23.76", "69.59.240.75")): ReportId("US", sIP, "UDP_" + sport, "open", "ntp/vonageserver") elif ((sport == "123") or (dport == "123")) and p.haslayer(NTPHeader): ntp_stratum = p[NTPHeader].stratum #What comes back in the "id" field is either an IPv4 address of sIP's primary reference (good!) or #the first 4 bytes of the MD5 hash of the IPv6 address of sIP's primary reference (bad.) Without actively #checking, there's no way to distinguish the two cases. https://www.nwtime.org/ntps-refid/ ntp_id = p[NTPHeader].id ntp_ref_id = str(p[NTPHeader].ref_id).rstrip(' \t\r\n\0') if ntp_id: ReportId("US", sIP, "UDP_123", "open", 'ntp/server stratum=' + str(ntp_stratum) + ' reference=' + str(ntp_id)) ReportId("US", ntp_id, "UDP_123", "open", 'ntp/server inferred from being a reference but must be checked.') elif ntp_ref_id in known_ntp_refs: ReportId("US", sIP, "UDP_123", "open", 'ntp/server stratum=' + str(ntp_stratum)) else: ReportId("US", sIP, "UDP_123", "open", 'ntp/server stratum=' + str(ntp_stratum)) ShowPacket(p, "IPv4/UDPv4/ntp with null reference:_" + str(ntp_ref_id) + "_", HonorQuit) elif (dport == "123") and p.haslayer(NTPPrivate) and p[NTPPrivate].response == 0: #response == 0 is a request if p[NTPPrivate].request_code == 42: #REQ_MON_GETLIST_1 ReportId("UC", sIP, "UDP_123", "open", 'ntp/client REQ_MON_GETLIST_1: Likely spoofed and DDOSed source IP') elif p[NTPPrivate].request_code == 32: #REQ_REQUEST_KEY ReportId("UC", sIP, "UDP_123", "open", 'ntp/client') else: ShowPacket(p, "IPv4/UDPv4/ntp Mode 7 request but not REQ_MON_GETLIST_1", HonorQuit) elif (dport == "123") and p.haslayer(NTPControl): ReportId("UC", sIP, "UDP_123", "open", 'ntp_control/client') elif (sport == "123") and p.haslayer(NTPPrivate) and p[NTPPrivate].response == 1: #response == 1 is a reply if p[NTPPrivate].request_code == 42: #REQ_MON_GETLIST_1 ReportId("US", sIP, "UDP_123", "open", 'ntp/server REQ_MON_GETLIST_1: Likely middleman in DDOS') else: ShowPacket(p, "IPv4/UDPv4/ntp Mode 7 reply but not REQ_MON_GETLIST_1", HonorQuit) ### IPv4/UDPv4/pwdgen=129 https://tools.ietf.org/html/rfc972 elif (dport == "129") and (Payload == "\n"): ReportId("UC", sIP, "UDP_" + dport, "open", "pwdgen/client") ### IPV4/UDPv4/netbios-ns=137 query elif dport == "137" and p.haslayer(NBNSQueryRequest): if dMAC == "ff:ff:ff:ff:ff:ff": #broadcast ReportId("UC", sIP, "UDP_" + dport, "open", "netbios-ns/broadcastclient") elif (Payload != None) and (Payload.find('CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') > -1): #wildcard ReportId("UC", sIP, "UDP_" + dport, "open", "netbios-ns/wildcardclient") else: ReportId("UC", sIP, "UDP_" + dport, "open", "netbios-ns/unicastclient") UnhandledPacket(p) ### IPV4/UDPv4/netbios-ns=137 response elif sport == "137" and p.haslayer(NBNSQueryResponse): netbios_hostname = p[NBNSQueryResponse].RR_NAME.rstrip() ReportId("US", sIP, "UDP_" + sport, "open", "netbios-ns") ReportId("NA", sIP, "PTR", netbios_hostname, "netbios-ns") ShowPacket(p, "Netbios-ns response", HonorQuit) ### IPV4/UDPv4/netbios-dgm=138 query elif (sport == "138") and (dport == "138") and p.haslayer(NBTDatagram): netbios_hostname = p[NBTDatagram].SourceName.rstrip() ReportId("US", sIP, "UDP_" + dport, "open", "netbios-dgm") ReportId("NA", sIP, "PTR", netbios_hostname, "netbios-dgm") elif sport == "138" and p.haslayer(NBTDatagram): netbios_hostname = p[NBTDatagram].SourceName.rstrip() ReportId("US", sIP, "UDP_" + sport, "open", "netbios-dgm") ReportId("NA", sIP, "PTR", netbios_hostname, "netbios-dgm") elif dport == "138" and p.haslayer(NBTDatagram): netbios_hostname = p[NBTDatagram].SourceName.rstrip() ReportId("NA", sIP, "PTR", netbios_hostname, "netbios-dgm") if dMAC == "ff:ff:ff:ff:ff:ff": #broadcast ReportId("UC", sIP, "UDP_" + dport, "open", "netbios-dgm/broadcastclient") else: ReportId("UC", sIP, "UDP_" + dport, "open", "netbios-dgm/unicastclient") ### IPV4/UDPv4/SNMP=161 elif dport == "161" and p.haslayer(SNMP) and (p.haslayer(SNMPget) or p.haslayer(SNMPbulk) or p.haslayer(SNMPvarbind)): if ShowCredentials: snmp_community_string = remove_control_characters(str(p[SNMP].community)).strip(' \t\r\n\0') ReportId("UC", sIP, "UDP_" + dport, "open", "snmp community string:" + snmp_community_string) else: ReportId("UC", sIP, "UDP_" + dport, "open", '') elif sport == "161" and p.haslayer(SNMP) and p.haslayer(SNMPresponse): if ShowCredentials: snmp_community_string = remove_control_characters(str(p[SNMP].community)).strip(' \t\r\n\0') ReportId("US", sIP, "UDP_" + sport, "open", "snmp community string:" + snmp_community_string) else: ReportId("US", sIP, "UDP_" + sport, "open", '') elif sport == "161" or dport == "161": UnhandledPacket(p) ### IPv4/UDPv4/isakmp=500 elif (sport == "500") and (dport == "500") and isinstance(p[ISAKMP], ISAKMP) and (p[ISAKMP].init_cookie != ''): ReportId("US", sIP, "UDP_" + sport, "open", "isakmp/generic") ### IPv4/UDPv4/biff=512 elif dport == "512": if (Payload != None) and (Payload.find('@') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "biff/client") else: UnhandledPacket(p) ### IPv4/UDPv4/syslog=514 https://www.ietf.org/rfc/rfc3164.txt elif dport == "514" and Payload != None and Payload.startswith('<') and (Payload[2] == '>' or Payload[3] == '>' or Payload[4] == '>'): ReportId("UC", sIP, "UDP_" + dport, "open", "syslog/client") ReportId("US", dIP, "UDP_" + dport, "open", "syslog/server not confirmed") hostname_and_process = SyslogMatch.search(Payload) if (hostname_and_process != None) and (len(hostname_and_process.groups()) >= 2): syslog_hostname = hostname_and_process.group(1) ReportId("NA", sIP, "PTR", syslog_hostname, "syslog") process_name = hostname_and_process.group(2) ReportId("IP", sIP, "IP", "live", 'running process: ' + process_name) else: #ShowPacket(p, "Syslog that does not match regex", HonorQuit) UnhandledPacket(p) ### IPv4/UDPv4/snmp on alternate ports elif (dport in snmp_altport) and (Payload != None) and (Payload.find('public') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "snmp-altport/client") ### IPv4/UDPv4/ibm-db2=523 client elif (dport == "523") and (Payload != None) and (Payload.find('DB2GETADDR') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "ibm-db2/clientscanner") ### IPv4/UDPv4/SIP sipvicious scanner and other SIP clients. https://www.nurango.ca/blog/sipvicious-the-not-so-friendly-scanner , http://www.hackingvoip.com/presentations/sample_chapter3_hacking_voip.pdf p54 #We used to look for 'User-Agent: friendly-scanner', but this is for sipvicious only. 'Via: SIP/2.0/UDP ' is more general. # https://github.com/EnableSecurity/sipvicious elif (dport in sip_altport) and (Payload != None) and (Payload.find('Via: SIP/2.0/UDP ') > -1): base_description = "sip/client" additional_info = "" num_1111s = 0 FromMatch = SIPFromMatch.search(Payload) if (FromMatch != None) and (len(FromMatch.groups()) >= 2): additional_info = additional_info + " From:" + FromMatch.group(1) + "(" + FromMatch.group(2) + ")" if FromMatch.group(1) in ("100", "132") and FromMatch.group(2) == "1.1.1.1": num_1111s += 1 ToMatch = SIPToMatch.search(Payload) if (ToMatch != None) and (len(ToMatch.groups()) >= 2): additional_info = additional_info + " To:" + ToMatch.group(1) + "(" + ToMatch.group(2) + ")" if ToMatch.group(1) in ("100", "132") and ToMatch.group(2) == "1.1.1.1": num_1111s += 1 if num_1111s == 2: base_description = 'sipscanner/client' ReportId("UC", sIP, "UDP_" + dport, "open", base_description + additional_info) ### IPv4/UDPv4/626 serialnumberd https://svn.nmap.org/nmap/nmap-payloads elif (dport == "626") and (Payload == 'SNQUERY: 127.0.0.1:AAAAAA:xsvr'): #nmap serialnumberd scan ReportId("UC", sIP, "UDP_" + dport, "open", "serialnumberd/clientscanner likely nmap scan") ### IPv4/UDPv4/636,992,993 make sure this follows snmp_altport line Payload contains \x03www\x03163\x03com elif dport in ("21", "22", "23", "25", "49", "80", "102", "110", "143", "636", "992", "993", "995") and (Payload != None) and (Payload.find(www163com_payload) > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "scan_www163com/client") ### IPv4/UDPv4/winpopup elif (dport == "1026") or (dport == "1027") or (dport == "1028"): #winpopup spam client if (Payload != None) and ((Payload.find('Download Registry Update from:') > -1) or (Payload.find('CRITICAL ERROR MESSAGE! - REGISTRY DAMAGED AND CORRUPTED.') > -1) or (Payload.find('Your system registry is corrupted and needs to be cleaned immediately.') > -1) or (Payload.find('CRITICAL SYSTEM ERRORS') > -1)): ReportId("UC", sIP, "UDP_" + dport, "open", "winpopup/spamclient") else: UnhandledPacket(p) ### IPv4/UDPv4/udp1124=1124 used by printers elif (dport == "1124") and (dMAC == "ff:ff:ff:ff:ff:ff") and (Payload != None) and (Payload.find('std-scan-discovery-all') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "udp1124/broadcast") ### IPv4/UDPv4/search-agent=1234 used by stora NAS elif (dport == "1234") and (dMAC == "ff:ff:ff:ff:ff:ff") and (Payload != None) and (Payload.find('Hello there. I am at ') > -1): HostnameMatch = StoraHostnameMatch.search(Payload) if (HostnameMatch != None) and (len(HostnameMatch.groups()) >= 1): ReportId("UC", sIP, "UDP_" + dport, "open", "stora_nas_scan/broadcast hostname: " + HostnameMatch.group(1)) else: ReportId("UC", sIP, "UDP_" + dport, "open", "stora_nas_scan/broadcast") ### IPv4/UDPv4/mssql=1434 elif dport == "1434": #Probable mssql attack if (Payload != None) and (Payload.find('Qh.dll') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "mssql/clientattack") else: UnhandledPacket(p) ### IPv4/UDPv4/radius=1812 elif (sport == "1812") and p.haslayer(Radius): ReportId("US", sIP, "UDP_1812", "open", 'radius/server') elif (dport == "1812") and p.haslayer(Radius): ReportId("UC", sIP, "UDP_1812", "open", 'radius/client') elif (sport == "1813") and (dport == "1900"): #Scapy misparses this as Radius accounting, when it's SSDP. Ignore. pass ### IPv4/UDPv4=17/ssdp=1900 https://embeddedinn.wordpress.com/tutorials/upnp-device-architecture/ elif (dport == "1900") and dIP in ("239.255.255.250", "ff02::c", "ff05::c", "ff08::c", "ff0e::c") and (Payload != None) and (Payload.startswith('M-SEARCH')): #ssdp discover ReportId("UC", sIP, "UDP_" + dport, "open", "ssdp-discovery/multicastclient") elif (dport == "1900") and (Payload != None) and (Payload.startswith('M-SEARCH')): #ssdp discover ReportId("UC", sIP, "UDP_" + dport, "open", "ssdp-discovery/client") elif (dport == "1900") and dIP in ("239.255.255.250", "ff02::c", "ff05::c", "ff08::c", "ff0e::c") and (Payload != None) and (Payload.startswith('NOTIFY')): #ssdp announcement additional_info = '' LocationMatch = SSDPLocationMatch.search(Payload) if (LocationMatch != None) and (len(LocationMatch.groups()) >= 1): additional_info = additional_info + ' SSDP Location: ' + str(LocationMatch.group(1)).strip() ServerMatch = SSDPServerMatch.search(Payload) if (ServerMatch != None) and (len(ServerMatch.groups()) >= 1): additional_info = additional_info + ' SSDP Server: ' + str(ServerMatch.group(1)).replace(',', ' ').strip() ReportId("UC", sIP, "UDP_" + dport, "open", "ssdp-announce/client" + additional_info) elif dport in ("1900","11211") and (Payload != None) and (Payload == 'GET / HTTP/1.1\r\n\r\n'): #bogus GET packet ReportId("UC", sIP, "UDP_" + dport, "open", "ssdp-bogus-get/clientscanner") elif (dport == "1900") and dIP in ("239.255.255.250", "ff02::c", "ff05::c", "ff08::c", "ff0e::c"): #ssdp ShowPacket(p, "IPv4/UDPv4/1900-multicast SSDP unknown method", HonorQuit) ### IPv4/UDPv4/digiman=2362 elif (dport == "2362") and (Payload != None) and Payload.startswith('DIGI'): ReportId("UC", sIP, "UDP_" + dport, "open", "digiman/client") ### IPv4/UDPv4/sybase=2638 elif (dport == "2638") and (Payload != None) and (Payload.find('CONNECTIONLESS_TDS') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "sybase_scan/client") ### IPv4/UDPv4/enpc=3289 elif (dport == "3289") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('EPSON')): ReportId("UC", sIP, "UDP_" + dport, "open", "enpc/broadcast") else: UnhandledPacket(p) ### IPv4/UDPv4/upnp-discovery=3702 elif (dport == "3702") and (Payload != None) and (Payload.startswith(' -1): if (dIP == "239.255.255.250"): ReportId("UC", sIP, "UDP_" + dport, "open", "upnp-discovery/broadcastclient") else: ReportId("UC", sIP, "UDP_" + dport, "open", "upnp-discovery/client") ### IPv4/UDPv4/xpl=3865 elif (dport == "3865") and (dIP == "255.255.255.255"): #XPL, http://wiki.xplproject.org.uk/index.php/Main_Page ReportId("UC", sIP, "UDP_" + dport, "open", "xpl/client") ### IPv4/UDPv4/bfd-control=3784 https://tools.ietf.org/html/rfc5881 elif (dport == "3784") and (p[IP].ttl == 255): ReportId("UC", sIP, "UDP_" + dport, "open", "bfd-control/client") ### IPv4/UDPv4/vertx=4070 https://github.com/brad-anton/VertX/blob/master/VertX_Query.py elif (dport == "4070") and (Payload == 'discover;013;'): ReportId("UC", sIP, "UDP_" + dport, "open", "vertx/client") ### IPv4/UDPv4/drobo=5002 used by drobo NAS elif (dport == "5002") and (Payload != None) and Payload.startswith('DRINETTM'): if dMAC == "ff:ff:ff:ff:ff:ff": ReportId("UC", sIP, "UDP_" + dport, "open", "drobo_nas_scan/broadcast") else: ReportId("UC", sIP, "UDP_" + dport, "open", "drobo_nas_scan/client") ### IPv4/UDPv4/vonage elif (sport == "5061") and (dport == "5061") and (dIP in ("216.115.30.28", "69.59.227.77", "69.59.232.33", "69.59.240.84")): #Vonage SIP client if (Payload != None) and (Payload.find('.vonage.net:5061 SIP/2.0') > -1): SipMatch = SipPhoneMatch.search(Payload) if (SipMatch != None) and (len(SipMatch.groups()) >= 1): ReportId("UC", sIP, "UDP_" + dport, "open", "sip/vonage_client, phone number: " + SipMatch.group(1)) else: ReportId("UC", sIP, "UDP_" + dport, "open", "sip/vonage_client") else: UnhandledPacket(p) elif (sport == "5061") and (dport == "5061") and (sIP in ("216.115.30.28", "69.59.227.77", "69.59.232.33", "69.59.240.84")): #Vonage SIP server if (Payload != None) and (Payload.find('.vonage.net:5061>') > -1): ReportId("US", sIP, "UDP_" + sport, "open", "sip/vonage_server") else: UnhandledPacket(p) ### IPv4/UDPv4/nat-pmp=5351 http://miniupnp.free.fr/nat-pmp.html , https://tools.ietf.org/html/rfc6886 elif (dport == "5351"): if (Payload != None) and Payload.startswith('0000'.decode('hex')): #\x00\x00 is Public address request ReportId("UC", sIP, "UDP_" + dport, "open", "nat-pmp-public-address-discovery/client") elif (Payload != None) and ( (Payload == '0001'.decode('hex')) or (Payload == '0002'.decode('hex')) ): #\x00\x0[12] is mapping request ReportId("UC", sIP, "UDP_" + dport, "open", "nat-pmp-mapping-request/client") else: ShowPacket(p, "IPv4/UDPv4/5351 nat-pmp unknown payload", HonorQuit) ### IPv4/UDPv4/llmnr=5355 query elif (dport == "5355") and (dIP == "224.0.0.252") and (p[IP].ttl in (1, 255)) and p.haslayer(LLMNRQuery) and (p[LLMNRQuery].qr == 0): #llmnr (link-local multicast node resolution) pass ### IPv4/UDPv4/llmnr=5355 response elif (dport == "5355") and (dIP == "224.0.0.252") and (p[IP].ttl in (1, 255)): #llmnr (link-local multicast node resolution) ShowPacket(p, "IPv4/UDPv4/5355-224.0.0.252 llmnr not query", HonorQuit) #Can we pass this off to PUDR? elif dport == "5355": #unicast fe80->fe80 llmnr (link-local multicast node resolution) ShowPacket(p, "IPv4/UDPv4/5355 unicast llmnr not to 224.0.0.252", HonorQuit) ### IPv4/UDPv4/corosync=5405 used by corosync elif (dport == "5405") and (dMAC == "ff:ff:ff:ff:ff:ff"): ReportId("UC", sIP, "UDP_" + dport, "open", "corosync/broadcast") ### IPv4/UDPv4/pcanywherestat=5632 client elif (dport == "5632") and (Payload != None) and (Payload.find('NQ') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "pcanywherestat/clientscanner") elif (sport == "6515") and (dport == "6514") and (dIP == "255.255.255.255"): #mcafee ASaP broadcast, looking for a proxy out. http://www.myasap.de/intl/EN/content/virusscan_asap/faq_new.asp if (Payload != None) and (Payload.find(' -1): ReportId("UC", sIP, "UDP_" + dport, "open", "coap/client") ### IPv4/UDPv4/bt-lpd=6771 https://security.stackexchange.com/questions/102766/wireshark-reveals-suspicious-udp-traffic-sending-to-a-bogon-ip-address elif (dport == "6771") and (dIP == "239.192.152.143") and (Payload != None) and (Payload.startswith('BT-SEARCH * HTTP/1.1')): ReportId("UC", sIP, "UDP_" + dport, "open", "bt-lpd/client") ### IPv4/UDPv4/unreal_status=7778 https://arp242.net/weblog/online_unreal_tournament_server_browser_with_pcntl_fork() elif (dport == "7778") and Payload.startswith('\\status\\'): ReportId("UC", sIP, "UDP_" + dport, "open", "unreal_status/client") ### IPv4/UDPv4/canon-bjnp2=8610 elif (dport == "8610") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('MFNP')): ReportId("UC", sIP, "UDP_" + dport, "open", "udp8610/broadcast") else: UnhandledPacket(p) ### IPv4/UDPv4/canon-bjnp2=8612 https://support.usa.canon.com/kb/index?page=content&id=ART109227 elif (dport == "8612") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('BJNP')): ReportId("UC", sIP, "UDP_" + dport, "open", "canon-bjnp2/broadcast") else: UnhandledPacket(p) ### IPv4/UDPv4/itunesdiscovery=8765 elif dport == "8765": #XPL, http://wiki.xplproject.org.uk/index.php/Main_Page ReportId("UC", sIP, "UDP_" + dport, "open", "itunesdiscovery/broadcast") ### IPv4/UDPv4/aoldns elif (sport in ("9052", "9053", "9054")) and (sIP in ("205.188.146.72", "205.188.157.241", "205.188.157.242", "205.188.157.243", "205.188.157.244", "64.12.51.145", "64.12.51.148", "149.174.54.131")): #Possibly AOL dns response if (Payload != None) and (Payload.find('dns-01') > -1): ReportId("US", sIP, "UDP_" + sport, "open", "aoldns/server") else: UnhandledPacket(p) ### IPV4/UDPv4/memcached=11211 https://blog.cloudflare.com/memcrashed-major-amplification-attacks-from-port-11211/ https://github.com/memcached/memcached/blob/master/doc/protocol.txt elif dport == "11211": if (Payload != None) and ((Payload.find('gets ') > -1) or (Payload.find('stats') > -1)): ReportId("UC", sIP, "UDP_11211", "open", 'memcached: Likely spoofed and DDOSed source IP') else: ShowPacket(p, "IPV4/UDPv4/memcached=11211 request but non-gets", HonorQuit) elif sport == "11211": ReportId("US", sIP, "UDP_11211", "open", 'memcached server') ### IPV4/UDPv4/zmapscanner=1707,3269,3544,6619,1121[45] https://zmap.io/ , https://github.com/zmap/zmap elif dport in ("80", "563", "655", "830", "898", "989", "990", "991", "992", "995", "1293", "1707", "1900", "2484", "3269", "3544", "4843", "5000", "5031", "6619", "9899", "11214", "11215", "18091", "18092") and (Payload == 'GET / HTTP/1.1\r\nHost: www\r\n\r\n'): ReportId("UC", sIP, "UDP_" + dport, "open", 'zmapscanner/client') ### IPv4/UDPv4/makerbotdiscovery=12307 https://github.com/gryphius/mini-makerbot-hacking/blob/master/doc/makerbotmini.md elif (sport == "12309") and (dport == "12307") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('{"command": "broadcast"')): ReportId("UC", sIP, "UDP_" + dport, "open", "makerbotdiscovery/broadcast") ### IPv4/UDPv4/dropbox=17500 http://whatportis.com/ports/17500_dropbox-lansync-protocol-db-lsp-used-to-synchronize-file-catalogs-between-dropbox-clients-on-your-local-network elif (sport == "17500") and (dport == "17500"): if (Payload != None) and (Payload.find('"host_int"') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "dropbox/client") else: UnhandledPacket(p) ### IPv4/UDPv4/googlemeet=19302-19309 elif (dport in meet_ports) and (dIP in meet_hosts): ReportId("UC", sIP, "UDP_" + dport, "open", "googlemeet/client") elif (sport in meet_ports) and (sIP in meet_hosts): ReportId("US", sIP, "UDP_" + sport, "open", "googlemeet/server") elif (dport in meet_ports): ReportId("UC", sIP, "UDP_" + dport, "open", "googlemeet/client missing dIP:" + dIP) elif (sport in meet_ports): ReportId("US", sIP, "UDP_" + sport, "open", "googlemeet/server missing sIP:" + sIP) ### IPv4/UDPv4/skype=all over the place elif (dport in skype_ports) and (dIP in skype_hosts): ReportId("UC", sIP, "UDP_" + dport, "open", "skype/client") elif (sport in skype_ports) and (sIP in skype_hosts): ReportId("US", sIP, "UDP_" + sport, "open", "skype/server") elif (dIP in skype_hosts): ReportId("UC", sIP, "UDP_" + dport, "open", "skype/client, missing dport:" + dport) elif (sIP in skype_hosts): ReportId("US", sIP, "UDP_" + sport, "open", "skype/server, missing sport:" + sport) elif (dport in skype_ports): ReportId("UC", sIP, "UDP_" + dport, "open", "skype/client missing dIP:" + dIP) elif (sport in skype_ports): ReportId("US", sIP, "UDP_" + sport, "open", "skype/server missing sIP:" + sIP) ### IPv4/UDPv4/pyzor=24441 elif dport == "24441": #Pyzor if (Payload != None) and (Payload.find('User:') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "pyzor/client") else: UnhandledPacket(p) ### IPv4/UDPv4/unknown26079 elif (sport == "26079") or (dport == "26079") or sIP in ("52.179.141.141", "100.112.42.45") or dIP in ("52.179.141.141", "100.112.42.45"): UnhandledPacket(p) ### IPv4/UDPv4/halflife=27005 and others elif (sport == "27005") and (dport in ('27015', '27016', '27017')): #Halflife client live game ReportId("UC", sIP, "UDP_" + dport, "open", "halflife/client") elif (dport == "27013") and (dIP == "207.173.177.12"): #variable payload, so can't (Payload != None) and (Payload.find('Steam.exe') > -1) #Halflife client ReportId("UC", sIP, "UDP_" + dport, "open", "halflife/client") elif (sport == "27013") and (sIP == "207.173.177.12"): #halflife server ReportId("US", sIP, "UDP_" + sport, "open", "halflife/server") elif (sport in '27015', '27016', '27017') and (dport == "27005"): #halflife server live game ReportId("US", sIP, "UDP_" + sport, "open", "halflife/server") elif dport in ("27015", "27016", "27025", "27026"): #Variable payload, so can't: (Payload != None) and (Payload.find('basic') > -1) #Halflife client ReportId("UC", sIP, "UDP_" + dport, "open", "halflife/client") elif sport in ("27015", "27016", "27025", "27026"): #Variable payload, so can't: (Payload != None) and (Payload.find('basic') > -1) #Halflife client ReportId("US", sIP, "UDP_" + sport, "open", "halflife/server") elif (dport == "27017") and (dIP in SteamFriendsServers): #Steamfriends client if (Payload != None) and (Payload.find('VS01') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "steamfriends/client") else: UnhandledPacket(p) elif (sport == "27017") and (sIP in SteamFriendsServers): #Steamfriends server if (Payload != None) and (Payload.find('VS01') > -1): ReportId("US", sIP, "UDP_" + sport, "open", "steamfriends/server") else: UnhandledPacket(p) elif sport in ("21020", "21250", "27016", "27017", "27018", "27030", "27035", "27040", "28015"): #halflife server if (Payload != None) and (Payload.find('Team Fortress') > -1): ReportId("US", sIP, "UDP_" + sport, "open", "halflife/server") else: UnhandledPacket(p) elif sport == "27019": #halflife server ReportId("US", sIP, "UDP_" + sport, "open", "halflife/server") ### IPv4/UDPv4/steam-ihs-discovery=27036 https://codingrange.com/blog/steam-in-home-streaming-discovery-protocol elif (sport == "27036") and (dport == "27036") and (dIP == "255.255.255.255"): if (Payload != None) and (Payload.startswith(stream_ihs_discovery_header)): ReportId("UC", sIP, "UDP_" + dport, "open", "stream-ihs-discovery-broadcast/client") else: UnhandledPacket(p) elif dport in halflife_altport: #Halflife client if (Payload != None) and (Payload.find('Source Engine Query') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "halflife/client") else: UnhandledPacket(p) ### IPv4/UDPv4/lima=25213 https://support.meetlima.com/hc/en-us/articles/115004950326-README-document elif dport == "25213": if (Payload != None) and (Payload.startswith('ZVPN')): ReportId("UC", sIP, "UDP_" + dport, "open", "lima/client") else: UnhandledPacket(p) ### IPv4/UDPv4/openarena=27960 https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=665656 , http://openarena.ws/board/index.php?topic=4391.0 , http://blog.alejandronolla.com/2013/06/24/amplification-ddos-attack-with-quake3-servers-an-analysis-1-slash-2/ elif (dport == "27960") and (Payload != None) and Payload.startswith('FFFFFFFF'.decode('hex') + 'getstatus'): ReportId("UC", sIP, "UDP_" + dport, "open", 'openarena-quake3/client getstatus: Likely spoofed and DDOSed source IP') ### IPv4/UDPv4/hap=28784 https://hal.inria.fr/hal-01456891/document elif (dport == "28784") and (Payload != None) and Payload.startswith('HAP'): ReportId("UC", sIP, "UDP_" + dport, "open", 'hap/client') ### IPv4/UDPv4/spotify-broadcast=57621 https://mrlithium.blogspot.com/2011/10/spotify-and-opting-out-of-spotify-peer.html elif (dport == "57621") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('SpotUdp')): ReportId("UC", sIP, "UDP_" + dport, "open", "spotify-broadcast/client") else: UnhandledPacket(p) #FIXME - interesting issue; the ttl<5 test will catch traceroutes coming into us, but not ones we're creating to go out. Hmmm. #We used to also specify and (p[IP].ttl <= 5) but this leaves a bunch of higher ttl packets as unhandled. elif ((dport >= "33434") and (dport <= "33524")): #udptraceroute client ReportId("UC", sIP, "UDP_33434", "open", "udptraceroute/client") ### IPv4/UDPv4/lima=33612 https://support.meetlima.com/hc/en-us/articles/115004950326-README-document elif dport == "33612": if (Payload != None) and (Payload.startswith('LIMA')): ReportId("UC", sIP, "UDP_" + dport, "open", "lima/client") else: UnhandledPacket(p) elif dport == "40348": if (Payload != None) and (Payload.find('HLS') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "halflife/client") else: UnhandledPacket(p) ### IPv4/UDPv4/crestron-cip=41794 https://media.defcon.org/DEF%20CON%2026/DEF%20CON%2026%20presentations/Ricky%20Lawshae/DEFCON-26-Lawshae-Who-Controls-the-Controllers-Hacking-Crestron.pdf elif (sport == "41794") and (dport == "41794") and (Payload != None) and Payload.startswith('14000000010400030000'.decode('hex') + 'hostname'): ReportId("UC", sIP, "UDP_" + dport, "open", 'crestron-cip/clientscanner') ### IPv4/UDPv4/zengge-bulb=48899 client https://github.com/vikstrous/zengge-lightcontrol/blob/master/README.md elif (dport == "48899") and (Payload != None) and (Payload.find('HF-A11ASSISTHREAD') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "zengge-bulb/clientscanner") ### IPv4/UDPv4/linkproof=49153 client https://eromang.zataz.com/2010/04/28/suc007-activities-on-49153udp-linkproof-proximity-advanced/ elif (dport == "49153") and (Payload != None) and (Payload.startswith('linkproof.proximity.advanced')): ReportId("UC", sIP, "UDP_" + dport, "open", "radware-linkproof/clientscanner") ### IPv4/UDPv4/netis-backdoor-53413=53413 client, exploting Netis router backdoor: https://isc.sans.edu/forums/diary/Surge+in+Exploit+Attempts+for+Netis+Router+Backdoor+UDP53413/21337/ elif dport == "53413": #To limit this signature to just shellcode, add the following tests to this line: and (Payload != None) and (Payload.find('; chmod 777 ') > -1) ReportId("UC", sIP, "UDP_" + dport, "open", "netis-backdoor-53413/client") ### IPv4/UDPv4/brother-announce=54925 and 54926 used by brother printers http://ww2.chemistry.gatech.edu/software/Drivers/Brother/MFC-9840CDW/document/ug/usa/html/sug/index.html?page=chapter7.html elif (dport in ("54925", "54926")) and dIP in ("255.255.255.255", "ff02::1") and (dMAC in ("ff:ff:ff:ff:ff:ff", "33:33:00:00:00:01")) and (Payload != None) and (Payload.find('NODENAME=') > -1): BrotherMatch = BrotherAnnounceMatch.search(Payload) if (BrotherMatch != None) and (len(BrotherMatch.groups()) >= 4): #In the packets I've seen, groups 1, 2, and 3 are ip addresses (1 ipv4 and 2 ipv6). Group 4 is a nodename ("BRWF" + uppercase mac address, no colons) ReportId("UC", sIP, "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) ReportId("UC", BrotherMatch.group(1), "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) ReportId("UC", BrotherMatch.group(2), "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) ReportId("UC", BrotherMatch.group(3), "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) else: ReportId("UC", sIP, "UDP_" + dport, "open", "brother-announce/broadcast") ### IPv4/UDPv4/probes with empty payloads elif dport in empty_payload_ports and Payload == '': ReportId("UC", sIP, "UDP_" + dport, "open", "empty-payload/client") elif Payload == '': ReportId("UC", sIP, "UDP_" + dport, "open", "empty-payload/client Port not registered") UnhandledPacket(p) ### IPv4/UDPv4/bt-dht http://www.bittorrent.org/beps/bep_0005.html elif (Payload != None) and Payload.find(':id') > -1 and ((Payload.find(':info_hash') > -1 and Payload.find(':get_peers') > -1) or Payload.find(':ping') > -1): #Unfortunately, can run on any port ReportId("UC", sIP, "UDP_" + dport, "open", 'bt-dht-scan/clientscanner') elif (Payload != None) and Payload.find(':id') > -1 and Payload.find(':token') > -1 and (Payload.find(':nodes') > -1 or Payload.find(':values')): ReportId("US", sIP, "UDP_" + sport, "open", 'bt-dht/server') elif (Payload != None) and Payload.startswith(a0_string): #Payload starting with A\x00 UnhandledPacket(p) elif dport in SecUDPPortNames: UnhandledPacket(p) ReportId("UC", sIP, "UDP_" + dport, "open", str(SecUDPPortNames[dport]) + "/client") elif sport in SecUDPPortNames: UnhandledPacket(p) ReportId("US", sIP, "UDP_" + sport, "open", str(SecUDPPortNames[sport]) + "/server") elif p[IP].frag > 0: UnhandledPacket(p) elif (sIP == "207.46.51.74") or (sIP == "65.55.251.10"): #Bigfish.com - dns? UnhandledPacket(p) elif sIP == "61.215.106.146": #junk UnhandledPacket(p) elif (sport == "53") and not p.haslayer(DNS): #source port 53, but no parsed DNS layer. Seen this in large packets with Raw immediately following UDP. UnhandledPacket(p) elif sport == "53": #source port 53. I've seen some coming back from port 53 with qr=0, request. Hmmm. UnhandledPacket(p) elif (sport == "53") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].rcode == 2): #source port 53, but the server is sending back server-failure. UnhandledPacket(p) elif ((sport == "51999") and (dport == "49901")) or ((sport == "47798") and (dport == "24223")): pass #FIXME - removeme after testing else: ShowPacket(p, "IPv4/UDPv4/unhandled port", HonorQuit) ### IPv4/UDPv4, probably truncated/fragmented elif p[IP].proto == 17: #This is the case where the protocol is listed as 17, but there's no complete UDP header. Quite likely a 2nd or future fragment. UnhandledPacket(p) ### IPv4/IPSecv4/GRE #GRE elif p[IP].proto == 47 and p.haslayer(GRE): ReportId("PC", sIP, "PROTO_" + str(p[IP].proto), "open", "gre/client") if p[GRE].proto == 2048: #0x800==2048==IPv4 if p[GRE].payload: encap_packet = p[GRE].payload processpacket(encap_packet) else: UnhandledPacket(p) elif p[GRE].proto == 25944: #0x6558==25944==Trans Ether Bridging if p.haslayer(Raw): encap_packet_raw = p[Raw].load encap_packet = Ether(encap_packet_raw) processpacket(encap_packet) else: UnhandledPacket(p) elif p[GRE].proto == 34827: #0x880B==34827==PPP if p.haslayer(Raw): #Sample payload: \x00\x08:v\xff\x03\x00!E\x00\x00c\x00\x00 Hack; strip off first 8 bytes and interpret the rest as IP. Similar packet has 4 byte intro, so we test that too. encap_packet_raw = None #FIXME - move all decodes to the start and save as strings for use in the program if p[GRE].load[4:6] == '4500'.decode('hex'): encap_packet_raw = p[GRE].load[4:] elif p[GRE].load[8:10] == '4500'.decode('hex'): encap_packet_raw = p[GRE].load[8:] if encap_packet_raw: encap_packet = IP(encap_packet_raw) processpacket(encap_packet) else: ShowPacket(p, "GRE raw does not appear to have E\x00\x00", HonorQuit) else: UnhandledPacket(p) else: ShowPacket(p, "GRE unhandled proto", HonorQuit) ### IPv4/IPSecv4/ESP #ESP (IPSEC) elif p[IP].proto == 50: ReportId("PC", sIP, "PROTO_" + str(p[IP].proto), "open", "ipsec-esp/client") ReportId("PS", dIP, "PROTO_" + str(p[IP].proto), "open", "ipsec-esp/server unconfirmed") UnhandledPacket(p) ### IPv4/IPSecv4/AH #AH (IPSEC) elif p[IP].proto == 51: ReportId("PC", sIP, "PROTO_" + str(p[IP].proto), "open", "ipsec-ah/client") ReportId("PS", dIP, "PROTO_" + str(p[IP].proto), "open", "ipsec-ah/server unconfirmed") ShowPacket(p, "IPSec-ESP", HonorQuit) UnhandledPacket(p) ### IPv4/EIGRPv4 EIGRP = Enhanced Interior Gateway Routing Protocol elif (p[IP].proto == 88) and dIP in ("224.0.0.10", "FF02:0:0:0:0:0:0:A"): #224.0.0.10 for IPv4 EIGRP Routers, FF02:0:0:0:0:0:0:A for IPv6 EIGRP Routers ReportId("RO", sIP, "EIGRP", "router", "") elif p[IP].proto == 88: #Different target address format, perhaps? ShowPacket(p, "IPv4/EIGRP unknown target IP", HonorQuit) ### IPv4/OSPFv4 elif (p[IP].proto == 89) and (dIP == "224.0.0.5"): #OSPF = Open Shortest Path First UnhandledPacket(p) ### IPv4/PIMv4 elif (p[IP].proto == 103) and (dIP == "224.0.0.13"): #PIM = Protocol Independent Multicast UnhandledPacket(p) ### IPv4/VRRPv4 elif (p[IP].proto == 112) and (dIP == "224.0.0.18"): #VRRP = virtual router redundancy protocol UnhandledPacket(p) ### IPv4/SSCOPMCE elif p[IP].proto == 128: UnhandledPacket(p) else: #http://www.iana.org/assignments/protocol-numbers #Look up protocol in /etc/protocols ShowPacket(p, "Other IP protocol (" + str(p[IP].src) + "->" + str(p[IP].dst) + "): " + str(p[IP].proto), HonorQuit) #Look up other ethernet types in: # http://en.wikipedia.org/wiki/EtherType # /etc/ethertypes # http://www.iana.org/assignments/ethernet-numbers # http://standards.ieee.org/develop/regauth/ethertype/eth.txt # http://www.cavebear.com/archive/cavebear/Ethernet/type.html elif ((p.haslayer(CookedLinux) and p[CookedLinux].proto == 0x800) or (p.haslayer(Ether) and ((p[Ether].type == 0x0800) or (p[Ether].type == 0x8100)))): #Like above, but has no IP layer. Probably truncated packet at the end of a still-running capture. UnhandledPacket(p) ### 2114: Wake-on-lan elif p.haslayer(Ether) and p[Ether].type == 0x0842: UnhandledPacket(p) ### 9728: Unknown elif p.haslayer(Ether) and p[Ether].type == 0x2600: UnhandledPacket(p) ### IPv6 #FIXME - add checks for CookedLinux and Ipv6 as well as Ether+IPv6 elif (p.haslayer(Ether) and p[Ether].type == 0x86DD) and p.haslayer(IPv6) and isinstance(p[IPv6], IPv6): sIP = str(p[IPv6].src) dIP = str(p[IPv6].dst) if p.getlayer(Raw): Payload = p.getlayer(Raw).load else: Payload = "" ### IPv6/IPv6ExtHdrHopByHop=0 Hop-by-hop option header if p[IPv6].nh == 0 and p[IPv6].hlim == 1 and p.getlayer(IPv6ExtHdrHopByHop) and p[IPv6ExtHdrHopByHop].nh == 58 and (p.haslayer(ICMPv6MLQuery) or p.haslayer(ICMPv6MLReport)): #0 is Hop-by-hop options pass UnhandledPacket(p) #FIXME - try to extract Multicast info later. #if p[ICMPv6MLQuery].type == 130: #MLD Query # if p[ICMPv6MLQuery].mladdr == '::' #General query # pass # else: #Multicast-address-specific query # pass #elif p[ICMPv6MLQuery].type == 131: #Multicast Listener Report # pass #elif p[ICMPv6MLQuery].type == 132: #Multicast Listener Done # pass #else: # pass elif p[IPv6].nh == 0 and p.getlayer(IPv6ExtHdrHopByHop) and p[IPv6ExtHdrHopByHop].nh == 58 and isinstance(p[IPv6ExtHdrHopByHop].payload, Raw): #The packet claims to have an ICMPv6 layer, but the following layer is Raw. Ignore. Any chance that scapy is not interpreting the next layer down when it encounters a hop-by-hop? pass elif p[IPv6].nh == 0: ShowPacket(p, "IPv6/IPv6ExtHdrHopByHop = 0; FIXME, intermediate header on its way to the real header", HonorQuit) #https://tools.ietf.org/html/rfc2711 (router alert option) #Specifically "router contains a MLD message": https://tools.ietf.org/html/rfc2710 ### IPv6/TCPv6=6 elif p[IPv6].nh == 6: sport = str(p[TCP].sport) dport = str(p[TCP].dport) if (p[TCP].flags & 0x17) == 0x12: #SYN/ACK (RST and FIN off) CliService = dIP + ",TCP_" + sport if not SynAckSentToTCPClient.has_key(CliService): SynAckSentToTCPClient[CliService] = True #If we've seen a syn sent to this port and have either not seen any SA/R, or we've seen a R in the past: #The last test is for a service that was previously closed and is now open; report each transition once. Service = sIP + ",TCP_" + sport if (SynSentToTCPService.has_key(Service)) and ((not LiveTCPService.has_key(Service)) or (not LiveTCPService[Service])): LiveTCPService[Service] = True ReportId("TS", sIP, "TCP_" + sport, "listening", '') elif (p[TCP].flags & 0x17) == 0x02: #SYN (RST, ACK, and FIN off) Service = dIP + ",TCP_" + dport if not SynSentToTCPService.has_key(Service): SynSentToTCPService[Service] = True elif (p[TCP].flags & 0x07) == 0x01: #FIN (RST and SYN off, ignore ACK) CliService = sIP + ",TCP_" + dport if SynAckSentToTCPClient.has_key(CliService) and ((not LiveTCPClient.has_key(CliService)) or (not LiveTCPClient[CliService])): LiveTCPClient[CliService] = True ReportId("TC", sIP, "TCP_" + dport, "open", '') elif (p[TCP].flags & 0x07) == 0x04: #RST (SYN and FIN off, ignore ACK) #FIXME - handle rst going in the other direction? Service = sIP + ",TCP_" + sport if SynSentToTCPService.has_key(Service) and ((not LiveTCPService.has_key(Service)) or LiveTCPService[Service]): LiveTCPService[Service] = False ReportId("TS", sIP, "TCP_" + sport, "closed", '') elif (p[TCP].flags & 0x17) == 0x10: #ACK (RST, SYN, and FIN off) pass #print p[IPv6].src + ":" + sport + " -> ", p[IPv6].dst + ":" + dport else: ShowPacket(p, "IPv6/TCPv6/UnhandledFlags: " + str(p[TCP].flags), HonorQuit) ### IPv6/UDPv6=17 elif (p[IPv6].nh == 17) and p.haslayer(UDP): udp_layer = p.getlayer(UDP) sport = str(udp_layer.sport) dport = str(udp_layer.dport) SrcService = sIP + ",UDP_" + sport DstService = dIP + ",UDP_" + dport SrcClient = sIP + ",UDP_" + dport ### IPv6/UDPv6=17/DNSv6=53 if dport in PriUDPPortNames: #Client talking to server ReportId("UC", sIP, "UDP_" + dport, "open", str(PriUDPPortNames[dport]) + "/client") elif sport in PriUDPPortNames: #server talking to client ReportId("US", sIP, "UDP_" + sport, "open", str(PriUDPPortNames[sport]) + "/server") #FIXME - add check for "if isinstance(p[DNS], whatevertype): here and at all p[] accesses. elif (sport == "53") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].qr == 1): #qr == 1 is a response process_udp_dns_response(sIP, dIP, SrcService, sport, p[DNS], p) elif (dport == "53") and p.haslayer(DNS) and isinstance(p[DNS], DNS) and (p[DNS].qr == 0): #dns query ReportId("UC", sIP, "UDP_" + dport, "open", "dns/client") elif sport == "80": #udp http response ReportId("US", sIP, "UDP_" + sport, "open", "UDP HTTP") elif dport == "80": #udp http request ReportId("UC", sIP, "UDP_" + dport, "open", "UDP HTTP") ### IPV6/UDPv6/ntp=123 elif ((sport == "123") or (dport == "123")) and p.haslayer(NTPHeader): ntp_stratum = p[NTPHeader].stratum #What comes back in the "id" field is either an IPv4 address of sIP's primary reference (good!) or #the first 4 bytes of the MD5 hash of the IPv6 address of sIP's primary reference (bad.) Without actively #checking, there's no way to distinguish the two cases. https://www.nwtime.org/ntps-refid/ ntp_id = p[NTPHeader].id ntp_ref_id = str(p[NTPHeader].ref_id).rstrip(' \t\r\n\0') if ntp_id: ReportId("US", sIP, "UDP_" + sport, "open", 'stratum=' + str(ntp_stratum) + ' reference=' + str(ntp_id)) ReportId("US", ntp_id, "UDP_" + sport, "open", 'inferred from being a reference but must be checked.') elif ntp_ref_id in known_ntp_refs: ReportId("US", sIP, "UDP_" + sport, "open", 'stratum=' + str(ntp_stratum)) else: ReportId("US", sIP, "UDP_" + sport, "open", 'stratum=' + str(ntp_stratum)) ShowPacket(p, "IPv6/UDPv6/ntp with null reference:_" + str(ntp_ref_id) + "_", HonorQuit) elif (dport == "123") and p.haslayer(NTPPrivate) and p[NTPPrivate].response == 0: #response == 0 is a request if p[NTPPrivate].request_code == 42: #REQ_MON_GETLIST_1 ReportId("UC", sIP, "UDP_123", "open", 'REQ_MON_GETLIST_1: Likely spoofed and DDOSed source IP') else: ShowPacket(p, "IPv4/UDPv4/ntp Mode 7 request but not REQ_MON_GETLIST_1", HonorQuit) elif (sport == "123") and p.haslayer(NTPPrivate) and p[NTPPrivate].response == 1: #response == 1 is a reply if p[NTPPrivate].request_code == 42: #REQ_MON_GETLIST_1 ReportId("US", sIP, "UDP_123", "open", 'REQ_MON_GETLIST_1: Likely middleman in DDOS') else: ShowPacket(p, "IPv4/UDPv4/ntp Mode 7 reply but not REQ_MON_GETLIST_1", HonorQuit) ### IPV6/UDPv6/SNMP=161 elif dport == "161" and p.haslayer(SNMP) and (p.haslayer(SNMPget) or p.haslayer(SNMPbulk)): if ShowCredentials: snmp_community_string = remove_control_characters(str(p[SNMP].community)).strip(' \t\r\n\0') ReportId("UC", sIP, "UDP_" + dport, "open", "snmp/client community string:" + snmp_community_string) else: ReportId("UC", sIP, "UDP_" + dport, "open", 'snmp/client') elif sport == "161" and p.haslayer(SNMP) and p.haslayer(SNMPresponse): if ShowCredentials: snmp_community_string = remove_control_characters(str(p[SNMP].community)).strip(' \t\r\n\0') ReportId("US", sIP, "UDP_" + sport, "open", "snmp/server community string:" + snmp_community_string) else: ReportId("US", sIP, "UDP_" + sport, "open", 'snmp/server') elif sport == "161" or dport == "161": UnhandledPacket(p) ### IPV6/UDPv6/udp_https=443 response elif sport == "443": ReportId("US", sIP, "UDP_" + sport, "open", "UDP HTTPS") ### IPv6/UDPv6=17/DHCPv6=547 request elif (sport == "546") and (dport == "547") and (dIP == "ff02::1:2"): ReportId("UC", sIP, "UDP_" + dport, "open", "UDP DHCPv6") elif (sport == "546") and (dport == "547"): #dhcp request ShowPacket(p, "IPv6/UDPv6/546-547-ff02::1:2 DHCP Request", HonorQuit) ### IPv6/UDPv6=17/DHCPv6=547 reply elif (sport == "547") and (dport == "546"): pass ### IPv4/UDPv4/SIP sipvicious scanner and other SIP clients. https://www.nurango.ca/blog/sipvicious-the-not-so-friendly-scanner , http://www.hackingvoip.com/presentations/sample_chapter3_hacking_voip.pdf p54 #We used to look for 'User-Agent: friendly-scanner', but this is for sipvicious only. 'Via: SIP/2.0/UDP ' is more general. elif (dport in sip_altport) and (Payload != None) and (Payload.find('Via: SIP/2.0/UDP ') > -1): additional_info = "sip/client" FromMatch = SIPFromMatch.search(Payload) if (FromMatch != None) and (len(FromMatch.groups()) >= 2): additional_info = additional_info + " From:" + FromMatch.group(1) + "(" + FromMatch.group(2) + ")" ToMatch = SIPToMatch.search(Payload) if (ToMatch != None) and (len(ToMatch.groups()) >= 2): additional_info = additional_info + " To:" + ToMatch.group(1) + "(" + ToMatch.group(2) + ")" ReportId("UC", sIP, "UDP_" + dport, "open", additional_info) ### IPv6/UDPv6/udp1124=1124 used by printers elif (dport == "1124") and (dMAC == "ff:ff:ff:ff:ff:ff") and (Payload != None) and (Payload.find('std-scan-discovery-all') > -1): ReportId("UC", sIP, "UDP_" + dport, "open", "udp1124/broadcast") ### IPv6/UDPv6=17/ssdp=1900 https://embeddedinn.wordpress.com/tutorials/upnp-device-architecture/ elif (sport == "1813") and (dport == "1900"): #Scapy misparses this as Radius accounting, when it's SSDP. Ignore. pass elif (dport == "1900") and dIP in ("239.255.255.250", "ff02::c", "ff05::c", "ff08::c", "ff0e::c") and (Payload != None) and (Payload.startswith('M-SEARCH')): #ssdp discover ReportId("UC", sIP, "UDP_" + dport, "open", "ssdp-discovery/multicastclient") elif (dport == "1900") and (Payload != None) and (Payload.startswith('M-SEARCH')): #ssdp discover ReportId("UC", sIP, "UDP_" + dport, "open", "ssdp-discovery/client") elif (dport == "1900") and dIP in ("239.255.255.250", "ff02::c", "ff05::c", "ff08::c", "ff0e::c") and (Payload != None) and (Payload.startswith('NOTIFY')): #ssdp announcement additional_info = '' LocationMatch = SSDPLocationMatch.search(Payload) if (LocationMatch != None) and (len(LocationMatch.groups()) >= 1): additional_info = additional_info + ' SSDP Location: ' + str(LocationMatch.group(1)).strip() ServerMatch = SSDPServerMatch.search(Payload) if (ServerMatch != None) and (len(ServerMatch.groups()) >= 1): additional_info = additional_info + ' SSDP Server: ' + str(ServerMatch.group(1)).replace(',', ' ').strip() ReportId("UC", sIP, "UDP_" + dport, "open", "ssdp-announce/client" + additional_info) elif (dport == "1900") and dIP in ("239.255.255.250", "ff02::c", "ff05::c", "ff08::c", "ff0e::c"): #ssdp ShowPacket(p, "IPv6/UDPv6/1900-multicast SSDP unknown method", HonorQuit) ### IPv6/UDPv6=17/upnp-discovery=3702 elif (dport == "3702") and (dIP == "ff02::c"): #upnp discovery ReportId("UC", sIP, "UDP_" + dport, "open", "upnp-discovery/client") elif (dport == "3784") and (str(p[IPv6].hlim) == "255"): #bfd_control: https://tools.ietf.org/html/rfc5881 #FIXME - add check that sport must be between 49152 and 65535 ShowPacket(p, "IPv6/UDPv6/3784 bfd_control", HonorQuit) elif dport == "443": #udp https request ReportId("UC", sIP, "UDP_" + dport, "open", "UDP HTTPS") ### IPv6/UDPv6=17/mdns=5353 elif (sport == "5353") and (dport == "5353") and p.haslayer(DNS) and (isinstance(p[DNS], DNS)) and (p[DNS].qr == 1): #qr == 1 is a response process_udp_dns_response(sIP, dIP, SrcService, sport, p[DNS], p) elif (sport == "5353") and (dport == "5353") and not p.haslayer(DNS): #No dns layer for some reason UnhandledPacket(p) elif (sport == "5353") and (dport == "5353") and (dIP == "ff02::fb"): #mdns to the link-local mdns IP #FIXME - fix test if dIP == "ff02::fb": ReportId("UC", sIP, "UDP_" + dport, "open", "mdns/broadcastclient") else: ReportId("UC", sIP, "UDP_" + dport, "open", "mdns/client") elif (sport == "5353") and (dport == "5353"): # and (dIP == "ff02::fb") ShowPacket(p, "IPv6/UDPv6/5353-5353 mdns?" + str(p[IPv6].src) + ":" + str(sport) + " -> " + str(p[IPv6].dst) + ":" + str(dport), HonorQuit) ### IPv6/UDPv6=17/llmnr=5355 query elif (dport == "5355") and (dIP == "ff02::1:3") and (p[IPv6].hlim == 255) and p.haslayer(LLMNRQuery) and (p[LLMNRQuery].qr == 0): #llmnr (link-local multicast node resolution) pass ### IPv6/UDPv6=17/llmnr=5355 response elif (dport == "5355") and (dIP == "ff02::1:3") and (p[IPv6].hlim == 255): #llmnr (link-local multicast node resolution) ShowPacket(p, "IPv6/UDPv6/5355-ff02::1:3 llmnr not query", HonorQuit) #Can we pass this off to PUDR? elif dport == "5355": #unicast fe80->fe80 llmnr (link-local multicast node resolution) ShowPacket(p, "IPv6/UDPv6/5355 unicast llmnr not to 1:3", HonorQuit) ### IPv6/UDPv6/canon-bjnp2=8612 https://support.usa.canon.com/kb/index?page=content&id=ART109227 elif (dport == "8612") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('BJNP')): ReportId("UC", sIP, "UDP_" + dport, "open", "canon-bjnp2/broadcast") else: UnhandledPacket(p) ### IPV6/UDPv6/memcached=11211 https://blog.cloudflare.com/memcrashed-major-amplification-attacks-from-port-11211/ https://github.com/memcached/memcached/blob/master/doc/protocol.txt elif dport == "11211": if (Payload != None) and (Payload.find('gets ') > -1): ReportId("UC", sIP, "UDP_11211", "open", 'memcached: Likely spoofed and DDOSed source IP') else: ShowPacket(p, "IPV6/UDPv6/memcached=11211 request but non-gets", HonorQuit) elif sport == "11211": ReportId("US", sIP, "UDP_11211", "open", 'memcached server') ### IPv6/UDPv6/makerbotdiscovery=12307 https://github.com/gryphius/mini-makerbot-hacking/blob/master/doc/makerbotmini.md elif (sport == "12309") and (dport == "12307") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('{"command": "broadcast"')): ReportId("UC", sIP, "UDP_" + dport, "open", "makerbotdiscovery/broadcast") ### IPv6/UDPv6/googlemeet=19302-19309 elif (dport in meet_ports) and (dIP in meet_hosts): ReportId("UC", sIP, "UDP_" + dport, "open", "googlemeet/client") elif (sport in meet_ports) and (sIP in meet_hosts): ReportId("US", sIP, "UDP_" + sport, "open", "googlemeet/server") elif (dport in meet_ports): ReportId("UC", sIP, "UDP_" + dport, "open", "googlemeet/client missing dIP:" + dIP) elif (sport in meet_ports): ReportId("US", sIP, "UDP_" + sport, "open", "googlemeet/server missing sIP:" + sIP) ### IPv6/UDPv6=17/traceroute elif ((dport >= "33434") and (dport <= "33524")): #udptraceroute client ReportId("UC", sIP, "UDP_33434", "open", "udptraceroute/client") ### IPv6/UDPv6/brother-announce=54925 and 54926 used by brother printers http://ww2.chemistry.gatech.edu/software/Drivers/Brother/MFC-9840CDW/document/ug/usa/html/sug/index.html?page=chapter7.html elif (dport in ("54925", "54926")) and dIP in ("255.255.255.255", "ff02::1") and (dMAC in ("ff:ff:ff:ff:ff:ff", "33:33:00:00:00:01")) and (Payload != None) and (Payload.find('NODENAME=') > -1): BrotherMatch = BrotherAnnounceMatch.search(Payload) if (BrotherMatch != None) and (len(BrotherMatch.groups()) >= 4): #In the packets I've seen, groups 1, 2, and 3 are ip addresses (1 ipv4 and 2 ipv6). Group 4 is a nodename ("BRWF" + uppercase mac address, no colons) ReportId("UC", sIP, "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) ReportId("UC", BrotherMatch.group(1), "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) ReportId("UC", BrotherMatch.group(2), "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) ReportId("UC", BrotherMatch.group(3), "UDP_" + dport, "open", "brother-announce/broadcast nodename: " + BrotherMatch.group(4)) else: ReportId("UC", sIP, "UDP_" + dport, "open", "brother-announce/broadcast") ### IPv6/UDPv6/spotify-broadcast=57621 https://mrlithium.blogspot.com/2011/10/spotify-and-opting-out-of-spotify-peer.html elif (dport == "57621") and (dMAC == "ff:ff:ff:ff:ff:ff"): if (Payload != None) and (Payload.startswith('SpotUdp')): ReportId("UC", sIP, "UDP_" + dport, "open", "spotify-broadcast/client") else: UnhandledPacket(p) elif dport in SecUDPPortNames: UnhandledPacket(p) ReportId("UC", sIP, "UDP_" + dport, "open", str(SecUDPPortNames[dport]) + "/client") elif sport in SecUDPPortNames: UnhandledPacket(p) ReportId("US", sIP, "UDP_" + sport, "open", str(SecUDPPortNames[sport]) + "/server") else: ShowPacket(p, "IPv6/UDPv6/unhandled port: " + str(p[IPv6].src) + ":" + str(sport) + " -> " + str(p[IPv6].dst) + ":" + str(dport), HonorQuit) ### IPv6/Fragmentation=44 elif p[IPv6].nh == 44: #Fragment header. Not worth trying to extract info from following headers. #https://tools.ietf.org/html/rfc5798 UnhandledPacket(p) ### IPv6/ICMPv6=58 elif p[IPv6].nh == 58: #Layer names; see layers/inet6.py ( /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/scapy/layers/inet6.py ), hash named icmp6typescls ### IPv6/ICMPv6=58/DestUnreach=1 if p.getlayer(ICMPv6DestUnreach) and p.getlayer(IPerror6) and isinstance(p[IPerror6], IPerror6): #https://tools.ietf.org/html/rfc4443#section-3.1 OrigdIP = p[IPerror6].dst Code = p[ICMPv6DestUnreach].code ### IPv6/ICMPv6=58/DestUnreach=1/No route to dest=0 No route to destination; appears equivalent to IPv4 net unreachable if Code == 0: ReportId("IP", OrigdIP, "IP", "dead", 'net unreachable') ReportId("RO", sIP, "NetUn", "router", "") ### IPv6/ICMPv6=58/DestUnreach=1/AdminProhib=1 Communication with destination administratively prohibited (blocked by firewall) elif Code == 1: pass ### IPv6/ICMPv6=58/DestUnreach=1/BeyondScope=2 Beyond scope of source address https://tools.ietf.org/html/rfc4443 elif Code == 2: pass ### IPv6/ICMPv6=58/DestUnreach=1/AddressUnreach=3 Address unreachable (general, used when there is no more specific reason); appears equivalent to host unreachable elif Code == 3: ReportId("IP", OrigdIP, "IP", "dead", 'host unreachable') ReportId("RO", sIP, "HostUn", "router", "") ### IPv6/ICMPv6=58/DestUnreach=1/PortUnreach=4 Port unreachable and embedded protocol = 17, UDP, as it should be. Appears equivalent to port unreachable elif (Code == 4) and (p[IPerror6].nh == 17) and p.haslayer(UDPerror) and isinstance(p[UDPerror], UDPerror): DNSServerLoc = p[IPerror6].src + ",UDP_53" if (p[UDPerror].sport == 53) and (ManualServerDescription.has_key(DNSServerLoc)) and (ManualServerDescription[DNSServerLoc] == "dns/server"): #If orig packet coming from 53 and coming from a dns server, don't do anything (closed port on client is a common effect) #Don't waste time on port unreachables going back to a dns server; too common, and ephemeral anyways. pass else: #If orig packet coming from something other than 53, or coming from 53 and NOT coming from a dns server, log as closed OrigDPort = str(p[UDPerror].dport) OrigDstService = OrigdIP + ",UDP_" + OrigDPort ReportId("US", OrigdIP, "UDP_" + OrigDPort, "closed", "port unreachable") elif (Code == 4) and (p[IPerror6].nh == 6) and isinstance(p[TCPerror], TCPerror): #Port unreachable and embedded protocol = 6, TCP, which it shouldn't. Service = str(p[IPerror6].dst) + ",TCP_" + str(p[TCPerror].dport) if SynSentToTCPService.has_key(Service) and ((not LiveTCPService.has_key(Service)) or LiveTCPService[Service]): LiveTCPService[Service] = False ReportId("TS", str(p[IPerror6].dst), "TCP_" + str(p[TCPerror].dport), "closed", '') elif (Code == 4) and (p[IPerror6].nh == 58): #Port unreachable and embedded protocol = 58, ICMP. Seen in response to pings pass ### IPv6/ICMPv6=58/DestUnreach=1/FailedPolicy=5 Source address failed ingress/egress policy (subset of code 1) https://tools.ietf.org/html/rfc4443 elif Code == 5: pass ### IPv6/ICMPv6=58/DestUnreach=1/RejectRoute=6 Reject route to destination (subset of code 1) https://tools.ietf.org/html/rfc4443 elif Code == 6: pass ### IPv6/ICMPv6=58/DestUnreach=1/HeaderError=7 Error in source routing header https://tools.ietf.org/html/rfc6550 https://tools.ietf.org/html/rfc6554 elif Code == 7: pass ### IPv6/ICMPv6=58/DestUnreach=1/Unknown else: ShowPacket(p, "IPV6/ICMPv6/Dest Unreach=1/Unknown code", HonorQuit) ### IPv6/ICMPv6=58/PacktTooBig=2 elif p.getlayer(ICMPv6PacketTooBig): ReportId("RO", sIP, "TooBig", "router", "") ### IPv6/ICMPv6=58/TimeExceeded=3 elif p.getlayer(ICMPv6TimeExceeded): Code = p[ICMPv6TimeExceeded].code if Code == 0: #hop limit exceeded in transit ReportId("RO", sIP, "TTLEx", "router", "") else: ShowPacket(p, "IPv6/ICMPv6/ICMPv6TimeExceeded = type 3/Code = " + str(Code), HonorQuit) ### IPv6/ICMPv6=58/EchoRequest=128 elif p.getlayer(ICMPv6EchoRequest): pass ### IPv6/ICMPv6=58/EchoReply=129 elif p.getlayer(ICMPv6EchoReply): ReportId("IP", sIP, "IP", "live", 'icmp echo reply') ### IPv6/ICMPv6=58/ND_RouterSolicitation=133 elif p.getlayer(ICMPv6ND_RS) and (dIP == "ff02::2"): pass ### IPv6/ICMPv6=58/ND_RouterAdvertisement=134 elif p.getlayer(ICMPv6ND_RA) and (dIP == "ff02::1"): AdditionalInfo = 'hop_limit=' + str(p[ICMPv6ND_RA].chlim) if p.getlayer(ICMPv6NDOptPrefixInfo): AdditionalInfo = AdditionalInfo + ' net=' + str(p[ICMPv6NDOptPrefixInfo].prefix) + '/' + str(p[ICMPv6NDOptPrefixInfo].prefixlen) if p.getlayer(ICMPv6NDOptRDNSS): for one_dns in p[ICMPv6NDOptRDNSS].dns: AdditionalInfo = AdditionalInfo + ' dns=' + str(one_dns) ReportId("RO", sIP, "RouterAdv", "router", AdditionalInfo) if p.getlayer(ICMPv6NDOptSrcLLAddr): router_mac_addr = str(p[ICMPv6NDOptSrcLLAddr].lladdr) ReportId("MA", sIP, 'Ethernet', router_mac_addr, '') ### IPv6/ICMPv6=58/ND_NeighborSolicitation=135 https://tools.ietf.org/html/rfc4861 elif p.getlayer(ICMPv6ND_NS) and p[IPv6].hlim == 255 and p[ICMPv6ND_NS].code == 0: host_mac_addr = '' if p.getlayer(ICMPv6NDOptSrcLLAddr): host_mac_addr = str(p[ICMPv6NDOptSrcLLAddr].lladdr) elif p.getlayer(Ether): host_mac_addr = sMAC #else: # pass #No source for ethernet mac addr, ignore if host_mac_addr: ReportId("MA", sIP, 'Ethernet', host_mac_addr, '') ### IPv6/ICMPv6=58/ND_NeighborAdvertisement=136 https://tools.ietf.org/html/rfc4861 elif p.getlayer(ICMPv6ND_NA) and p.getlayer(Ether) and p[IPv6].hlim == 255 and p[ICMPv6ND_NA].code == 0: if p[ICMPv6ND_NA].R == 1: ReportId("RO", sIP, "NeighborAdvRouterFlag", "router", '') host_mac_addr = sMAC ReportId("MA", sIP, 'Ethernet', host_mac_addr, '') ### IPv6/ICMPv6=58/ND_Redirect=137 http://www.tcpipguide.com/free/t_ICMPv6RedirectMessages-2.htm elif p.getlayer(ICMPv6ND_Redirect) and p.getlayer(Ether) and p[IPv6].hlim == 255 and p[ICMPv6ND_Redirect].code == 0: ReportId("RO", sIP, "ND_Redirect_source", "router", '') #the original complaining router ReportId("RO", p[ICMPv6ND_Redirect].tgt, "ND_Redirect_target", "router", '') #the better router to use if p.getlayer(ICMPv6NDOptDstLLAddr): ReportId("MA", p[ICMPv6ND_Redirect].tgt, 'Ethernet', p[ICMPv6NDOptDstLLAddr].lladdr, '') #packet probably includes the mac address of the better router too. else: ShowPacket(p, "IPv6/ICMPv6/unhandled type", HonorQuit) ### IPv6/SATNET-EXPAK=64 elif p[IPv6].nh == 64: UnhandledPacket(p) ### IPv6/EIGRPv4 EIGRP = Enhanced Interior Gateway Routing Protocol elif (p[IPv6].nh == 88) and dIP in ("224.0.0.10", "FF02:0:0:0:0:0:0:A"): #224.0.0.10 for IPv4 EIGRP Routers, FF02:0:0:0:0:0:0:A for IPv6 EIGRP Routers ReportId("RO", sIP, "EIGRP", "router", "") elif p[IPv6].nh == 88: #Different target address format, perhaps? ShowPacket(p, "IPv6/EIGRP unknown target IP", HonorQuit) ### IPv6/OSPF=89 elif (p[IPv6].nh == 89) and (dIP == "ff02::5"): #OSPF #https://tools.ietf.org/html/rfc5340 UnhandledPacket(p) ### IPv6/VRRP=112 elif (p[IPv6].nh == 112) and (dIP == "ff02::12"): #VRRPv6 VRRP = virtual router redundancy protocol #https://tools.ietf.org/html/rfc5798 UnhandledPacket(p) ### IPv6/other else: ShowPacket(p, "IPV6 unknown protocol; Next header:" + str(p[IPv6].nh), HonorQuit) ### No ethernet layer elif not p.haslayer(Ether): if p.haslayer(Raw): #Sample payload from Mac lo0 packet: \x02\x00\x00\x00E\x00\x00 Hack; strip off first 4 bytes and interpret the rest as IP. encap_packet_raw = None #FIXME - move all decodes to the start and save as strings for use in the program if p[Raw].load[0:6] in ('020000004500'.decode('hex'), '020000004502'.decode('hex'), '020000004510'.decode('hex')): encap_packet_raw = p[Raw].load[4:] #elif p[Raw].load[8:10] == '4500'.decode('hex'): # encap_packet_raw = p[Raw].load[8:] if encap_packet_raw: encap_packet = IP(encap_packet_raw) processpacket(encap_packet) else: ShowPacket(p, "Non-ethernet raw does not appear to have E\x00\x00", HonorQuit) else: UnhandledPacket(p) #ShowPacket(p, "packet has no ethernet layer", HonorQuit) #NOTE: Old format of p[Ethernet] appears to be changed to p[Ether] in scapy 2.3.3 (~20161026) elif p[Ether].type == 0x6002: #24578: MOP Remote Console UnhandledPacket(p) elif p[Ether].type == 0x8035: #32821: Reverse ARP https://en.wikipedia.org/wiki/Reverse_Address_Resolution_Protocol UnhandledPacket(p) elif p[Ether].type == 0x8100: #33024 = IEEE 802.1Q VLAN-tagged frames (initially Wellfleet) UnhandledPacket(p) elif p[Ether].type == 0x872D: #34605 ? UnhandledPacket(p) elif p[Ether].type == 0x8809: #34825 LACP (builds multiple links into a trunk) UnhandledPacket(p) elif p[Ether].type == 0x888E: #34958 EAPOL, EAP over LAN (IEEE 802.1X) UnhandledPacket(p) elif p[Ether].type == 0x8899: #34969 Unknown UnhandledPacket(p) elif p[Ether].type == 0x88A2: #34978 ATA over ethernet UnhandledPacket(p) elif p[Ether].type == 0x88A7: #34983 Unknown UnhandledPacket(p) elif p[Ether].type == 0x88CC: #35020 LLDP Link Layer Discovery Protocol UnhandledPacket(p) elif p[Ether].type == 0x8912: #35090 unknown UnhandledPacket(p) elif p[Ether].type == 0x9000: #36864 = Ethernet loopback protocol. http://wiki.wireshark.org/Loop UnhandledPacket(p) else: ShowPacket(p, "Unregistered ethernet type:" + str(p[Ether].type), HonorQuit) #For a good reference on new ethernet types, see: #http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml #http://www.iana.org/assignments/ethernet-numbers #http://en.wikipedia.org/wiki/EtherType def Usage(): if len(sys.argv) > 0: Debug(str(sys.argv[0]) + " command line options:") else: Debug("passer.py command line options:") Debug("[-h]\t\t\tShow this help screen.") Debug("[-i interfacename]\t\tread packets from a specific interface instead of all network interfaces.") Debug("[-r packetfile.pcap]\t\tread packets from a pcap file instead of from network interfaces.") Debug("[-l logfilename]\t\tsave all output in text format to this file as well.") Debug("[-u unhandledfilename]\t\tsave all unhandled packets to this pcap file.") Debug("['bpf filter']\t\tUse this bpf filter to limit packets seen (last parameter).") quit() #======== Start of main code. ======== Debug("Passer version " + str(passerVersion)) #Debug(str(len(sys.argv) - 1) + " params") ParamPointer = 1 LogFilename = '' UnhandledFilename = '' PcapFilename = '' InterfaceName = '' bpfilter = '' while ParamPointer < len(sys.argv): #Debug("Processing: " + sys.argv[ParamPointer]) if sys.argv[ParamPointer] == "-h": Usage() elif sys.argv[ParamPointer] == "-i": if ParamPointer + 1 >= len(sys.argv): Debug("'-i' command line option requested, but no interface following it, exiting.") Usage() elif PcapFilename != '': print("Both '-i' and '-r' requested, exiting.") Usage() else: InterfaceName = sys.argv[ParamPointer + 1] ParamPointer += 1 #Debug("Interface name is " + str(InterfaceName)) elif sys.argv[ParamPointer] == "-l": if ParamPointer + 1 >= len(sys.argv): Debug("'-l' command line option requested, but no filename following it, exiting.") Usage() else: LogFilename = sys.argv[ParamPointer + 1] ParamPointer += 1 #Debug("Log file is " + str(LogFilename)) elif sys.argv[ParamPointer] == "-u": if ParamPointer + 1 >= len(sys.argv): Debug("'-u' command line option requested, but no filename following it, exiting.") Usage() else: UnhandledFilename = sys.argv[ParamPointer + 1] ParamPointer += 1 Debug("Unhandled file is " + str(UnhandledFilename)) elif sys.argv[ParamPointer] == "-r": if ParamPointer + 1 >= len(sys.argv): Debug("'-r' command line option requested, but no filename following it, exiting.") Usage() elif InterfaceName != '': Debug("Both '-i' and '-r' requested, exiting.") Usage() else: PcapFilename = sys.argv[ParamPointer + 1] ParamPointer += 1 #Debug("Pcap file is " + PcapFilename) elif ParamPointer + 1 == len(sys.argv): #If there's a sole parameter left, that should be the bpfilter. bpfilter = sys.argv[ParamPointer] else: Debug("Too many parameters, exiting.") Usage() ParamPointer += 1 #FIXME - drop isfile check; could be named pipe or, i suppose, a device. Perhaps test writable? if LogFilename != '': #and os.path.isfile(LogFilename): try: LogFile = open(LogFilename, 'a') except: Debug("Unable to append to " + LogFilename + ", no logging will be done.") LogFile = None else: LogFile = None if UnhandledFilename != '': try: UnhandledFile = PcapWriter(filename=UnhandledFilename) except: Debug("Unable to open " + UnhandledFilename + ", no unhandled packets will be saved.") UnhandledFile = None else: UnhandledFile = None Debug("BPFilter is " + bpfilter) #Hmmm, bpfilter appears not to work. It loads correctly into the variable, but the sniff command appears to ignore it. #Temporarily disabled p0f #if not os.path.isfile("/etc/p0f/p0f.fp"): # Debug("/etc/p0f/p0f.fp not found; please install p0f version 2 to enable OS fingerprinting.") for oneMacFile in ('/usr/share/ettercap/etter.finger.mac', '/opt/local/share/ettercap/etter.finger.mac', '/usr/share/nmap/nmap-mac-prefixes', '/opt/local/share/nmap/nmap-mac-prefixes', '/usr/share/wireshark/manuf', '/opt/local/share/wireshark/manuf', '/usr/share/ethereal/manuf', '/usr/share/arp-scan/ieee-oui.txt', '/opt/local/share/arp-scan/ieee-oui.txt'): if os.path.isfile(oneMacFile): LoadMacData(oneMacFile) if len(EtherManuf) == 0: Debug("None of the default mac address listings found. Please install ettercap, nmap, wireshark, and/or arp-scan.") else: Debug(str(len(EtherManuf)) + " mac prefixes loaded.") for oneFPFile in ('/usr/local/share/nmap/nmap-service-probes', '/usr/share/nmap/nmap-service-probes', '/opt/local/share/nmap/nmap-service-probes'): if os.path.isfile(oneFPFile): LoadNmapServiceFP(oneFPFile) if len(ServiceFPs) == 0: Debug("Can't locate /{usr,opt}/{local/,}share/nmap/nmap-service-probes. Please install nmap to support more server descriptions.") else: Debug("Fingerprints for " + str(len(ServiceFPs)) + " ports loaded.") #FIXME - change to dictionary of compiled regexes? SipPhoneMatch = re.compile('Contact: ([0-9-]+) ') SSDPLocationMatch = re.compile('LOCATION:([a-zA-Z0-9:,/_\. -]+)\r') SSDPServerMatch = re.compile('[Ss][Ee][Rr][Vv][Ee][Rr]:([a-zA-Z0-9:,/_\. -]+)\r') BrotherAnnounceMatch = re.compile('IP=([0-9][0-9\.]*):5492[56];IPv6=\[([0-9a-fA-F:][0-9a-fA-F:]*)\]:5492[56],\[([0-9a-fA-F:][0-9a-fA-F:]*)\]:5492[56];NODENAME="([0-9a-zA-Z][0-9a-zA-Z]*)"') SIPFromMatch = re.compile('From:[^<]*]') #https://en.wikipedia.org/wiki/SIP_URI_scheme SIPToMatch = re.compile('To:[^<]*]') SyslogMatch = re.compile('^<[0-9][0-9]*>[A-Z][a-z][a-z] [ 0-9][0-9] [0-2][0-9]:[0-9][0-9]:[0-9][0-9] ([^ ][^ ]*) ([^: [][^: []*)[: []') #Match 1 is short hostname, match 2 is process name that generated the message #To set scapy options: #conf.verb = 0 #conf.iface = 'eth1' #Default: every interface #conf.nmap_base = '/usr/share/nmap/nmap-os-fingerprints' #conf.p0f_base = '/etc/p0f.fp' #conf.promisc = 1 try: conf.sniff_promisc = 1 except: config.sniff_promisc = 1 #Neither this nor adding "filter=bpfilter" to each sniff line seems to actually apply the bpf. Hmmm. try: conf.filter = bpfilter except: config.filter = bpfilter if PcapFilename != '': sniff(store=0, offline=PcapFilename, filter=bpfilter, prn=lambda x: processpacket(x)) elif InterfaceName != '': sniff(store=0, iface=InterfaceName, filter=bpfilter, prn=lambda x: processpacket(x)) else: sniff(store=0, filter=bpfilter, prn=lambda x: processpacket(x)) #To limit to the first 500 packets, add ", count=500" at the end of the "sniff" command inside the last paren