1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
#!/usr/bin/env python
# 2012 Laurent Ghigonis <laurent@p1sec.com>
# 2012 Pierre-Olivier Vauboin <po@p1sec.com>
# TODO
# * [TOTEST] detect AP only if they send more bea than other
# * display UTF* or other charset bssid
# * display hex value when bssid is non-printable
# * Network View (who is conn, has ap ...)
# * store in redis Nodes and Networks
# * read pcap file
# * better UI
# * implement a time to forget about nodes
# * forget nodes on demand
import sys
import datetime
import getopt
import pdb #pdb.set_trace()
import pprint
import netaddr
from scapy.all import *
node_list = {}
# Dot11 Type and Subtype put to hex and concatenated
dot11_types = {
"00": ["arq", "Association request"],
"01": ["ars", "Association response"],
"04": ["prq", "Probe request"],
"05": ["prs", "Probe response"],
"08": ["bea", "Beacon frame"],
"0b": ["aut", "Authentication"],
"0c": ["dea", "Deauthentication"],
"0d": ["act", "Action"],
"19": ["bac", "Block Ack"],
"1b": ["cts", "Clear to Send"],
"1d": ["ack", "Acknowledgement"],
"20": ["dat", "Data"],
"24": ["nul", "Null function"],
"28": ["qda", "QoS Data"],
}
packet_counter=0
start_time = datetime.datetime.now()
last_time = 0
option_ap = False
option_debug = False
option_iface = "wlan0"
option_update_time = 3
option_mac_target = ""
def usage():
print "usage: %s [-ad] [-m mac_target] iface" % sys.argv[0]
def dot11_type_name(num):
if num in dot11_types:
return dot11_types[num][0]
else:
return "%s " % num
def dump_(obj, classkey=None):
if isinstance(obj, dict):
for k in obj.keys():
obj[k] = dump_(obj[k], classkey)
return obj
elif hasattr(obj, "__iter__"):
return [dump_(v, classkey) for v in obj]
elif hasattr(obj, "__dict__"):
data = dict([(key, dump_(value, classkey))
for key, value in obj.__dict__.iteritems()
if not callable(value) and not key.startswith('_')])
if classkey is not None and hasattr(obj, "__class__"):
data[classkey] = obj.__class__.__name__
return data
else:
return obj
def dump(obj, classkey=None):
pprint.pprint(dump_(obj, classkey))
def mac_lookup(mac):
mac_prefix = '-'.join(mac.replace(':', '-').split('-')[0:3])
try:
oui = netaddr.OUI(mac_prefix)
if oui.records:
vendor = oui.records[0]['org']
else:
vendor = 'Unknown'
except netaddr.NotRegisteredError:
vendor = 'Unknown'
return vendor
class Node:
def __init__(self, mac):
self.mac = mac
self.types = {}
self.pkt_recv_count = 0
def addpkt(self, pkt):
self.pkt_recv_count += 1
type1 = pkt[Dot11].type
type2 = pkt[Dot11].subtype
ftype = "%x%x" % (type1, type2)
if ftype not in self.types:
self.types[ftype] = {"count": 0}
self.types[ftype]["count"] += 1
if ftype in ["00", "04", "05", "08"]:
if "ssid" not in self.types[ftype]:
self.types[ftype]["ssid"] = set()
ssid = pkt[Dot11Elt].info if Dot11Elt in pkt else "#unknown"
if (pkt.addr2):
if (pkt.addr3):
Network.update(ssid, pkt.addr3)
else:
Network.update(ssid, pkt.addr2)
self.types[ftype]["ssid"].add(ssid)
else:
bssid = pkt[Dot11].addr3
if bssid:
if "bssid" not in self.types[ftype]:
self.types[ftype]["bssid"] = set()
self.types[ftype]["bssid"].add(bssid)
def is_ap(self):
if '08' in self.types.keys():
na = len(self.types['08'])
for t in self.types.keys():
if len(self.types[t]) > na:
return False
return True
return False
def __str__(self):
s = ""
s += self.mac + " %s (%s)" % (mac_lookup(self.mac), self.pkt_recv_count)
l = sorted(self.types.keys(), key=lambda x: self.types[x]["count"], reverse=True)
for t in l:
s += "\n"
s += " " + str(self.types[t]["count"]) + "\t" + dot11_type_name(t) + " "
for i in self.types[t]:
if i == "count": continue
if i == "bssid":
s += ','.join(map(Network.resolv, self.types[t][i]))
else:
s += ','.join(self.types[t][i])
return s
class Network:
_nets = {}
@classmethod
def resolv(cls, bssid):
for n in cls._nets.values():
if bssid in n.bssid:
return n.name
return bssid
@classmethod
def update(cls, name, bssid):
if name in cls._nets.keys():
cls._nets[name].bssid.add(bssid)
else:
n = Network(name)
n.addbssid(bssid)
cls._nets[name] = n
def __init__(self, name):
self.name = name
self.bssid = set()
Network._nets[name] = self
def addbssid(self, bssid):
self.bssid.add(bssid)
def analyse_packet(pkt):
# pkt.addr1 : bssid
# pkt.addr2 : station
# pkt.addr3 :
global packet_counter
global start_time, last_time
packet_counter+=1
try:
if pkt.addr2 is None:
if option_debug:
print "source None !!!"
return
except:
if option_debug:
print "no addr2 !!!"
return
if option_mac_target == "" or option_mac_target == pkt.addr2:
if pkt.addr2 not in node_list:
n = Node(pkt.addr2)
node_list[pkt.addr2] = n
node_list[pkt.addr2].addpkt(pkt)
#pdb.set_trace()
if packet_counter % 20 == 0:
time = datetime.datetime.now()
if time.second != last_time:
delta = time - start_time
if delta.seconds % option_update_time == 0:
last_time = time.second
print_stats()
def print_stats():
print "==================================== %d pkts, %d nodes" % (packet_counter, len(node_list))
l = sorted(node_list.keys(), key=lambda x: node_list[x].pkt_recv_count, reverse=True)
for cli in l:
if not option_ap and node_list[cli].is_ap():
continue
print node_list[cli]
if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], "adm:")
except getopt.GetoptError, err:
usage()
sys.exit(1)
for o, a in opts:
if o == "-a": option_ap=True
elif o == "-d": option_debug=True
elif o == "-m": option_mac_target=a
else:
print "unknown option %s" % o
sys.exit(1)
if len(args) < 1:
usage()
sys.exit(1)
option_iface = args[0]
print "Be sure that %s is in monitor mode." % option_iface
print "Use airodump at the same time if you want channel hopping. hop hop !"
sniff(iface=option_iface, prn=analyse_packet, store=0)
|