Ticket #76: patch
| File patch, 14.1 kB (added by pierre@droids-corp.org, 10 months ago) |
|---|
-
scapy.py
old new 10664 10664 # OS - OS genre 10665 10665 # details - OS description 10666 10666 10667 10668 10669 10667 class p0fKnowledgeBase(KnowledgeBase): 10670 10668 def __init__(self, filename): 10671 10669 KnowledgeBase.__init__(self, filename) … … 10684 10682 l = tuple(l.split(":")) 10685 10683 if len(l) < 8: 10686 10684 continue 10687 li = map(int,l[1:4]) 10685 def a2i(x): 10686 if x.isdigit(): 10687 return int(x) 10688 return x 10689 li = map(a2i, l[1:4]) 10688 10690 #if li[0] not in self.ttl_range: 10689 10691 # self.ttl_range.append(li[0]) 10690 10692 # self.ttl_range.sort() … … 10694 10696 self.base = None 10695 10697 f.close() 10696 10698 10699 def p0f_selectdb(flags): 10700 # tested flags: S, R, A 10701 if flags & 0x16 == 0x2: 10702 # SYN 10703 return p0f_kdb 10704 elif flags & 0x16 == 0x12: 10705 # SYN/ACK 10706 return p0fa_kdb 10707 elif flags & 0x16 in [ 0x4, 0x14 ]: 10708 # RST RST/ACK 10709 return p0fr_kdb 10710 elif flags & 0x16 == 0x10: 10711 # ACK 10712 return p0fo_kdb 10713 else: 10714 return None 10697 10715 10698 10716 def packet2p0f(pkt): 10717 pkt = pkt.copy() 10718 pkt = pkt.__class__(str(pkt)) 10699 10719 while pkt.haslayer(IP) and pkt.haslayer(TCP): 10700 10720 pkt = pkt.getlayer(IP) 10701 10721 if isinstance(pkt.payload, TCP): 10702 10722 break 10703 10723 pkt = pkt.payload 10704 10724 10705 10725 if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): 10706 10726 raise TypeError("Not a TCP/IP packet") 10707 if pkt.payload.flags & 0x13 != 0x02: #S,!A,!F 10708 raise TypeError("Not a syn packet") 10727 #if pkt.payload.flags & 0x7 != 0x02: #S,!F,!R 10728 # raise TypeError("Not a SYN or SYN/ACK packet") 10729 10730 db = p0f_selectdb(pkt.payload.flags) 10709 10731 10710 10732 #t = p0f_kdb.ttl_range[:] 10711 10733 #t += [pkt.ttl] 10712 10734 #t.sort() 10713 10735 #ttl=t[t.index(pkt.ttl)+1] 10714 10736 ttl = pkt.ttl 10715 10737 10716 10738 df = (pkt.flags & 2) / 2 10717 10739 ss = len(pkt) 10718 10740 # from p0f/config.h : PACKET_BIG = 100 10719 10741 if ss > 100: 10720 ss = 0 10721 10742 if db == p0fr_kdb: 10743 # p0fr.fp: "Packet size may be wildcarded. The meaning of 10744 # wildcard is, however, hardcoded as 'size > 10745 # PACKET_BIG'" 10746 ss = '*' 10747 else: 10748 ss = 0 10749 if db == p0fo_kdb: 10750 # p0fo.fp: "Packet size MUST be wildcarded." 10751 ss = '*' 10752 10722 10753 ooo = "" 10723 10754 mss = -1 10724 10755 qqT = False 10725 10756 qqP = False 10726 10757 #qqBroken = False 10727 ilen = (pkt [TCP].dataofs << 2) - 20 # from p0f.c10758 ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c 10728 10759 for option in pkt.payload.options: 10729 10760 ilen -= 1 10730 10761 if option[0] == "MSS": … … 10758 10789 # FIXME: ilen 10759 10790 ooo = ooo[:-1] 10760 10791 if ooo == "": ooo = "." 10761 10792 10762 10793 win = pkt.payload.window 10763 10794 if mss != -1: 10764 if win % mss == 0:10795 if mss != 0 and win % mss == 0: 10765 10796 win = "S" + str(win/mss) 10766 10797 elif win % (mss + 40) == 0: 10767 10798 win = "T" + str(win/(mss+40)) 10768 win = str(win)10769 10799 win = str(win) 10800 10770 10801 qq = "" 10771 10802 10803 if db == p0fr_kdb: 10804 if pkt.payload.flags & 0x10 == 0x10: 10805 # p0fr.fp: "A new quirk, 'K', is introduced to denote 10806 # RST+ACK packets" 10807 qq += "K" 10808 if pkt.payload.seq == pkt.payload.ack: 10809 # p0fr.fp: "A new quirk, 'Q', is used to denote SEQ number 10810 # equal to ACK number." 10811 qq += "Q" 10812 if pkt.payload.seq == 0: 10813 # p0fr.fp: "A new quirk, '0', is used to denote packets 10814 # with SEQ number set to 0." 10815 qq += "0" 10772 10816 if qqP: 10773 10817 qq += "P" 10774 if pkt [IP].id == 0:10818 if pkt.id == 0: 10775 10819 qq += "Z" 10776 if pkt [IP].options != '':10820 if pkt.options != '': 10777 10821 qq += "I" 10778 if pkt [TCP].urgptr != 0:10822 if pkt.payload.urgptr != 0: 10779 10823 qq += "U" 10780 if pkt [TCP].reserved != 0:10824 if pkt.payload.reserved != 0: 10781 10825 qq += "X" 10782 if pkt [TCP].ack != 0:10826 if pkt.payload.ack != 0: 10783 10827 qq += "A" 10784 10828 if qqT: 10785 10829 qq += "T" 10786 if pkt[TCP].flags & 40 != 0: 10787 # U or P 10788 qq += "F" 10789 if not isinstance(pkt[TCP].payload, NoPayload): 10830 if db == p0fo_kdb: 10831 if pkt.payload.flags & 0x20 != 0: 10832 # U 10833 # p0fo.fp: "PUSH flag is excluded from 'F' quirk checks" 10834 qq += "F" 10835 else: 10836 if pkt.payload.flags & 0x28 != 0: 10837 # U or P 10838 qq += "F" 10839 if db != p0fo_kdb and not isinstance(pkt.payload.payload, NoPayload): 10840 # p0fo.fp: "'D' quirk is not checked for." 10790 10841 qq += "D" 10791 # FIXME : "!" - broken options segment 10842 # FIXME : "!" - broken options segment: not handled yet 10792 10843 10793 10844 if qq == "": 10794 10845 qq = "." 10795 10846 10796 return (win, 10797 ttl, 10798 df, 10799 ss, 10800 ooo, 10801 qq) 10847 return (db, (win, ttl, df, ss, ooo, qq)) 10802 10848 10803 10849 def p0f_correl(x,y): 10804 10850 d = 0 10805 # wwww can be "*" or "%nn" 10851 # wwww can be "*" or "%nn". "Tnn" and "Snn" should work fine with 10852 # the x[0] == y[0] test. 10806 10853 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)) 10807 10854 # ttl 10808 10855 d += (y[1] >= x[1] and y[1] - x[1] < 32) 10809 10856 for i in [2, 3, 5]: 10810 d += (x[i] == y[i] )10857 d += (x[i] == y[i] or y[i] == '*') 10811 10858 xopt = x[4].split(",") 10812 10859 yopt = y[4].split(",") 10813 10860 if len(xopt) == len(yopt): … … 10827 10874 10828 10875 10829 10876 def p0f(pkt): 10830 """Passive OS fingerprinting: which OS emitted this TCP SYN?10877 """Passive OS fingerprinting: which OS emitted this TCP packet ? 10831 10878 p0f(packet) -> accuracy, [list of guesses] 10832 10879 """ 10833 pb = p0f_kdb.get_base() 10880 db, sig = packet2p0f(pkt) 10881 if db: 10882 pb = db.get_base() 10883 else: 10884 pb = [] 10834 10885 if not pb: 10835 10886 warning("p0f base empty.") 10836 10887 return [] 10837 s = len(pb[0][0])10888 #s = len(pb[0][0]) 10838 10889 r = [] 10839 sig = packet2p0f(pkt)10840 10890 max = len(sig[4].split(",")) + 5 10841 10891 for b in pb: 10842 10892 d = p0f_correl(sig,b) 10843 10893 if d == max: 10844 10894 r.append((b[6], b[7], b[1] - pkt[IP].ttl)) 10845 10895 return r 10846 10847 10896 10848 10897 def prnp0f(pkt): 10898 # we should print which DB we use 10849 10899 try: 10850 10900 r = p0f(pkt) 10851 10901 except: 10852 10902 return 10853 10903 if r == []: 10854 r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt) )) + ":?:?]", None)10904 r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt)[1])) + ":?:?]", None) 10855 10905 else: 10856 10906 r = r[0] 10857 10907 uptime = None … … 10863 10913 uptime = None 10864 10914 res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) 10865 10915 if uptime is not None: 10866 res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport% ")10916 res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") 10867 10917 else: 10868 res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport% ")10918 res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") 10869 10919 if r[2] is not None: 10870 10920 res += " (distance " + str(r[2]) + ")" 10871 10921 print res … … 10889 10939 raise TypeError("No timestamp option") 10890 10940 10891 10941 10942 def p0f_impersonate(pkt, osgenre, osdetails=None, extrahops=0, mtu=1500, uptime=None): 10943 """Modifies pkt so that p0f will think it has been sent by a 10944 specific OS. If osdetails is None, then we randomly pick up a 10945 personality matching osgenre. 10946 10947 For now, only TCP Syn packets are supported. 10948 Some specifications of the p0f.fp file are not (yet) implemented. 10949 """ 10950 pkt = pkt.copy() 10951 #pkt = pkt.__class__(str(pkt)) 10952 while pkt.haslayer(IP) and pkt.haslayer(TCP): 10953 pkt = pkt.getlayer(IP) 10954 if isinstance(pkt.payload, TCP): 10955 break 10956 pkt = pkt.payload 10957 10958 if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): 10959 raise TypeError("Not a TCP/IP packet") 10960 10961 if uptime is None: 10962 uptime = random.randint(120,100*60*60*24*365) 10963 10964 db = p0f_selectdb(pkt.payload.flags) 10965 pb = db.get_base() 10966 if pb is None: 10967 pb = [] 10968 pb = filter(lambda x: x[6] == osgenre, pb) 10969 if osdetails: 10970 pb = filter(lambda x: x[7] == osdetails, pb) 10971 if db == p0fr_kdb: 10972 # 'K' quirk <=> RST+ACK 10973 if pkt.payload.flags & 0x4 == 0x4: 10974 pb = filter(lambda x: 'K' in x[5], pb) 10975 else: 10976 pb = filter(lambda x: 'K' not in x[5], pb) 10977 if not pb: 10978 raise Scapy_Exception("No match in the p0f database") 10979 pers = pb[random.randint(0, len(pb) - 1)] 10980 10981 # options (we start with options because of MSS) 10982 ## TODO: let the options already set if they are valid 10983 options = [] 10984 if pers[4] != '.': 10985 for opt in pers[4].split(','): 10986 if opt[0] == 'M': 10987 # MSS might have a maximum size because of window size 10988 # specification 10989 if pers[0][0] == 'S': 10990 maxmss = (2L**16-1) / int(pers[0][1:]) 10991 else: 10992 maxmss = (2L**16-1) 10993 # If we have to randomly pick up a value, we cannot use 10994 # scapy RandXXX() functions, because the value has to be 10995 # set in case we need it for the window size value. That's 10996 # why we use random.randint() 10997 if opt[1:] == '*': 10998 options.append(('MSS', random.randint(1,maxmss))) 10999 elif opt[1] == '%': 11000 coef = int(opt[2:]) 11001 options.append(('MSS', coef*random.randint(1,maxmss/coef))) 11002 else: 11003 options.append(('MSS', int(opt[1:]))) 11004 elif opt[0] == 'W': 11005 if opt[1:] == '*': 11006 options.append(('WScale', RandByte())) 11007 elif opt[1] == '%': 11008 coef = int(opt[2:]) 11009 options.append(('WScale', coef*RandNum(min=1, 11010 max=(2L**8-1)/coef))) 11011 else: 11012 options.append(('WScale', int(opt[1:]))) 11013 elif opt == 'T0': 11014 options.append(('Timestamp', (0, 0))) 11015 elif opt == 'T': 11016 if 'T' in pers[5]: 11017 # FIXME: RandInt() here does not work (bug (?) in 11018 # TCPOptionsField.m2i often raises "OverflowError: 11019 # long int too large to convert to int" in: 11020 # oval = struct.pack(ofmt, *oval)" 11021 # Actually, this is enough to often raise the error: 11022 # struct.pack('I', RandInt()) 11023 options.append(('Timestamp', (uptime, random.randint(1,2**32-1)))) 11024 else: 11025 options.append(('Timestamp', (uptime, 0))) 11026 elif opt == 'S': 11027 options.append(('SAckOK', '')) 11028 elif opt == 'N': 11029 options.append(('NOP', None)) 11030 elif opt == 'E': 11031 options.append(('EOL', None)) 11032 ## FIXME: qqP not handled 11033 else: 11034 warning("unhandled TCP option " + opt) 11035 pkt[TCP].options = options 11036 11037 # window size 11038 if pers[0] == '*': 11039 pkt[TCP].window = RandShort() 11040 elif pers[0].isdigit(): 11041 pkt[TCP].window = int(pers[0]) 11042 elif pers[0][0] == '%': 11043 coef = int(pers[0][1:]) 11044 pkt[TCP].window = coef * RandNum(min=1,max=(2L**16-1)/coef) 11045 elif pers[0][0] == 'T': 11046 pkt[TCP].window = mtu * int(pers[0][1:]) 11047 elif pers[0][0] == 'S': 11048 ## needs MSS set 11049 MSS = filter(lambda x: x[0] == 'MSS', options) 11050 if not filter(lambda x: x[0] == 'MSS', options): 11051 raise Scapy_Exception("TCP window value requires MSS, and MSS option not set") 11052 pkt[TCP].window = filter(lambda x: x[0] == 'MSS', options)[0][1] * int(pers[0][1:]) 11053 else: 11054 raise Scapy_Exception('Unhandled window size specification') 11055 11056 # ttl 11057 pkt[IP].ttl = pers[1]+extrahops 11058 # DF flag 11059 pkt[IP].flags |= (2 * pers[2]) 11060 ## FIXME: ss (packet size) not handled (how ? may be with D quirk 11061 ## if present) 11062 # Quirks 11063 if pers[5] != '.': 11064 for qq in pers[5]: 11065 ## FIXME: not handled: P, I, X, ! 11066 # T handled with the Timestamp option 11067 if qq == 'Z': pkt.id = 0 11068 elif qq == 'U': pkt.payload.urgptr = RandShort() 11069 elif qq == 'A': pkt.payload.ack = RandInt() 11070 elif qq == 'F': 11071 if db == p0fo_kdb: 11072 pkt.payload.flags |= 0x20 # U 11073 else: 11074 pkt.payload.flags |= RandChoice(8, 32, 40) #P / U / PU 11075 elif qq == 'D' and db != p0fo_kdb: 11076 pkt /= Raw(load=RandString(random.randint(1, 10))) # XXX p0fo.fp 11077 elif qq == 'Q': pkt.payload.seq = pkt.payload.ack 11078 #elif qq == '0': pkt.payload.seq = 0 11079 if db == p0fr_kdb: 11080 if '0' in pers[5]: 11081 pkt.payload.seq = 0 11082 elif pkt.payload.seq == 0: 11083 pkt.payload.seq = RandInt() 11084 11085 while pkt.underlayer: 11086 pkt = pkt.underlayer 11087 return pkt 10892 11088 10893 11089 ################# 10894 11090 ## Queso stuff ## … … 13202 13403 histfile = os.path.join(os.environ["HOME"], ".scapy_history") 13203 13404 padding = 1 13204 13405 p0f_base ="/etc/p0f/p0f.fp" 13406 p0fa_base ="/etc/p0f/p0fa.fp" 13407 p0fr_base ="/etc/p0f/p0fr.fp" 13408 p0fo_base ="/etc/p0f/p0fo.fp" 13205 13409 queso_base ="/etc/queso.conf" 13206 13410 nmap_base ="/usr/share/nmap/nmap-os-fingerprints" 13207 13411 IPCountry_base = "GeoIPCountry4Scapy.gz" … … 13244 13448 13245 13449 13246 13450 p0f_kdb = p0fKnowledgeBase(conf.p0f_base) 13451 p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base) 13452 p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base) 13453 p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base) 13247 13454 queso_kdb = QuesoKnowledgeBase(conf.queso_base) 13248 13455 nmap_kdb = NmapKnowledgeBase(conf.nmap_base) 13249 13456 IP_country_kdb = IPCountryKnowledgeBase(conf.IPCountry_base)