1 % Tests for Scapy's p0f module. 2 3 ~ p0f 4 5 6 ############ 7 ############ 8 + Basic p0f module tests 9 10 = Module loading 11 load_module('p0f') 12 13 = Fetch database 14 from __future__ import print_function 15 try: 16 from urllib.request import urlopen 17 except ImportError: 18 from urllib2 import urlopen 19 20 def _load_database(file): 21 for i in range(10): 22 try: 23 open(file, 'wb').write(urlopen('https://raw.githubusercontent.com/p0f/p0f/4b4d1f384abebbb9b1b25b8f3c6df5ad7ab365f7/' + file).read()) 24 break 25 except: 26 raise 27 pass 28 29 _load_database("p0f.fp") 30 conf.p0f_base = "p0f.fp" 31 _load_database("p0fa.fp") 32 conf.p0fa_base = "p0fa.fp" 33 _load_database("p0fr.fp") 34 conf.p0fr_base = "p0fr.fp" 35 _load_database("p0fo.fp") 36 conf.p0fo_base = "p0fo.fp" 37 38 p0f_load_knowledgebases() 39 40 ############ 41 ############ 42 + Default tests 43 44 = Test p0f 45 46 pkt = Ether(b'\x14\x0cv\x8f\xfe(\xd0P\x99V\xdd\xf9\x08\x00E\x00\x0045+@\x00\x80\x06\x00\x00\xc0\xa8\x00w(M\xe2\xf9\xda\xcb\x01\xbbcc\xdd\x1e\x00\x00\x00\x00\x80\x02\xfa\xf0\xcc\x8c\x00\x00\x02\x04\x05\xb4\x01\x03\x03\x08\x01\x01\x04\x02') 47 48 assert p0f(pkt) == [('@Windows', 'XP/2000 (RFC1323+, w+, tstamp-)', 0)] 49 50 = Test prnp0f 51 52 with ContextManagerCaptureOutput() as cmco: 53 prnp0f(pkt) 54 assert cmco.get_output() == '192.168.0.119:56011 - @Windows XP/2000 (RFC1323+, w+, tstamp-)\n -> 40.77.226.249:https (S) (distance 0)\n' 55 56 ############ 57 ############ 58 + Tests for p0f_impersonate 59 60 # XXX: a lot of pieces of p0f_impersonate don't have tests yet. 61 62 = Impersonate when window size must be multiple of some integer 63 sig = ('%467', 64, 1, 60, 'M*,W*', '.', 'Phony Sys', '1.0') 64 pkt = p0f_impersonate(IP()/TCP(), signature=sig) 65 assert pkt.payload.window % 467 == 0 66 67 = Handle unusual flags ("F") quirk 68 sig = ('1024', 64, 0, 60, 'W*', 'F', 'Phony Sys', '1.0') 69 pkt = p0f_impersonate(IP()/TCP(), signature=sig) 70 assert (pkt.payload.flags & 40) in (8, 32, 40) 71 72 = Use valid option values from original packet 73 sig = ('S4', 64, 1, 60, 'M*,W*,T', '.', 'Phony Sys', '1.0') 74 opts = [('MSS', 1400), ('WScale', 3), ('Timestamp', (97256, 0))] 75 pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) 76 assert pkt.payload.options == opts 77 78 = Use valid option values when multiples required 79 sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0') 80 opts = [('MSS', 37*15), ('WScale', 19*12)] 81 pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) 82 assert pkt.payload.options == opts 83 84 = Discard non-multiple option values when multiples required 85 sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0') 86 opts = [('MSS', 37*15 + 1), ('WScale', 19*12 + 1)] 87 pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) 88 assert pkt.payload.options[0][1] % 37 == 0 89 assert pkt.payload.options[1][1] % 19 == 0 90 91 = Discard bad timestamp values 92 sig = ('S4', 64, 1, 60, 'M*,T', '.', 'Phony Sys', '1.0') 93 opts = [('Timestamp', (0, 1000))] 94 pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) 95 # since option is "T" and not "T0": 96 assert pkt.payload.options[1][1][0] > 0 97 # since T quirk is not present: 98 assert pkt.payload.options[1][1][1] == 0 99 100 = Discard 2nd timestamp of 0 if "T" quirk is present 101 sig = ('S4', 64, 1, 60, 'M*,T', 'T', 'Phony Sys', '1.0') 102 opts = [('Timestamp', (54321, 0))] 103 pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig) 104 assert pkt.payload.options[1][1][1] > 0 105 106 + Clear temp files 107 108 = Remove fp files 109 def _rem(f): 110 try: 111 os.remove(f) 112 except: 113 pass 114 115 _rem("p0f.fp") 116 _rem("p0fa.fp") 117 _rem("p0fr.fp") 118 _rem("p0fo.fp")