28
28
29
29
30
30
class Interceptor :
31
- def __init__ (self , net_iface , skip_monitor_mode_setup , kill_networkmanager ):
31
+ def __init__ (self , net_iface , skip_monitor_mode_setup , kill_networkmanager , bssid_name , custom_channels ):
32
32
self .interface = net_iface
33
33
self ._channel_sniff_timeout = 2
34
34
self ._scan_intv = 0.1
@@ -61,6 +61,36 @@ def __init__(self, net_iface, skip_monitor_mode_setup, kill_networkmanager):
61
61
self ._channel_range = {channel : defaultdict (dict ) for channel in self ._get_channels ()}
62
62
self ._all_ssids : Dict [BandType , Dict [str , SSID ]] = {band : dict () for band in BandType }
63
63
64
+ self ._custom_bssid_name : Union [str , None ] = self .parse_custom_bssid_name (bssid_name )
65
+ self ._custom_bssid_channels : List [int ] = self .parse_custom_channels (custom_channels )
66
+ self ._custom_bbsid_last_ch = 0 # to avoid overlapping
67
+
68
+ @staticmethod
69
+ def parse_custom_bssid_name (bssid_name : Union [None , str ]) -> Union [None , str ]:
70
+ if bssid_name is not None :
71
+ bssid_name = str (bssid_name )
72
+ if len (bssid_name ) != 0 :
73
+ print_error (f"Custom BSSID name cannot be an empty string" )
74
+ raise Exception ("Invalid BSSID name" )
75
+ return bssid_name
76
+
77
+ def parse_custom_channels (self , channel_list : Union [None , str ]):
78
+ ch_list = list ()
79
+ if channel_list is not None :
80
+ try :
81
+ ch_list = channel_list .split (',' )
82
+ except Exception as exc :
83
+ print_error (f"Invalid custom channel input -> { channel_list } " )
84
+ raise Exception ("Bad custom channel input" )
85
+
86
+ if len (ch_list ):
87
+ for ch in ch_list :
88
+ if ch not in self ._channel_range :
89
+ print_error (f"Custom channel { ch } is not supported by the network interface" )
90
+ raise Exception ("Unsupported channel" )
91
+ return ch_list
92
+
93
+
64
94
def _enable_monitor_mode (self ):
65
95
for cmd in [f"sudo ip link set { self .interface } down" ,
66
96
f"sudo iw { self .interface } set monitor control" ,
@@ -76,6 +106,7 @@ def _kill_networkmanager():
76
106
print_cmd (f"Running command -> '{ BOLD } { cmd } { RESET } '" )
77
107
return not os .system (cmd )
78
108
109
+
79
110
def _set_channel (self , ch_num ):
80
111
os .system (f"iw dev { self .interface } set channel { ch_num } " )
81
112
self ._current_channel_num = ch_num
@@ -90,30 +121,52 @@ def _ap_sniff_cb(self, pkt):
90
121
if pkt .haslayer (Dot11Beacon ) or pkt .haslayer (Dot11ProbeResp ):
91
122
ap_mac = str (pkt .addr3 )
92
123
ssid = pkt [Dot11Elt ].info .strip (b'\x00 ' ).decode ('utf-8' ).strip () or ap_mac
93
- if ap_mac == BD_MACADDR or not ssid :
124
+ if ap_mac == BD_MACADDR or not ssid or (self ._custom_bbsid_name_is_set ()
125
+ and ssid != self ._custom_bssid_name ):
94
126
return
95
127
pkt_ch = frequency_to_channel (pkt [RadioTap ].Channel )
96
128
band_type = BandType .T_50GHZ if pkt_ch > 14 else BandType .T_24GHZ
97
129
if ssid not in self ._all_ssids [band_type ]:
98
130
self ._all_ssids [band_type ][ssid ] = SSID (ssid , ap_mac , band_type )
99
131
self ._all_ssids [band_type ][ssid ].add_channel (pkt_ch if pkt_ch in self ._channel_range else self ._current_channel_num )
132
+ if self ._custom_bbsid_name_is_set ():
133
+ self ._custom_bbsid_last_ch = self ._all_ssids [band_type ][ssid ].channel
100
134
else :
101
135
self ._clients_sniff_cb (pkt ) # pass forward to find potential clients
102
136
except Exception as exc :
103
137
pass
104
138
105
139
def _scan_channels_for_aps (self ):
140
+ channels_to_scan = self ._custom_bssid_channels or self ._channel_range
141
+ print_info (f"Starting AP scan, please wait... ({ len (channels_to_scan )} channels total)" )
142
+ if self ._custom_bbsid_name_is_set ():
143
+ print_info (f"Scanning for target BBSID -> { self ._custom_bssid_name } " )
144
+
106
145
try :
107
- for idx , ch_num in enumerate (self ._channel_range ):
146
+ for idx , ch_num in enumerate (channels_to_scan ):
147
+ if self ._custom_bbsid_name_is_set () and self ._found_custom_bssid_name () \
148
+ and self ._current_channel_num - self ._custom_bbsid_last_ch > 2 :
149
+ # make sure sniffing doesn't stop on an overlapped channel for custom BBSIDs
150
+ return
108
151
self ._set_channel (ch_num )
109
152
print_info (f"Scanning channel { self ._current_channel_num } (left -> "
110
- f"{ len (self . _channel_range ) - (idx + 1 )} )" , end = "\r " )
153
+ f"{ len (channels_to_scan ) - (idx + 1 )} )" , end = "\r " )
111
154
sniff (prn = self ._ap_sniff_cb , iface = self .interface , timeout = self ._channel_sniff_timeout )
112
155
except KeyboardInterrupt :
113
156
self .user_abort ()
114
157
finally :
115
158
printf ("" )
116
159
160
+ def _found_custom_bssid_name (self ):
161
+ for all_channel_aps in self ._channel_range .values ():
162
+ for ssid_name in all_channel_aps .keys ():
163
+ if ssid_name == self ._custom_bssid_name :
164
+ return True
165
+ return False
166
+
167
+ def _custom_bbsid_name_is_set (self ):
168
+ return self ._custom_bssid_name is not None
169
+
117
170
def _start_initial_ap_scan (self ) -> SSID :
118
171
print_info (f"Starting AP scan, please wait... ({ len (self ._channel_range )} channels total)" )
119
172
@@ -142,7 +195,10 @@ def _start_initial_ap_scan(self) -> SSID:
142
195
exit (0 )
143
196
144
197
printf (DELIM )
198
+
145
199
chosen = - 1
200
+ if self ._custom_bbsid_name_is_set () and self ._found_custom_bssid_name ():
201
+ chosen = 0
146
202
while chosen not in target_map .keys ():
147
203
user_input = print_input (f"Choose a target from { min (target_map .keys ())} to { max (target_map .keys ())} :" )
148
204
try :
@@ -261,10 +317,16 @@ def user_abort(self):
261
317
default = False , dest = "skip_monitormode" , required = False )
262
318
parser .add_argument ('-k' , '--kill' , help = 'kill NetworkManager (might interfere with the process)' ,
263
319
action = 'store_true' , default = False , dest = "kill_networkmanager" , required = False )
320
+ parser .add_argument ('-b' , '--bbsid' , help = 'custom BBSID name (case-sensitive)' ,
321
+ action = 'store' , default = None , dest = "custom_bbsid" , required = False )
322
+ parser .add_argument ('-c' , '--channel' , help = 'custom channels to scan, separated by a comma (i.e -> 1,3,4)' ,
323
+ action = 'store' , default = None , dest = "custom_channels" , required = False )
264
324
pargs = parser .parse_args ()
265
325
266
326
invalidate_print () # after arg parsing
267
327
attacker = Interceptor (net_iface = pargs .net_iface ,
268
328
skip_monitor_mode_setup = pargs .skip_monitormode ,
269
- kill_networkmanager = pargs .kill_networkmanager )
329
+ kill_networkmanager = pargs .kill_networkmanager ,
330
+ bssid_name = custom_bbsid ,
331
+ custom_channels = custom_channels )
270
332
attacker .run ()
0 commit comments