diff --git a/scapy/asn1/asn1.py b/scapy/asn1/asn1.py index c8c42f72d40ffe74382809d30bdf1f7a630b3549..5e1866da5aae6d0152f6b42acfac575baf50b350 100644 --- a/scapy/asn1/asn1.py +++ b/scapy/asn1/asn1.py @@ -161,7 +161,9 @@ class ASN1_Class_UNIVERSAL(ASN1_Class): BMP_STRING = 30 IPADDRESS = 0x40 COUNTER32 = 0x41 + GAUGE32 = 0x42 TIME_TICKS = 0x43 + SEP = 0x80 class ASN1_Object_metaclass(type): def __new__(cls, name, bases, dct): @@ -246,15 +248,27 @@ class ASN1_IPADDRESS(ASN1_STRING): class ASN1_UTC_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME +class ASN1_GENERALIZED_TIME(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME + class ASN1_TIME_TICKS(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS class ASN1_BOOLEAN(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN + +class ASN1_ENUMERATED(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.ENUMERATED class ASN1_NULL(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.NULL +class ASN1_SEP(ASN1_NULL): + tag = ASN1_Class_UNIVERSAL.SEP + +class ASN1_GAUGE32(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.GAUGE32 + class ASN1_COUNTER32(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 @@ -276,6 +290,8 @@ class ASN1_OID(ASN1_Object): ASN1_Object.__init__(self, val) def __repr__(self): return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val)) + def __oidname__(self): + return '%s'%conf.mib._oidname(self.val) diff --git a/scapy/asn1/ber.py b/scapy/asn1/ber.py index 25c913dd63388d44329b4fca4159df82b260d56a..371274bf66950de660f393b0f5ddf7094c5507c0 100644 --- a/scapy/asn1/ber.py +++ b/scapy/asn1/ber.py @@ -223,6 +223,9 @@ class BERcodec_INTEGER(BERcodec_Object): class BERcodec_BOOLEAN(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN +class BERcodec_ENUMERATED(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.ENUMERATED + class BERcodec_NULL(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.NULL @classmethod @@ -232,6 +235,9 @@ class BERcodec_NULL(BERcodec_INTEGER): else: return super(cls,cls).enc(i) +class BERcodec_SEP(BERcodec_NULL): + tag = ASN1_Class_UNIVERSAL.SEP + class BERcodec_STRING(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.STRING @classmethod @@ -277,9 +283,15 @@ class BERcodec_IPADDRESS(BERcodec_STRING): class BERcodec_UTC_TIME(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME +class BERcodec_GENERALIZED_TIME(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME + class BERcodec_TIME_TICKS(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS +class BERcodec_GAUGE32(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.GAUGE32 + class BERcodec_COUNTER32(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 diff --git a/scapy/asn1fields.py b/scapy/asn1fields.py index 83722125e8b6298d606d83cfc6e7c1d6d0ac4ec0..c32fbe31b6bcb86fa6116478d217ec48a05e63d2 100644 --- a/scapy/asn1fields.py +++ b/scapy/asn1fields.py @@ -123,6 +123,9 @@ class ASN1F_BOOLEAN(ASN1F_field): class ASN1F_NULL(ASN1F_INTEGER): ASN1_tag= ASN1_Class_UNIVERSAL.NULL +class ASN1F_SEP(ASN1F_NULL): + ASN1_tag= ASN1_Class_UNIVERSAL.SEP + class ASN1F_enum_INTEGER(ASN1F_INTEGER): def __init__(self, name, default, enum): ASN1F_INTEGER.__init__(self, name, default) @@ -155,6 +158,9 @@ class ASN1F_enum_INTEGER(ASN1F_INTEGER): else: return self.i2repr_one(pkt,x) +class ASN1F_ENUMERATED(ASN1F_enum_INTEGER): + ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED + class ASN1F_STRING(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.STRING def randval(self): @@ -175,6 +181,9 @@ class ASN1F_TIME_TICKS(ASN1F_INTEGER): class ASN1F_UTC_TIME(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME +class ASN1F_GENERALIZED_TIME(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME + class ASN1F_OID(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.OID def randval(self): diff --git a/scapy/base_classes.py b/scapy/base_classes.py index 6d36053c021c16386dc593b9037da18273863ae6..c2fa4b73aa9611f6ddd6a296d1092b988047770c 100644 --- a/scapy/base_classes.py +++ b/scapy/base_classes.py @@ -46,28 +46,34 @@ class Net(Gen): """Generate a list of IPs from a network address or a name""" name = "ip" ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") - def __init__(self, net): - self.repr=net + @staticmethod + def _parse_digit(a,netmask): + netmask = min(8,max(netmask,0)) + if a == "*": + a = (0,256) + elif a.find("-") >= 0: + x,y = map(int,a.split("-")) + if x > y: + y = x + a = (x & (0xffL<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1) + else: + a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1) + return a + + @classmethod + def _parse_net(cls, net): tmp=net.split('/')+["32"] - if not self.ipaddress.match(net): + if not cls.ipaddress.match(net): tmp[0]=socket.gethostbyname(tmp[0]) netmask = int(tmp[1]) + return map(lambda x,y: cls._parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))),netmask + + def __init__(self, net): + self.repr=net + self.parsed,self.netmask = self._parse_net(net) - def parse_digit(a,netmask): - netmask = min(8,max(netmask,0)) - if a == "*": - a = (0,256) - elif a.find("-") >= 0: - x,y = map(int,a.split("-")) - if x > y: - y = x - a = (x & (0xffL<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1) - else: - a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1) - return a - self.parsed = map(lambda x,y: parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))) def __iter__(self): for d in xrange(*self.parsed[3]): @@ -83,6 +89,24 @@ class Net(Gen): def __repr__(self): return "Net(%r)" % self.repr + def __eq__(self, other): + if hasattr(other, "parsed"): + p2 = other.parsed + else: + p2,nm2 = self._parse_net(other) + return self.parsed == p2 + def __contains__(self, other): + if hasattr(other, "parsed"): + p2 = other.parsed + else: + p2,nm2 = self._parse_net(other) + for (a1,b1),(a2,b2) in zip(self.parsed,p2): + if a1 > a2 or b1 < b2: + return False + return True + def __rcontains__(self, other): + return self in self.__class__(other) + class OID(Gen): name = "OID" diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py index 5466e985721c3bae2c465fd2c57beaf0338057f2..5f5314d2b9bf3254ae12ae98d4adcb8a159b4345 100644 --- a/scapy/layers/inet6.py +++ b/scapy/layers/inet6.py @@ -206,6 +206,8 @@ class IP6Field(Field): vaddr = in6_6to4ExtractAddr(x) return "%s [6to4 GW: %s]" % (self.i2h(pkt, x), vaddr) return self.i2h(pkt, x) # No specific information to return + def randval(self): + return RandIP6() class SourceIP6Field(IP6Field): def __init__(self, name, dstname): diff --git a/scapy/layers/isakmp.py b/scapy/layers/isakmp.py index 8dd60ad8c069cad0a25764cb9c4fe19f05337473..ccbad10deb2a29906499abfb01122d423b20dc95 100644 --- a/scapy/layers/isakmp.py +++ b/scapy/layers/isakmp.py @@ -7,7 +7,8 @@ import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * -from scapy.layers.inet import UDP +from scapy.layers.inet import IP,UDP +from scapy.sendrecv import sr # see http://www.iana.org/assignments/ipsec-registry for details diff --git a/scapy/modules/p0f.py b/scapy/modules/p0f.py index 28542edfa6f578048e882a24ccb42e2e662e12ed..ebcf68d05940be09298b95365ccd5d3692906b78 100644 --- a/scapy/modules/p0f.py +++ b/scapy/modules/p0f.py @@ -5,8 +5,13 @@ from scapy.data import KnowledgeBase from scapy.config import conf +from scapy.layers.inet import IP, TCP, TCPOptions +from scapy.packet import NoPayload conf.p0f_base ="/etc/p0f/p0f.fp" +conf.p0fa_base ="/etc/p0f/p0fa.fp" +conf.p0fr_base ="/etc/p0f/p0fr.fp" +conf.p0fo_base ="/etc/p0f/p0fo.fp" ############### @@ -26,8 +31,6 @@ conf.p0f_base ="/etc/p0f/p0f.fp" # OS - OS genre # details - OS description - - class p0fKnowledgeBase(KnowledgeBase): def __init__(self, filename): KnowledgeBase.__init__(self, filename) @@ -46,7 +49,11 @@ class p0fKnowledgeBase(KnowledgeBase): l = tuple(l.split(":")) if len(l) < 8: continue - li = map(int,l[1:4]) + def a2i(x): + if x.isdigit(): + return int(x) + return x + li = map(a2i, l[1:4]) #if li[0] not in self.ttl_range: # self.ttl_range.append(li[0]) # self.ttl_range.sort() @@ -57,38 +64,70 @@ class p0fKnowledgeBase(KnowledgeBase): f.close() p0f_kdb = p0fKnowledgeBase(conf.p0f_base) +p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base) +p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base) +p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base) +def p0f_selectdb(flags): + # tested flags: S, R, A + if flags & 0x16 == 0x2: + # SYN + return p0f_kdb + elif flags & 0x16 == 0x12: + # SYN/ACK + return p0fa_kdb + elif flags & 0x16 in [ 0x4, 0x14 ]: + # RST RST/ACK + return p0fr_kdb + elif flags & 0x16 == 0x10: + # ACK + return p0fo_kdb + else: + return None def packet2p0f(pkt): + pkt = pkt.copy() + pkt = pkt.__class__(str(pkt)) while pkt.haslayer(IP) and pkt.haslayer(TCP): pkt = pkt.getlayer(IP) if isinstance(pkt.payload, TCP): break pkt = pkt.payload - + if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): raise TypeError("Not a TCP/IP packet") - if pkt.payload.flags & 0x13 != 0x02: #S,!A,!F - raise TypeError("Not a syn packet") + #if pkt.payload.flags & 0x7 != 0x02: #S,!F,!R + # raise TypeError("Not a SYN or SYN/ACK packet") + + db = p0f_selectdb(pkt.payload.flags) #t = p0f_kdb.ttl_range[:] #t += [pkt.ttl] #t.sort() #ttl=t[t.index(pkt.ttl)+1] ttl = pkt.ttl - + df = (pkt.flags & 2) / 2 ss = len(pkt) # from p0f/config.h : PACKET_BIG = 100 if ss > 100: - ss = 0 - + if db == p0fr_kdb: + # p0fr.fp: "Packet size may be wildcarded. The meaning of + # wildcard is, however, hardcoded as 'size > + # PACKET_BIG'" + ss = '*' + else: + ss = 0 + if db == p0fo_kdb: + # p0fo.fp: "Packet size MUST be wildcarded." + ss = '*' + ooo = "" mss = -1 qqT = False qqP = False #qqBroken = False - ilen = (pkt[TCP].dataofs << 2) - 20 # from p0f.c + ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c for option in pkt.payload.options: ilen -= 1 if option[0] == "MSS": @@ -118,60 +157,85 @@ def packet2p0f(pkt): if ilen > 0: qqP = True else: - ooo += "?," + if type(option[0]) is str: + ooo += "?%i," % TCPOptions[1][option[0]] + else: + ooo += "?%i," % option[0] # FIXME: ilen ooo = ooo[:-1] if ooo == "": ooo = "." - + win = pkt.payload.window if mss != -1: - if win % mss == 0: + if mss != 0 and win % mss == 0: win = "S" + str(win/mss) elif win % (mss + 40) == 0: win = "T" + str(win/(mss+40)) - win = str(win) - + win = str(win) + qq = "" - + + if db == p0fr_kdb: + if pkt.payload.flags & 0x10 == 0x10: + # p0fr.fp: "A new quirk, 'K', is introduced to denote + # RST+ACK packets" + qq += "K" + # The two next cases should also be only for p0f*r*, but although + # it's not documented (or I have not noticed), p0f seems to + # support the '0' and 'Q' quirks on any databases (or at the least + # "classical" p0f.fp). + if pkt.payload.seq == pkt.payload.ack: + # p0fr.fp: "A new quirk, 'Q', is used to denote SEQ number + # equal to ACK number." + qq += "Q" + if pkt.payload.seq == 0: + # p0fr.fp: "A new quirk, '0', is used to denote packets + # with SEQ number set to 0." + qq += "0" if qqP: qq += "P" - if pkt[IP].id == 0: + if pkt.id == 0: qq += "Z" - if pkt[IP].options != '': + if pkt.options != []: qq += "I" - if pkt[TCP].urgptr != 0: + if pkt.payload.urgptr != 0: qq += "U" - if pkt[TCP].reserved != 0: + if pkt.payload.reserved != 0: qq += "X" - if pkt[TCP].ack != 0: + if pkt.payload.ack != 0: qq += "A" if qqT: qq += "T" - if pkt[TCP].flags & 40 != 0: - # U or P - qq += "F" - if not isinstance(pkt[TCP].payload, NoPayload): + if db == p0fo_kdb: + if pkt.payload.flags & 0x20 != 0: + # U + # p0fo.fp: "PUSH flag is excluded from 'F' quirk checks" + qq += "F" + else: + if pkt.payload.flags & 0x28 != 0: + # U or P + qq += "F" + if db != p0fo_kdb and not isinstance(pkt.payload.payload, NoPayload): + # p0fo.fp: "'D' quirk is not checked for." qq += "D" - # FIXME : "!" - broken options segment + # FIXME : "!" - broken options segment: not handled yet if qq == "": qq = "." - return (win, - ttl, - df, - ss, - ooo, - qq) + return (db, (win, ttl, df, ss, ooo, qq)) def p0f_correl(x,y): d = 0 - # wwww can be "*" or "%nn" + # wwww can be "*" or "%nn". "Tnn" and "Snn" should work fine with + # the x[0] == y[0] test. 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)) # ttl d += (y[1] >= x[1] and y[1] - x[1] < 32) - for i in [2, 3, 5]: - d += (x[i] == y[i]) + for i in [2, 5]: + d += (x[i] == y[i] or y[i] == '*') + # '*' has a special meaning for ss + d += x[3] == y[3] xopt = x[4].split(",") yopt = y[4].split(",") if len(xopt) == len(yopt): @@ -192,31 +256,34 @@ def p0f_correl(x,y): @conf.commands.register def p0f(pkt): - """Passive OS fingerprinting: which OS emitted this TCP SYN ? + """Passive OS fingerprinting: which OS emitted this TCP packet ? p0f(packet) -> accuracy, [list of guesses] """ - pb = p0f_kdb.get_base() + db, sig = packet2p0f(pkt) + if db: + pb = db.get_base() + else: + pb = [] if not pb: warning("p0f base empty.") return [] - s = len(pb[0][0]) + #s = len(pb[0][0]) r = [] - sig = packet2p0f(pkt) max = len(sig[4].split(",")) + 5 for b in pb: d = p0f_correl(sig,b) if d == max: r.append((b[6], b[7], b[1] - pkt[IP].ttl)) return r - def prnp0f(pkt): + # we should print which DB we use try: r = p0f(pkt) except: return if r == []: - r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt))) + ":?:?]", None) + r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt)[1])) + ":?:?]", None) else: r = r[0] uptime = None @@ -228,16 +295,16 @@ def prnp0f(pkt): uptime = None res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) if uptime is not None: - res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport%") + res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") else: - res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport%") + res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") if r[2] is not None: res += " (distance " + str(r[2]) + ")" print res @conf.commands.register def pkt2uptime(pkt, HZ=100): - """Calculate the date the machine which emitted the packet booted using TCP timestamp + """Calculate the date the machine which emitted the packet booted using TCP timestamp pkt2uptime(pkt, [HZ=100])""" if not isinstance(pkt, Packet): raise TypeError("Not a TCP packet") @@ -253,4 +320,219 @@ pkt2uptime(pkt, [HZ=100])""" return t raise TypeError("No timestamp option") +def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None, + extrahops=0, mtu=1500, uptime=None): + """Modifies pkt so that p0f will think it has been sent by a +specific OS. If osdetails is None, then we randomly pick up a +personality matching osgenre. If osgenre and signature are also None, +we use a local signature (using p0f_getlocalsigs). If signature is +specified (as a tuple), we use the signature. + +For now, only TCP Syn packets are supported. +Some specifications of the p0f.fp file are not (yet) implemented.""" + pkt = pkt.copy() + #pkt = pkt.__class__(str(pkt)) + while pkt.haslayer(IP) and pkt.haslayer(TCP): + pkt = pkt.getlayer(IP) + if isinstance(pkt.payload, TCP): + break + pkt = pkt.payload + + if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): + raise TypeError("Not a TCP/IP packet") + + if uptime is None: + uptime = random.randint(120,100*60*60*24*365) + + db = p0f_selectdb(pkt.payload.flags) + if osgenre: + pb = db.get_base() + if pb is None: + pb = [] + pb = filter(lambda x: x[6] == osgenre, pb) + if osdetails: + pb = filter(lambda x: x[7] == osdetails, pb) + elif signature: + pb = [signature] + else: + pb = p0f_getlocalsigs()[db] + if db == p0fr_kdb: + # 'K' quirk <=> RST+ACK + if pkt.payload.flags & 0x4 == 0x4: + pb = filter(lambda x: 'K' in x[5], pb) + else: + pb = filter(lambda x: 'K' not in x[5], pb) + if not pb: + raise Scapy_Exception("No match in the p0f database") + pers = pb[random.randint(0, len(pb) - 1)] + + # options (we start with options because of MSS) + ## TODO: let the options already set if they are valid + options = [] + if pers[4] != '.': + for opt in pers[4].split(','): + if opt[0] == 'M': + # MSS might have a maximum size because of window size + # specification + if pers[0][0] == 'S': + maxmss = (2L**16-1) / int(pers[0][1:]) + else: + maxmss = (2L**16-1) + # If we have to randomly pick up a value, we cannot use + # scapy RandXXX() functions, because the value has to be + # set in case we need it for the window size value. That's + # why we use random.randint() + if opt[1:] == '*': + options.append(('MSS', random.randint(1,maxmss))) + elif opt[1] == '%': + coef = int(opt[2:]) + options.append(('MSS', coef*random.randint(1,maxmss/coef))) + else: + options.append(('MSS', int(opt[1:]))) + elif opt[0] == 'W': + if opt[1:] == '*': + options.append(('WScale', RandByte())) + elif opt[1] == '%': + coef = int(opt[2:]) + options.append(('WScale', coef*RandNum(min=1, + max=(2L**8-1)/coef))) + else: + options.append(('WScale', int(opt[1:]))) + elif opt == 'T0': + options.append(('Timestamp', (0, 0))) + elif opt == 'T': + if 'T' in pers[5]: + # FIXME: RandInt() here does not work (bug (?) in + # TCPOptionsField.m2i often raises "OverflowError: + # long int too large to convert to int" in: + # oval = struct.pack(ofmt, *oval)" + # Actually, this is enough to often raise the error: + # struct.pack('I', RandInt()) + options.append(('Timestamp', (uptime, random.randint(1,2**32-1)))) + else: + options.append(('Timestamp', (uptime, 0))) + elif opt == 'S': + options.append(('SAckOK', '')) + elif opt == 'N': + options.append(('NOP', None)) + elif opt == 'E': + options.append(('EOL', None)) + elif opt[0] == '?': + if int(opt[1:]) in TCPOptions[0]: + optname = TCPOptions[0][int(opt[1:])][0] + optstruct = TCPOptions[0][int(opt[1:])][1] + options.append((optname, + struct.unpack(optstruct, + RandString(struct.calcsize(optstruct))._fix()))) + else: + options.append((int(opt[1:]), '')) + ## FIXME: qqP not handled + else: + warning("unhandled TCP option " + opt) + pkt.payload.options = options + + # window size + if pers[0] == '*': + pkt.payload.window = RandShort() + elif pers[0].isdigit(): + pkt.payload.window = int(pers[0]) + elif pers[0][0] == '%': + coef = int(pers[0][1:]) + pkt.payload.window = coef * RandNum(min=1,max=(2L**16-1)/coef) + elif pers[0][0] == 'T': + pkt.payload.window = mtu * int(pers[0][1:]) + elif pers[0][0] == 'S': + ## needs MSS set + MSS = filter(lambda x: x[0] == 'MSS', options) + if not filter(lambda x: x[0] == 'MSS', options): + raise Scapy_Exception("TCP window value requires MSS, and MSS option not set") + pkt.payload.window = filter(lambda x: x[0] == 'MSS', options)[0][1] * int(pers[0][1:]) + else: + raise Scapy_Exception('Unhandled window size specification') + + # ttl + pkt.ttl = pers[1]-extrahops + # DF flag + pkt.flags |= (2 * pers[2]) + ## FIXME: ss (packet size) not handled (how ? may be with D quirk + ## if present) + # Quirks + if pers[5] != '.': + for qq in pers[5]: + ## FIXME: not handled: P, I, X, ! + # T handled with the Timestamp option + if qq == 'Z': pkt.id = 0 + elif qq == 'U': pkt.payload.urgptr = RandShort() + elif qq == 'A': pkt.payload.ack = RandInt() + elif qq == 'F': + if db == p0fo_kdb: + pkt.payload.flags |= 0x20 # U + else: + pkt.payload.flags |= RandChoice(8, 32, 40) #P / U / PU + elif qq == 'D' and db != p0fo_kdb: + pkt /= Raw(load=RandString(random.randint(1, 10))) # XXX p0fo.fp + elif qq == 'Q': pkt.payload.seq = pkt.payload.ack + #elif qq == '0': pkt.payload.seq = 0 + #if db == p0fr_kdb: + # '0' quirk is actually not only for p0fr.fp (see + # packet2p0f()) + if '0' in pers[5]: + pkt.payload.seq = 0 + elif pkt.payload.seq == 0: + pkt.payload.seq = RandInt() + + while pkt.underlayer: + pkt = pkt.underlayer + return pkt + +def p0f_getlocalsigs(): + """This function returns a dictionary of signatures indexed by p0f +db (e.g., p0f_kdb, p0fa_kdb, ...) for the local TCP/IP stack. + +You need to have your firewall at least accepting the TCP packets +from/to a high port (30000 <= x <= 40000) on your loopback interface. + +Please note that the generated signatures come from the loopback +interface and may (are likely to) be different than those generated on +"normal" interfaces.""" + pid = os.fork() + port = random.randint(30000, 40000) + if pid > 0: + # parent: sniff + result = {} + def addresult(res): + # TODO: wildcard window size in some cases? and maybe some + # other values? + if res[0] not in result: + result[res[0]] = [res[1]] + else: + if res[1] not in result[res[0]]: + result[res[0]].append(res[1]) + # XXX could we try with a "normal" interface using other hosts + iface = conf.route.route('127.0.0.1')[0] + # each packet is seen twice: S + RA, S + SA + A + FA + A + # XXX are the packets also seen twice on non Linux systems ? + count=14 + pl = sniff(iface=iface, filter='tcp and port ' + str(port), count = count, timeout=3) + map(addresult, map(packet2p0f, pl)) + os.waitpid(pid,0) + elif pid < 0: + log_runtime.error("fork error") + else: + # child: send + # XXX erk + time.sleep(1) + s1 = socket.socket(socket.AF_INET, type = socket.SOCK_STREAM) + # S & RA + try: + s1.connect(('127.0.0.1', port)) + except socket.error: + pass + # S, SA, A, FA, A + s1.bind(('127.0.0.1', port)) + s1.connect(('127.0.0.1', port)) + # howto: get an RST w/o ACK packet + s1.close() + os._exit(0) + return result diff --git a/scapy/plist.py b/scapy/plist.py index afed77fc3efad348ce9ec474fba419b4cd83a915..716727a04ac71183584bf89791d332707199f3f0 100644 --- a/scapy/plist.py +++ b/scapy/plist.py @@ -411,6 +411,7 @@ lfilter: truth function to apply to each packet to decide whether it will be dis def sessions(self, session_extractor=None): if session_extractor is None: def session_extractor(p): + sess = "Other" if 'Ether' in p: if 'IP' in p: if 'TCP' in p: diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index b0432686d306b1c28c10c3cc10825d9791c09c28..587b8396c80e19468806afb16153c73786136a58 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -203,7 +203,7 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0 return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered") -def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): +def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs): if type(x) is str: x = Raw(load=x) if not isinstance(x, Gen): @@ -215,9 +215,18 @@ def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): loop = -count elif not loop: loop=-1 + dt0 = None try: while loop: for p in x: + if realtime: + ct = time.time() + if dt0: + st = dt0+p.time-ct + if st > 0: + time.sleep(st) + else: + dt0 = ct-p.time s.send(p) n += 1 if verbose: @@ -232,18 +241,18 @@ def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): print "\nSent %i packets." % n @conf.commands.register -def send(x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): +def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs): """Send packets at layer 3 send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" - __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose) + __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose, realtime=realtime) @conf.commands.register -def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, *args, **kargs): +def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, *args, **kargs): """Send packets at layer 2 sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] - __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose) + __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose, realtime=realtime) @conf.commands.register def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, iface=None): diff --git a/scapy/utils6.py b/scapy/utils6.py index 4f9b096734d6c48a59d0b8a96ade79ba37ae3841..fd9a39a9471f93662d2863814bd99284bdde40d6 100644 --- a/scapy/utils6.py +++ b/scapy/utils6.py @@ -26,7 +26,7 @@ def construct_source_candidate_set(addr, plen, laddr, loname): """ cset = [] - if in6_isgladdr(addr): + if in6_isgladdr(addr) or in6_isuladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr) elif in6_islladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr) @@ -600,7 +600,7 @@ def in6_isuladdr(str): Returns True if provided address in printable format belongs to Unique local address space (fc00::/7). """ - return in6_isincluded(str, 'fc::', 7) + return in6_isincluded(str, 'fc00::', 7) # TODO : we should see the status of Unique Local addresses against # global address space. diff --git a/scapy/volatile.py b/scapy/volatile.py index d1f72c7bbbc043e1d03f4240bae096409f812080..bbe61e7f01d7e2652f4c300fd5bfe03af4a79ff7 100644 --- a/scapy/volatile.py +++ b/scapy/volatile.py @@ -66,6 +66,13 @@ class VolatileValue: def __getattr__(self, attr): if attr == "__setstate__": raise AttributeError(attr) + elif attr == "__cmp__": + x = self._fix() + def cmp2(y,x=x): + if type(x) != type(y): + return -1 + return x.__cmp__(y) + return cmp2 return getattr(self._fix(),attr) def _fix(self): return None @@ -226,13 +233,58 @@ class RandMAC(RandString): v = RandByte() elif "-" in template[i]: x,y = template[i].split("-") - v = RandSeq(int(x,16), int(y,16)) + v = RandNum(int(x,16), int(y,16)) else: v = int(template[i],16) self.mac += (v,) def _fix(self): return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac +class RandIP6(RandString): + def __init__(self, ip6template="**"): + self.tmpl = ip6template + self.sp = self.tmpl.split(":") + for i,v in enumerate(self.sp): + if not v or v == "**": + continue + if "-" in v: + a,b = v.split("-") + elif v == "*": + a=b="" + else: + a=b=v + + if not a: + a = "0" + if not b: + b = "ffff" + if a==b: + self.sp[i] = int(a,16) + else: + self.sp[i] = RandNum(int(a,16), int(b,16)) + self.variable = "" in self.sp + self.multi = self.sp.count("**") + def _fix(self): + done = 0 + nbm = self.multi + ip = [] + for i,n in enumerate(self.sp): + if n == "**": + nbm -= 1 + remain = 8-(len(self.sp)-i-1)-len(ip)+nbm + if "" in self.sp: + remain += 1 + if nbm or self.variable: + remain = random.randint(0,remain) + for j in range(remain): + ip.append("%04x" % random.randint(0,65535)) + elif not n: + ip.append("") + else: + ip.append("%04x" % n) + if len(ip) == 9: + ip.remove("") + return ":".join(ip) class RandOID(RandString): def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): @@ -267,8 +319,6 @@ class RandOID(RandString): return ".".join(oid) -from pprint import pprint - class RandRegExp(RandField): def __init__(self, regexp, lambda_=0.3,): self._regexp = regexp