summaryrefslogtreecommitdiffstats
path: root/scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-03-21 16:03:47 +0200
committerimarom <imarom@cisco.com>2016-03-21 16:03:47 +0200
commitb89efa188810bf95a9d245e69e2961b5721c3b0f (patch)
tree454273ac6c4ae972ebb8a2c86b893296970b4fa9 /scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py
parentf72c6df9d2e9998ae1f3529d729ab7930b35785a (diff)
scapy python 2/3
Diffstat (limited to 'scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py')
-rw-r--r--scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py549
1 files changed, 0 insertions, 549 deletions
diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py b/scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py
deleted file mode 100644
index 289ef531..00000000
--- a/scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py
+++ /dev/null
@@ -1,549 +0,0 @@
-## This file is part of Scapy
-## See http://www.secdev.org/projects/scapy for more informations
-## Copyright (C) Philippe Biondi <phil@secdev.org>
-## This program is published under a GPLv2 license
-
-"""
-Clone of p0f passive OS fingerprinting
-"""
-
-from scapy.data import KnowledgeBase
-from scapy.config import conf
-from scapy.error import warning
-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"
-
-
-###############
-## p0f stuff ##
-###############
-
-# File format (according to p0f.fp) :
-#
-# wwww:ttt:D:ss:OOO...:QQ:OS:Details
-#
-# wwww - window size
-# ttt - initial TTL
-# D - don't fragment bit (0=unset, 1=set)
-# ss - overall SYN packet size
-# OOO - option value and order specification
-# QQ - quirks list
-# OS - OS genre
-# details - OS description
-
-class p0fKnowledgeBase(KnowledgeBase):
- def __init__(self, filename):
- KnowledgeBase.__init__(self, filename)
- #self.ttl_range=[255]
- def lazy_init(self):
- try:
- f=open(self.filename)
- except IOError:
- warning("Can't open base %s" % self.filename)
- return
- try:
- self.base = []
- for l in f:
- if l[0] in ["#","\n"]:
- continue
- l = tuple(l.split(":"))
- if len(l) < 8:
- continue
- def a2i(x):
- if x.isdigit():
- return int(x)
- return x
- li = [ a2i(i) for i in l[1:4] ]
- #if li[0] not in self.ttl_range:
- # self.ttl_range.append(li[0])
- # self.ttl_range.sort()
- self.base.append((l[0], li[0], li[1], li[2], l[4], l[5], l[6], l[7][:-1]))
- except:
- warning("Can't parse p0f database (new p0f version ?)")
- self.base = None
- 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__(bytes(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 & 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:
- 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.payload.dataofs << 2) - 20 # from p0f.c
- for option in pkt.payload.options:
- ilen -= 1
- if option[0] == "MSS":
- ooo += "M" + str(option[1]) + ","
- mss = option[1]
- # FIXME: qqBroken
- ilen -= 3
- elif option[0] == "WScale":
- ooo += "W" + str(option[1]) + ","
- # FIXME: qqBroken
- ilen -= 2
- elif option[0] == "Timestamp":
- if option[1][0] == 0:
- ooo += "T0,"
- else:
- ooo += "T,"
- if option[1][1] != 0:
- qqT = True
- ilen -= 9
- elif option[0] == "SAckOK":
- ooo += "S,"
- ilen -= 1
- elif option[0] == "NOP":
- ooo += "N,"
- elif option[0] == "EOL":
- ooo += "E,"
- if ilen > 0:
- qqP = True
- else:
- 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 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)
-
- 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.id == 0:
- qq += "Z"
- if pkt.options != []:
- qq += "I"
- if pkt.payload.urgptr != 0:
- qq += "U"
- if pkt.payload.reserved != 0:
- qq += "X"
- if pkt.payload.ack != 0:
- qq += "A"
- if qqT:
- qq += "T"
-# 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):
- if not isinstance(pkt.payload.payload, NoPayload):
- # p0fo.fp: "'D' quirk is not checked for."
- qq += "D"
- # FIXME : "!" - broken options segment: not handled yet
-
- if qq == "":
- qq = "."
-
- return (db, (win, ttl, df, ss, ooo, qq))
-
-def p0f_correl(x,y):
- d = 0
- # 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, 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):
- same = True
- for i in range(len(xopt)):
- if not (xopt[i] == yopt[i] or
- (len(yopt[i]) == 2 and len(xopt[i]) > 1 and
- yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or
- (len(yopt[i]) > 2 and len(xopt[i]) > 1 and
- yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and
- int(xopt[i][1:]) % int(yopt[i][2:]) == 0)):
- same = False
- break
- if same:
- d += len(xopt)
- return d
-
-
-@conf.commands.register
-def p0f(pkt):
- """Passive OS fingerprinting: which OS emitted this TCP packet ?
-p0f(packet) -> accuracy, [list of guesses]
-"""
- 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])
- r = []
- 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([ str(i) for i in packet2p0f(pkt)[1]]) + ":?:?]", None)
- else:
- r = r[0]
- uptime = None
- try:
- uptime = pkt2uptime(pkt)
- except:
- pass
- if uptime == 0:
- 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% (%TCP.flags%)")
- else:
- 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
-pkt2uptime(pkt, [HZ=100])"""
- if not isinstance(pkt, Packet):
- raise TypeError("Not a TCP packet")
- if isinstance(pkt,NoPayload):
- raise TypeError("Not a TCP packet")
- if not isinstance(pkt, TCP):
- return pkt2uptime(pkt.payload)
- for opt in pkt.options:
- if opt[0] == "Timestamp":
- #t = pkt.time - opt[1][0] * 1.0/HZ
- #return time.ctime(t)
- t = opt[1][0] / HZ
- 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)
- pb = [ x for x in pb if x[6] == osgenre ]
- if osdetails:
- #pb = filter(lambda x: x[7] == osdetails, pb)
- pb = [ x for x in pb if x[7] == osdetails ]
- 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)
- pb = [ x for x in pb if 'K' in x[5] ]
- else:
- #pb = filter(lambda x: 'K' not in x[5], pb)
- pb = [ x for x in pb if 'K' not in x[5] ]
- 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 = (2**16-1) / int(pers[0][1:])
- else:
- maxmss = (2**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=(2**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=(2**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)
- MSS = [ x for x in options if x[0] == 'MSS' ]
- if not MSS:
- raise Scapy_Exception("TCP window value requires MSS, and MSS option not set")
- pkt.payload.window = MSS[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 /= conf.raw_layer(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
-