Ticket #76: patch

File patch, 14.1 kB (added by pierre@droids-corp.org, 10 months ago)
  • scapy.py

    old new  
    1066410664# OS      - OS genre 
    1066510665# details - OS description 
    1066610666 
    10667  
    10668  
    1066910667class p0fKnowledgeBase(KnowledgeBase): 
    1067010668    def __init__(self, filename): 
    1067110669        KnowledgeBase.__init__(self, filename) 
     
    1068410682                l = tuple(l.split(":")) 
    1068510683                if len(l) < 8: 
    1068610684                    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]) 
    1068810690                #if li[0] not in self.ttl_range: 
    1068910691                #    self.ttl_range.append(li[0]) 
    1069010692                #    self.ttl_range.sort() 
     
    1069410696            self.base = None 
    1069510697        f.close() 
    1069610698 
     10699def 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 
    1069710715 
    1069810716def packet2p0f(pkt): 
     10717    pkt = pkt.copy() 
     10718    pkt = pkt.__class__(str(pkt)) 
    1069910719    while pkt.haslayer(IP) and pkt.haslayer(TCP): 
    1070010720        pkt = pkt.getlayer(IP) 
    1070110721        if isinstance(pkt.payload, TCP): 
    1070210722            break 
    1070310723        pkt = pkt.payload 
    10704  
     10724     
    1070510725    if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): 
    1070610726        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) 
    1070910731     
    1071010732    #t = p0f_kdb.ttl_range[:] 
    1071110733    #t += [pkt.ttl] 
    1071210734    #t.sort() 
    1071310735    #ttl=t[t.index(pkt.ttl)+1] 
    1071410736    ttl = pkt.ttl 
    10715  
     10737     
    1071610738    df = (pkt.flags & 2) / 2 
    1071710739    ss = len(pkt) 
    1071810740    # from p0f/config.h : PACKET_BIG = 100 
    1071910741    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     
    1072210753    ooo = "" 
    1072310754    mss = -1 
    1072410755    qqT = False 
    1072510756    qqP = False 
    1072610757    #qqBroken = False 
    10727     ilen = (pkt[TCP].dataofs << 2) - 20 # from p0f.c 
     10758    ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c 
    1072810759    for option in pkt.payload.options: 
    1072910760        ilen -= 1 
    1073010761        if option[0] == "MSS": 
     
    1075810789            # FIXME: ilen 
    1075910790    ooo = ooo[:-1] 
    1076010791    if ooo == "": ooo = "." 
    10761  
     10792     
    1076210793    win = pkt.payload.window 
    1076310794    if mss != -1: 
    10764         if win % mss == 0: 
     10795        if mss != 0 and win % mss == 0: 
    1076510796            win = "S" + str(win/mss) 
    1076610797        elif win % (mss + 40) == 0: 
    1076710798            win = "T" + str(win/(mss+40)) 
    10768         win = str(win) 
    10769  
     10799    win = str(win) 
     10800     
    1077010801    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" 
    1077210816    if qqP: 
    1077310817        qq += "P" 
    10774     if pkt[IP].id == 0: 
     10818    if pkt.id == 0: 
    1077510819        qq += "Z" 
    10776     if pkt[IP].options != '': 
     10820    if pkt.options != '': 
    1077710821        qq += "I" 
    10778     if pkt[TCP].urgptr != 0: 
     10822    if pkt.payload.urgptr != 0: 
    1077910823        qq += "U" 
    10780     if pkt[TCP].reserved != 0: 
     10824    if pkt.payload.reserved != 0: 
    1078110825        qq += "X" 
    10782     if pkt[TCP].ack != 0: 
     10826    if pkt.payload.ack != 0: 
    1078310827        qq += "A" 
    1078410828    if qqT: 
    1078510829        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." 
    1079010841        qq += "D" 
    10791     # FIXME : "!" - broken options segment 
     10842    # FIXME : "!" - broken options segment: not handled yet 
    1079210843 
    1079310844    if qq == "": 
    1079410845        qq = "." 
    1079510846 
    10796     return (win, 
    10797             ttl, 
    10798             df, 
    10799             ss, 
    10800             ooo, 
    10801             qq) 
     10847    return (db, (win, ttl, df, ss, ooo, qq)) 
    1080210848 
    1080310849def p0f_correl(x,y): 
    1080410850    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. 
    1080610853    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)) 
    1080710854    # ttl 
    1080810855    d += (y[1] >= x[1] and y[1] - x[1] < 32) 
    1080910856    for i in [2, 3, 5]: 
    10810         d += (x[i] == y[i]
     10857        d += (x[i] == y[i] or y[i] == '*'
    1081110858    xopt = x[4].split(",") 
    1081210859    yopt = y[4].split(",") 
    1081310860    if len(xopt) == len(yopt): 
     
    1082710874 
    1082810875 
    1082910876def p0f(pkt): 
    10830     """Passive OS fingerprinting: which OS emitted this TCP SYN
     10877    """Passive OS fingerprinting: which OS emitted this TCP packet
    1083110878p0f(packet) -> accuracy, [list of guesses] 
    1083210879""" 
    10833     pb = p0f_kdb.get_base() 
     10880    db, sig = packet2p0f(pkt) 
     10881    if db: 
     10882        pb = db.get_base() 
     10883    else: 
     10884        pb = [] 
    1083410885    if not pb: 
    1083510886        warning("p0f base empty.") 
    1083610887        return [] 
    10837     s = len(pb[0][0]) 
     10888    #s = len(pb[0][0]) 
    1083810889    r = [] 
    10839     sig = packet2p0f(pkt) 
    1084010890    max = len(sig[4].split(",")) + 5 
    1084110891    for b in pb: 
    1084210892        d = p0f_correl(sig,b) 
    1084310893        if d == max: 
    1084410894            r.append((b[6], b[7], b[1] - pkt[IP].ttl)) 
    1084510895    return r 
    10846              
    1084710896 
    1084810897def prnp0f(pkt): 
     10898    # we should print which DB we use 
    1084910899    try: 
    1085010900        r = p0f(pkt) 
    1085110901    except: 
    1085210902        return 
    1085310903    if r == []: 
    10854         r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt))) + ":?:?]", None) 
     10904        r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt)[1])) + ":?:?]", None) 
    1085510905    else: 
    1085610906        r = r[0] 
    1085710907    uptime = None 
     
    1086310913        uptime = None 
    1086410914    res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) 
    1086510915    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%)") 
    1086710917    else: 
    10868         res += pkt.sprintf("\n  -> %IP.dst%:%TCP.dport%") 
     10918        res += pkt.sprintf("\n  -> %IP.dst%:%TCP.dport% (%TCP.flags%)") 
    1086910919    if r[2] is not None: 
    1087010920        res += " (distance " + str(r[2]) + ")" 
    1087110921    print res 
     
    1088910939    raise TypeError("No timestamp option") 
    1089010940 
    1089110941 
     10942def 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 
     10944specific OS.  If osdetails is None, then we randomly pick up a 
     10945personality matching osgenre. 
     10946 
     10947For now, only TCP Syn packets are supported. 
     10948Some 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 
    1089211088 
    1089311089################# 
    1089411090## Queso stuff ## 
     
    1320213403    histfile = os.path.join(os.environ["HOME"], ".scapy_history") 
    1320313404    padding = 1 
    1320413405    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" 
    1320513409    queso_base ="/etc/queso.conf" 
    1320613410    nmap_base ="/usr/share/nmap/nmap-os-fingerprints" 
    1320713411    IPCountry_base = "GeoIPCountry4Scapy.gz" 
     
    1324413448 
    1324513449 
    1324613450p0f_kdb = p0fKnowledgeBase(conf.p0f_base) 
     13451p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base) 
     13452p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base) 
     13453p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base) 
    1324713454queso_kdb = QuesoKnowledgeBase(conf.queso_base) 
    1324813455nmap_kdb = NmapKnowledgeBase(conf.nmap_base) 
    1324913456IP_country_kdb = IPCountryKnowledgeBase(conf.IPCountry_base)