2019-09-19 15:15:46 +02:00
|
|
|
import time
|
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import re
|
2019-10-03 17:06:40 +02:00
|
|
|
import logging
|
2019-09-19 15:15:46 +02:00
|
|
|
import _thread
|
|
|
|
|
2019-10-13 12:18:35 +02:00
|
|
|
import pwnagotchi
|
2019-10-04 00:26:00 +02:00
|
|
|
import pwnagotchi.utils as utils
|
2019-10-02 19:01:07 +02:00
|
|
|
import pwnagotchi.plugins as plugins
|
2019-11-05 14:48:26 +01:00
|
|
|
from pwnagotchi.ui.web.server import Server
|
2019-10-23 18:19:43 +02:00
|
|
|
from pwnagotchi.automata import Automata
|
2019-10-08 14:54:03 +02:00
|
|
|
from pwnagotchi.log import LastSession
|
2019-10-04 00:42:12 +02:00
|
|
|
from pwnagotchi.bettercap import Client
|
2019-09-30 21:22:01 +02:00
|
|
|
from pwnagotchi.mesh.utils import AsyncAdvertiser
|
2019-09-19 15:15:46 +02:00
|
|
|
from pwnagotchi.ai.train import AsyncTrainer
|
|
|
|
|
|
|
|
RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery'
|
|
|
|
|
|
|
|
|
2019-10-23 18:19:43 +02:00
|
|
|
class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
2019-10-06 23:25:02 +02:00
|
|
|
def __init__(self, view, config, keypair):
|
2019-09-19 15:15:46 +02:00
|
|
|
Client.__init__(self, config['bettercap']['hostname'],
|
|
|
|
config['bettercap']['scheme'],
|
|
|
|
config['bettercap']['port'],
|
|
|
|
config['bettercap']['username'],
|
|
|
|
config['bettercap']['password'])
|
2019-10-23 18:19:43 +02:00
|
|
|
Automata.__init__(self, config, view)
|
2019-10-06 23:25:02 +02:00
|
|
|
AsyncAdvertiser.__init__(self, config, view, keypair)
|
2019-09-19 15:15:46 +02:00
|
|
|
AsyncTrainer.__init__(self, config)
|
|
|
|
|
|
|
|
self._started_at = time.time()
|
2019-12-18 18:58:28 +01:00
|
|
|
self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter'])
|
2019-09-19 15:15:46 +02:00
|
|
|
self._current_channel = 0
|
2019-11-12 22:51:10 +00:00
|
|
|
self._tot_aps = 0
|
|
|
|
self._aps_on_channel = 0
|
2019-10-04 00:26:00 +02:00
|
|
|
self._supported_channels = utils.iface_channels(config['main']['iface'])
|
2019-09-19 15:15:46 +02:00
|
|
|
self._view = view
|
2019-10-24 13:42:43 +02:00
|
|
|
self._view.set_agent(self)
|
2019-11-12 21:19:31 +01:00
|
|
|
self._web_ui = Server(self, config['ui'])
|
2019-11-05 14:48:26 +01:00
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
self._access_points = []
|
|
|
|
self._last_pwnd = None
|
|
|
|
self._history = {}
|
|
|
|
self._handshakes = {}
|
2019-10-08 14:54:03 +02:00
|
|
|
self.last_session = LastSession(self._config)
|
2019-11-04 17:35:06 +01:00
|
|
|
self.mode = 'auto'
|
2019-09-19 15:15:46 +02:00
|
|
|
|
2019-10-09 12:04:28 +02:00
|
|
|
if not os.path.exists(config['bettercap']['handshakes']):
|
|
|
|
os.makedirs(config['bettercap']['handshakes'])
|
|
|
|
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("%s@%s (v%s)", pwnagotchi.name(), self.fingerprint(), pwnagotchi.version)
|
2019-11-15 12:27:53 +01:00
|
|
|
for _, plugin in plugins.loaded.items():
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.debug("plugin '%s' v%s", plugin.__class__.__name__, plugin.__version__)
|
2019-11-15 12:27:53 +01:00
|
|
|
|
2019-10-02 19:41:50 +02:00
|
|
|
def config(self):
|
|
|
|
return self._config
|
|
|
|
|
2019-10-08 14:54:03 +02:00
|
|
|
def view(self):
|
|
|
|
return self._view
|
|
|
|
|
2019-09-21 18:52:00 +02:00
|
|
|
def supported_channels(self):
|
|
|
|
return self._supported_channels
|
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
def setup_events(self):
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("connecting to %s ...", self.url)
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
for tag in self._config['bettercap']['silence']:
|
|
|
|
try:
|
2019-12-07 15:43:10 +02:00
|
|
|
self.run('events.ignore %s' % tag, verbose_errors=False)
|
2019-09-19 15:15:46 +02:00
|
|
|
except Exception as e:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def _reset_wifi_settings(self):
|
|
|
|
mon_iface = self._config['main']['iface']
|
|
|
|
self.run('set wifi.interface %s' % mon_iface)
|
|
|
|
self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl'])
|
|
|
|
self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl'])
|
|
|
|
self.run('set wifi.rssi.min %d' % self._config['personality']['min_rssi'])
|
|
|
|
self.run('set wifi.handshakes.file %s' % self._config['bettercap']['handshakes'])
|
|
|
|
self.run('set wifi.handshakes.aggregate false')
|
|
|
|
|
|
|
|
def start_monitor_mode(self):
|
|
|
|
mon_iface = self._config['main']['iface']
|
|
|
|
mon_start_cmd = self._config['main']['mon_start_cmd']
|
|
|
|
restart = not self._config['main']['no_restart']
|
|
|
|
has_mon = False
|
|
|
|
|
|
|
|
while has_mon is False:
|
|
|
|
s = self.session()
|
|
|
|
for iface in s['interfaces']:
|
|
|
|
if iface['name'] == mon_iface:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("found monitor interface: %s", iface['name'])
|
2019-09-19 15:15:46 +02:00
|
|
|
has_mon = True
|
|
|
|
break
|
|
|
|
|
|
|
|
if has_mon is False:
|
|
|
|
if mon_start_cmd is not None and mon_start_cmd != '':
|
2019-10-03 17:06:40 +02:00
|
|
|
logging.info("starting monitor interface ...")
|
2019-09-19 15:15:46 +02:00
|
|
|
self.run('!%s' % mon_start_cmd)
|
|
|
|
else:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("waiting for monitor interface %s ...", mon_iface)
|
2019-09-19 15:15:46 +02:00
|
|
|
time.sleep(1)
|
|
|
|
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("supported channels: %s", self._supported_channels)
|
|
|
|
logging.info("handshakes will be collected inside %s", self._config['bettercap']['handshakes'])
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
self._reset_wifi_settings()
|
|
|
|
|
|
|
|
wifi_running = self.is_module_running('wifi')
|
|
|
|
if wifi_running and restart:
|
2019-10-03 17:06:40 +02:00
|
|
|
logging.debug("restarting wifi module ...")
|
2019-10-04 12:22:16 +02:00
|
|
|
self.restart_module('wifi.recon')
|
2019-09-19 15:15:46 +02:00
|
|
|
self.run('wifi.clear')
|
|
|
|
elif not wifi_running:
|
2019-10-03 17:06:40 +02:00
|
|
|
logging.debug("starting wifi module ...")
|
2019-10-04 12:22:16 +02:00
|
|
|
self.start_module('wifi.recon')
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
self.start_advertising()
|
|
|
|
|
2019-10-21 16:26:07 +02:00
|
|
|
def _wait_bettercap(self):
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
s = self.session()
|
|
|
|
return
|
|
|
|
except:
|
|
|
|
logging.info("waiting for bettercap API to be available ...")
|
|
|
|
time.sleep(1)
|
|
|
|
|
2019-10-04 00:11:31 +02:00
|
|
|
def start(self):
|
|
|
|
self.start_ai()
|
2019-10-21 16:26:07 +02:00
|
|
|
self._wait_bettercap()
|
2019-10-04 00:11:31 +02:00
|
|
|
self.setup_events()
|
|
|
|
self.set_starting()
|
|
|
|
self.start_monitor_mode()
|
|
|
|
self.start_event_polling()
|
|
|
|
# print initial stats
|
|
|
|
self.next_epoch()
|
|
|
|
self.set_ready()
|
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
def recon(self):
|
|
|
|
recon_time = self._config['personality']['recon_time']
|
|
|
|
max_inactive = self._config['personality']['max_inactive_scale']
|
|
|
|
recon_mul = self._config['personality']['recon_inactive_multiplier']
|
|
|
|
channels = self._config['personality']['channels']
|
|
|
|
|
|
|
|
if self._epoch.inactive_for >= max_inactive:
|
|
|
|
recon_time *= recon_mul
|
|
|
|
|
|
|
|
self._view.set('channel', '*')
|
|
|
|
|
|
|
|
if not channels:
|
|
|
|
self._current_channel = 0
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.debug("RECON %ds", recon_time)
|
2019-09-19 15:15:46 +02:00
|
|
|
self.run('wifi.recon.channel clear')
|
|
|
|
else:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.debug("RECON %ds ON CHANNELS %s", recon_time, ','.join(map(str, channels)))
|
2019-09-19 15:15:46 +02:00
|
|
|
try:
|
2019-12-07 15:43:10 +02:00
|
|
|
self.run('wifi.recon.channel %s' % ','.join(map(str, channels)))
|
2019-09-19 15:15:46 +02:00
|
|
|
except Exception as e:
|
2019-10-03 17:06:40 +02:00
|
|
|
logging.exception("error")
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
self.wait_for(recon_time, sleeping=False)
|
|
|
|
|
|
|
|
def _filter_included(self, ap):
|
|
|
|
return self._filter is None or \
|
|
|
|
self._filter.match(ap['hostname']) is not None or \
|
|
|
|
self._filter.match(ap['mac']) is not None
|
|
|
|
|
|
|
|
def set_access_points(self, aps):
|
|
|
|
self._access_points = aps
|
2019-10-02 19:01:07 +02:00
|
|
|
plugins.on('wifi_update', self, aps)
|
2019-10-13 17:24:47 +02:00
|
|
|
self._epoch.observe(aps, list(self._peers.values()))
|
2019-09-19 15:15:46 +02:00
|
|
|
return self._access_points
|
|
|
|
|
|
|
|
def get_access_points(self):
|
|
|
|
whitelist = self._config['main']['whitelist']
|
|
|
|
aps = []
|
|
|
|
try:
|
|
|
|
s = self.session()
|
2019-10-13 17:52:41 -04:00
|
|
|
plugins.on("unfiltered_ap_list", self, s['wifi']['aps'])
|
2019-09-19 15:15:46 +02:00
|
|
|
for ap in s['wifi']['aps']:
|
2019-10-30 17:50:56 +01:00
|
|
|
if ap['encryption'] == '' or ap['encryption'] == 'OPEN':
|
|
|
|
continue
|
2019-11-08 16:18:42 -06:00
|
|
|
elif ap['hostname'] not in whitelist \
|
2019-11-15 12:27:53 +01:00
|
|
|
and ap['mac'].lower() not in whitelist \
|
|
|
|
and ap['mac'][:8].lower() not in whitelist:
|
2019-09-19 15:15:46 +02:00
|
|
|
if self._filter_included(ap):
|
|
|
|
aps.append(ap)
|
|
|
|
except Exception as e:
|
2019-10-03 17:06:40 +02:00
|
|
|
logging.exception("error")
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
aps.sort(key=lambda ap: ap['channel'])
|
|
|
|
return self.set_access_points(aps)
|
|
|
|
|
2019-11-12 22:51:10 +00:00
|
|
|
def get_total_aps(self):
|
|
|
|
return self._tot_aps
|
|
|
|
|
|
|
|
def get_aps_on_channel(self):
|
|
|
|
return self._aps_on_channel
|
|
|
|
|
|
|
|
def get_current_channel(self):
|
|
|
|
return self._current_channel
|
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
def get_access_points_by_channel(self):
|
|
|
|
aps = self.get_access_points()
|
|
|
|
channels = self._config['personality']['channels']
|
|
|
|
grouped = {}
|
|
|
|
|
2019-09-22 12:08:11 +02:00
|
|
|
# group by channel
|
2019-09-19 15:15:46 +02:00
|
|
|
for ap in aps:
|
|
|
|
ch = ap['channel']
|
|
|
|
# if we're sticking to a channel, skip anything
|
|
|
|
# which is not on that channel
|
2019-12-13 19:29:44 +01:00
|
|
|
if channels and ch not in channels:
|
2019-09-19 15:15:46 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
if ch not in grouped:
|
|
|
|
grouped[ch] = [ap]
|
|
|
|
else:
|
|
|
|
grouped[ch].append(ap)
|
|
|
|
|
|
|
|
# sort by more populated channels
|
|
|
|
return sorted(grouped.items(), key=lambda kv: len(kv[1]), reverse=True)
|
|
|
|
|
|
|
|
def _find_ap_sta_in(self, station_mac, ap_mac, session):
|
|
|
|
for ap in session['wifi']['aps']:
|
|
|
|
if ap['mac'] == ap_mac:
|
|
|
|
for sta in ap['clients']:
|
|
|
|
if sta['mac'] == station_mac:
|
|
|
|
return (ap, sta)
|
|
|
|
return (ap, {'mac': station_mac, 'vendor': ''})
|
|
|
|
return None
|
|
|
|
|
|
|
|
def _update_uptime(self, s):
|
2019-10-13 12:18:35 +02:00
|
|
|
secs = pwnagotchi.uptime()
|
2019-10-04 00:26:00 +02:00
|
|
|
self._view.set('uptime', utils.secs_to_hhmmss(secs))
|
2019-10-13 12:18:35 +02:00
|
|
|
# self._view.set('epoch', '%04d' % self._epoch.epoch)
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
def _update_counters(self):
|
2019-11-12 22:51:10 +00:00
|
|
|
self._tot_aps = len(self._access_points)
|
2019-09-19 15:15:46 +02:00
|
|
|
tot_stas = sum(len(ap['clients']) for ap in self._access_points)
|
|
|
|
if self._current_channel == 0:
|
2019-11-12 22:51:10 +00:00
|
|
|
self._view.set('aps', '%d' % self._tot_aps)
|
2019-09-19 15:15:46 +02:00
|
|
|
self._view.set('sta', '%d' % tot_stas)
|
|
|
|
else:
|
2019-11-12 22:51:10 +00:00
|
|
|
self._aps_on_channel = len([ap for ap in self._access_points if ap['channel'] == self._current_channel])
|
2019-09-19 15:15:46 +02:00
|
|
|
stas_on_channel = sum(
|
|
|
|
[len(ap['clients']) for ap in self._access_points if ap['channel'] == self._current_channel])
|
2019-11-12 22:51:10 +00:00
|
|
|
self._view.set('aps', '%d (%d)' % (self._aps_on_channel, self._tot_aps))
|
2019-09-19 15:15:46 +02:00
|
|
|
self._view.set('sta', '%d (%d)' % (stas_on_channel, tot_stas))
|
|
|
|
|
|
|
|
def _update_handshakes(self, new_shakes=0):
|
|
|
|
if new_shakes > 0:
|
|
|
|
self._epoch.track(handshake=True, inc=new_shakes)
|
|
|
|
|
2019-10-04 00:26:00 +02:00
|
|
|
tot = utils.total_unique_handshakes(self._config['bettercap']['handshakes'])
|
2019-09-19 15:15:46 +02:00
|
|
|
txt = '%d (%d)' % (len(self._handshakes), tot)
|
|
|
|
|
|
|
|
if self._last_pwnd is not None:
|
2019-09-22 12:08:11 +02:00
|
|
|
txt += ' [%s]' % self._last_pwnd[:20]
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
self._view.set('shakes', txt)
|
|
|
|
|
|
|
|
if new_shakes > 0:
|
|
|
|
self._view.on_handshakes(new_shakes)
|
|
|
|
|
|
|
|
def _update_peers(self):
|
2019-10-13 17:24:47 +02:00
|
|
|
self._view.set_closest_peer(self._closest_peer, len(self._peers))
|
2019-09-19 15:15:46 +02:00
|
|
|
|
2019-10-23 18:19:43 +02:00
|
|
|
def _reboot(self):
|
|
|
|
self.set_rebooting()
|
|
|
|
self._save_recovery_data()
|
|
|
|
pwnagotchi.reboot()
|
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
def _save_recovery_data(self):
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
|
2019-09-19 15:15:46 +02:00
|
|
|
with open(RECOVERY_DATA_FILE, 'w') as fp:
|
|
|
|
data = {
|
|
|
|
'started_at': self._started_at,
|
|
|
|
'epoch': self._epoch.epoch,
|
|
|
|
'history': self._history,
|
|
|
|
'handshakes': self._handshakes,
|
|
|
|
'last_pwnd': self._last_pwnd
|
|
|
|
}
|
|
|
|
json.dump(data, fp)
|
|
|
|
|
|
|
|
def _load_recovery_data(self, delete=True, no_exceptions=True):
|
|
|
|
try:
|
|
|
|
with open(RECOVERY_DATA_FILE, 'rt') as fp:
|
|
|
|
data = json.load(fp)
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("found recovery data: %s", data)
|
2019-09-19 15:15:46 +02:00
|
|
|
self._started_at = data['started_at']
|
|
|
|
self._epoch.epoch = data['epoch']
|
|
|
|
self._handshakes = data['handshakes']
|
|
|
|
self._history = data['history']
|
|
|
|
self._last_pwnd = data['last_pwnd']
|
|
|
|
|
|
|
|
if delete:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("deleting %s", RECOVERY_DATA_FILE)
|
2019-09-19 15:15:46 +02:00
|
|
|
os.unlink(RECOVERY_DATA_FILE)
|
|
|
|
except:
|
|
|
|
if not no_exceptions:
|
|
|
|
raise
|
|
|
|
|
|
|
|
def _event_poller(self):
|
|
|
|
self._load_recovery_data()
|
|
|
|
|
|
|
|
self.run('events.clear')
|
|
|
|
|
|
|
|
while True:
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
new_shakes = 0
|
|
|
|
|
2019-10-23 19:32:24 +02:00
|
|
|
logging.debug("polling events ...")
|
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
try:
|
2019-10-21 16:22:38 +02:00
|
|
|
s = self.session()
|
|
|
|
self._update_uptime(s)
|
|
|
|
|
|
|
|
self._update_advertisement(s)
|
|
|
|
self._update_peers()
|
|
|
|
self._update_counters()
|
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
for h in [e for e in self.events() if e['tag'] == 'wifi.client.handshake']:
|
2019-10-02 19:41:50 +02:00
|
|
|
filename = h['data']['file']
|
2019-09-19 15:15:46 +02:00
|
|
|
sta_mac = h['data']['station']
|
|
|
|
ap_mac = h['data']['ap']
|
|
|
|
key = "%s -> %s" % (sta_mac, ap_mac)
|
|
|
|
|
|
|
|
if key not in self._handshakes:
|
|
|
|
self._handshakes[key] = h
|
|
|
|
new_shakes += 1
|
2019-10-03 12:50:55 +02:00
|
|
|
ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s)
|
|
|
|
if ap_and_station is None:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.warning("!!! captured new handshake: %s !!!", key)
|
2019-09-19 15:15:46 +02:00
|
|
|
self._last_pwnd = ap_mac
|
2019-10-02 19:41:50 +02:00
|
|
|
plugins.on('handshake', self, filename, ap_mac, sta_mac)
|
2019-09-19 15:15:46 +02:00
|
|
|
else:
|
2019-10-03 12:50:55 +02:00
|
|
|
(ap, sta) = ap_and_station
|
2019-09-19 15:15:46 +02:00
|
|
|
self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[
|
|
|
|
'hostname'] != '<hidden>' else ap_mac
|
2019-11-15 12:27:53 +01:00
|
|
|
logging.warning(
|
2019-12-03 11:34:40 -08:00
|
|
|
"!!! captured new handshake on channel %d, %d dBm: %s (%s) -> %s [%s (%s)] !!!",
|
2019-11-15 12:27:53 +01:00
|
|
|
ap['channel'],
|
|
|
|
ap['rssi'],
|
|
|
|
sta['mac'], sta['vendor'],
|
2019-12-03 11:34:40 -08:00
|
|
|
ap['hostname'], ap['mac'], ap['vendor'])
|
2019-10-02 19:41:50 +02:00
|
|
|
plugins.on('handshake', self, filename, ap, sta)
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
except Exception as e:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.error("error: %s", e)
|
2019-09-19 15:15:46 +02:00
|
|
|
|
|
|
|
finally:
|
|
|
|
self._update_handshakes(new_shakes)
|
|
|
|
|
|
|
|
def start_event_polling(self):
|
|
|
|
_thread.start_new_thread(self._event_poller, ())
|
|
|
|
|
|
|
|
def is_module_running(self, module):
|
|
|
|
s = self.session()
|
|
|
|
for m in s['modules']:
|
|
|
|
if m['name'] == module:
|
|
|
|
return m['running']
|
|
|
|
return False
|
|
|
|
|
2019-10-04 12:22:16 +02:00
|
|
|
def start_module(self, module):
|
2019-09-19 15:15:46 +02:00
|
|
|
self.run('%s on' % module)
|
|
|
|
|
2019-10-04 12:22:16 +02:00
|
|
|
def restart_module(self, module):
|
2019-09-19 15:15:46 +02:00
|
|
|
self.run('%s off; %s on' % (module, module))
|
|
|
|
|
|
|
|
def _has_handshake(self, bssid):
|
|
|
|
for key in self._handshakes:
|
|
|
|
if bssid.lower() in key:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def _should_interact(self, who):
|
|
|
|
if self._has_handshake(who):
|
|
|
|
return False
|
|
|
|
|
|
|
|
elif who not in self._history:
|
|
|
|
self._history[who] = 1
|
|
|
|
return True
|
|
|
|
|
|
|
|
else:
|
|
|
|
self._history[who] += 1
|
|
|
|
|
|
|
|
return self._history[who] < self._config['personality']['max_interactions']
|
|
|
|
|
|
|
|
def associate(self, ap, throttle=0):
|
|
|
|
if self.is_stale():
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.debug("recon is stale, skipping assoc(%s)", ap['mac'])
|
2019-09-19 15:15:46 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
if self._config['personality']['associate'] and self._should_interact(ap['mac']):
|
|
|
|
self._view.on_assoc(ap)
|
|
|
|
|
|
|
|
try:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("sending association frame to %s (%s %s) on channel %d [%d clients], %d dBm...",
|
|
|
|
ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients']), ap['rssi'])
|
2019-09-19 15:15:46 +02:00
|
|
|
self.run('wifi.assoc %s' % ap['mac'])
|
|
|
|
self._epoch.track(assoc=True)
|
|
|
|
except Exception as e:
|
|
|
|
self._on_error(ap['mac'], e)
|
|
|
|
|
2019-10-02 19:01:07 +02:00
|
|
|
plugins.on('association', self, ap)
|
2019-09-19 15:15:46 +02:00
|
|
|
if throttle > 0:
|
|
|
|
time.sleep(throttle)
|
|
|
|
self._view.on_normal()
|
|
|
|
|
|
|
|
def deauth(self, ap, sta, throttle=0):
|
|
|
|
if self.is_stale():
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.debug("recon is stale, skipping deauth(%s)", sta['mac'])
|
2019-09-19 15:15:46 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
if self._config['personality']['deauth'] and self._should_interact(sta['mac']):
|
|
|
|
self._view.on_deauth(sta)
|
|
|
|
|
|
|
|
try:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("deauthing %s (%s) from %s (%s %s) on channel %d, %d dBm ...",
|
|
|
|
sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], ap['rssi'])
|
2019-09-19 15:15:46 +02:00
|
|
|
self.run('wifi.deauth %s' % sta['mac'])
|
|
|
|
self._epoch.track(deauth=True)
|
|
|
|
except Exception as e:
|
|
|
|
self._on_error(sta['mac'], e)
|
|
|
|
|
2019-10-02 19:01:07 +02:00
|
|
|
plugins.on('deauthentication', self, ap, sta)
|
2019-09-19 15:15:46 +02:00
|
|
|
if throttle > 0:
|
|
|
|
time.sleep(throttle)
|
|
|
|
self._view.on_normal()
|
|
|
|
|
|
|
|
def set_channel(self, channel, verbose=True):
|
|
|
|
if self.is_stale():
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.debug("recon is stale, skipping set_channel(%d)", channel)
|
2019-09-19 15:15:46 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
# if in the previous loop no client stations has been deauthenticated
|
2019-09-22 12:08:11 +02:00
|
|
|
# and only association frames have been sent, we don't need to wait
|
2019-09-19 15:15:46 +02:00
|
|
|
# very long before switching channel as we don't have to wait for
|
|
|
|
# such client stations to reconnect in order to sniff the handshake.
|
|
|
|
wait = 0
|
|
|
|
if self._epoch.did_deauth:
|
|
|
|
wait = self._config['personality']['hop_recon_time']
|
|
|
|
elif self._epoch.did_associate:
|
|
|
|
wait = self._config['personality']['min_recon_time']
|
|
|
|
|
|
|
|
if channel != self._current_channel:
|
|
|
|
if self._current_channel != 0 and wait > 0:
|
|
|
|
if verbose:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("waiting for %ds on channel %d ...", wait, self._current_channel)
|
2019-10-03 17:06:40 +02:00
|
|
|
else:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.debug("waiting for %ds on channel %d ...", wait, self._current_channel)
|
2019-09-19 15:15:46 +02:00
|
|
|
self.wait_for(wait)
|
|
|
|
if verbose and self._epoch.any_activity:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.info("CHANNEL %d", channel)
|
2019-09-19 15:15:46 +02:00
|
|
|
try:
|
|
|
|
self.run('wifi.recon.channel %d' % channel)
|
|
|
|
self._current_channel = channel
|
|
|
|
self._epoch.track(hop=True)
|
|
|
|
self._view.set('channel', '%d' % channel)
|
2019-10-02 19:01:07 +02:00
|
|
|
|
|
|
|
plugins.on('channel_hop', self, channel)
|
|
|
|
|
2019-09-19 15:15:46 +02:00
|
|
|
except Exception as e:
|
2019-12-03 11:34:40 -08:00
|
|
|
logging.error("error: %s", e)
|