From 8c9519f2eed39c0308cd0c9d25f4e040979ea750 Mon Sep 17 00:00:00 2001 From: Jeroen Oudshoorn Date: Tue, 5 Sep 2023 20:29:09 +0200 Subject: [PATCH] Version 2.2.9: Signed-off-by: Jeroen Oudshoorn --- bin/pwnagotchi | 27 +++- pwnagotchi/_version.py | 2 +- pwnagotchi/bettercap.py | 17 ++- pwnagotchi/defaults.toml | 32 +---- pwnagotchi/plugins/cmd.py | 2 +- pwnagotchi/plugins/default/auto-update.py | 11 +- pwnagotchi/plugins/default/fix_services.py | 25 +--- pwnagotchi/plugins/default/led.py | 159 --------------------- pwnagotchi/ui/hw/base.py | 2 + scripts/preview.py | 6 +- 10 files changed, 66 insertions(+), 217 deletions(-) delete mode 100644 pwnagotchi/plugins/default/led.py diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 03ae604f..775ef43e 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -12,7 +12,7 @@ from pwnagotchi.plugins import cmd as plugins_cmd from pwnagotchi import log from pwnagotchi import restart from pwnagotchi import fs -from pwnagotchi.utils import DottedTomlEncoder +from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple def do_clear(display): @@ -118,6 +118,11 @@ if __name__ == '__main__': parser.add_argument('--print-config', dest="print_config", action="store_true", default=False, help="Print the configuration.") + parser.add_argument('--check-update', dest="check_update", action="store_true", default=False, + help="Check for updates on Pwnagotchi.") + parser.add_argument('--donate', dest="donate", action="store_true", default=False, + help="How to donate to this project.") + args = parser.parse_args() if plugins_cmd.used_plugin_cmd(args): @@ -126,6 +131,26 @@ if __name__ == '__main__': rc = plugins_cmd.handle_cmd(args, config) sys.exit(rc) + if args.donate: + print("Donations can made @ https://www.patreon.com/pwnagotchi_torch \n\nBut only if you really want to!") + + if args.check_update: + resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi/releases/latest") + latest = resp.json() + latest_ver = latest['tag_name'].replace('v', '') + + local = version_to_tuple(pwnagotchi.__version__) + remote = version_to_tuple(latest_ver) + if remote > local: + user_input = input("There is a new version available! Update to %s? [y(es)/n(o)]" % latest_ver) + # input validation + if user_input.lower() in ('y', 'yes'): + os.system("rm /root/.auto-update && systemctl restart pwnagotchi") + elif user_input.lower() in ('n', 'no'): # using this elif for readability + print("Okay, guess not!") + else: + print("You are currently on the latest release, %s." % pwnagotchi.__version__) + if args.version: print(pwnagotchi.__version__) sys.exit(0) diff --git a/pwnagotchi/_version.py b/pwnagotchi/_version.py index bc570f20..00c3889f 100644 --- a/pwnagotchi/_version.py +++ b/pwnagotchi/_version.py @@ -1 +1 @@ -__version__ = '2.2.8' +__version__ = '2.2.9' diff --git a/pwnagotchi/bettercap.py b/pwnagotchi/bettercap.py index 592ac5c2..96d4a5aa 100644 --- a/pwnagotchi/bettercap.py +++ b/pwnagotchi/bettercap.py @@ -51,7 +51,18 @@ class Client(object): logging.debug("Lost websocket connection. Reconnecting...") except websockets.exceptions.WebSocketException as wex: logging.debug("Websocket exception (%s)", wex) + except Exception as e: + logging.exception("Other error while opening websocket (%s) with parameter %s", e, s) - def run(self, command, verbose_errors=True): - r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command}) - return decode(r, verbose_errors=verbose_errors) + +def run(self, command, verbose_errors=True): + for _ in range(0, 2): + try: + r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command}) + except requests.exceptions.ConnectionError as e: + logging.exception("Request connection error (%s) while running command (%s)", e, command) + sleep(1) # Sleep for 1-s before trying a second time + else: + break + + return decode(r, verbose_errors=verbose_errors) diff --git a/pwnagotchi/defaults.toml b/pwnagotchi/defaults.toml index c3915804..d6bd8f51 100644 --- a/pwnagotchi/defaults.toml +++ b/pwnagotchi/defaults.toml @@ -46,9 +46,9 @@ main.plugins.grid.exclude = [ "YourHomeNetworkHere" ] -main.plugins.auto-update.enabled = false -main.plugins.auto-update.install = false -main.plugins.auto-update.interval = 24 +main.plugins.auto-update.enabled = true +main.plugins.auto-update.install = true +main.plugins.auto-update.interval = 1 main.plugins.net-pos.enabled = false main.plugins.net-pos.api_key = "test" @@ -119,32 +119,6 @@ main.plugins.ups_lite.shutdown = 2 main.plugins.gpio_buttons.enabled = false -main.plugins.led.enabled = false -main.plugins.led.led = 0 -main.plugins.led.delay = 200 -main.plugins.led.patterns.loaded = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.updating = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.unread_inbox = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.ready = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.ai_ready = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.ai_training_start = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.ai_best_reward = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.ai_worst_reward = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.bored = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.sad = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.excited = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.lonely = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.rebooting = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.wait = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.sleep = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.wifi_update = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.association = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.deauthentication = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.handshake = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.epoch = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.peer_detected = "oo oo oo oo oo oo oo" -main.plugins.led.patterns.peer_lost = "oo oo oo oo oo oo oo" - main.plugins.webcfg.enabled = true main.plugins.logtail.enabled = false diff --git a/pwnagotchi/plugins/cmd.py b/pwnagotchi/plugins/cmd.py index eb87cb96..561191d5 100644 --- a/pwnagotchi/plugins/cmd.py +++ b/pwnagotchi/plugins/cmd.py @@ -10,7 +10,7 @@ from pwnagotchi.utils import download_file, unzip, save_config, parse_version, m from pwnagotchi.plugins import default_path -SAVE_DIR = '/usr/local/share/pwnagotchi/availaible-plugins/' +SAVE_DIR = '/usr/local/share/pwnagotchi/available-plugins/' DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/' diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py index 1d65efdc..cc229d82 100644 --- a/pwnagotchi/plugins/default/auto-update.py +++ b/pwnagotchi/plugins/default/auto-update.py @@ -123,8 +123,13 @@ def install(display, update): if not os.path.exists(source_path): source_path = "%s-%s" % (source_path, update['available']) - # setup.py is going to install data files for us - os.system("cd %s && pip3 install ." % source_path) + if "pwngrid" in source_path: + os.system("cd %s && make && make install" % source_path) + elif "bettercap" in source_path: + os.system("cd %s && make && make install" % source_path) + else: + # setup.py is going to install data files for us + os.system("cd %s && pip3 install ." % source_path) return True @@ -182,7 +187,7 @@ class AutoUpdate(plugins.Plugin): to_install = [] to_check = [ ('jayofelony/bettercap', parse_version('bettercap -version'), True, 'bettercap'), - ('evilsocket/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'), + ('jayofelony/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'), ('jayofelony/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi') ] diff --git a/pwnagotchi/plugins/default/fix_services.py b/pwnagotchi/plugins/default/fix_services.py index b94b60f0..485e4403 100644 --- a/pwnagotchi/plugins/default/fix_services.py +++ b/pwnagotchi/plugins/default/fix_services.py @@ -35,9 +35,8 @@ class Fix_BRCMF(plugins.Plugin): self.options = dict() self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed') self.pattern2 = re.compile(r'wifi error while hopping to channel') - self.pattern3 = re.compile(r'Firmware has halted or crashed') + self.pattern3 = re.compile(r'error 400: could not find interface wlan0mon') self.pattern4 = re.compile(r'AI not loaded!') - self.pattern5 = re.compile(r'ConnectionError') self.isReloadingMon = False self.connection = None self.LASTTRY = 0 @@ -113,6 +112,8 @@ class Fix_BRCMF(plugins.Plugin): stdout=subprocess.PIPE).stdout))[-10:]) other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'], stdout=subprocess.PIPE).stdout))[-10:]) + other_other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/var/log/pwnagotchi.log'], + stdout=subprocess.PIPE).stdout))[-10:]) # don't check if we ran a reset recently logging.debug("[FixBRCMF]**** epoch") if time.time() - self.LASTTRY > 180: @@ -159,7 +160,7 @@ class Fix_BRCMF(plugins.Plugin): logging.error("[FixBRCMF wifi.recon flip] %s" % repr(err)) # Look for pattern 3 - elif len(self.pattern3.findall(other_last_lines)) >= 1: + elif len(self.pattern3.findall(other_other_last_lines)) >= 1: logging.info("[FixBRCMF] Firmware has halted or crashed. Restarting wlan0mon.") if hasattr(agent, 'view'): display = agent.view() @@ -167,14 +168,14 @@ class Fix_BRCMF(plugins.Plugin): display.update(force=True) try: # Run the monstart command to restart wlan0mon - cmd_output = restart("AUTO") + cmd_output = subprocess.check_output("monstart", shell=True) self._status = "up" logging.info("[FixBRCMF monstart]: %s" % repr(cmd_output)) except Exception as err: logging.error("[FixBRCMF monstart]: %s" % repr(err)) # Look for pattern 4 - elif len(self.pattern4.findall(other_last_lines)) >= 1: + elif len(self.pattern4.findall(other_other_last_lines)) >= 1: logging.info("[FixBRCMF] Having a brain meltdown. Deleting myself.") if hasattr(agent, 'view'): display = agent.view() @@ -190,20 +191,6 @@ class Fix_BRCMF(plugins.Plugin): except Exception as err: logging.error("[FixBRCMF brain]: %s" % repr(err)) - # Look for pattern 5 - elif len(self.pattern5.findall(other_last_lines)) >= 1: - logging.info("[FixBRCMF] Bettercap connection failure. Restarting Bettercap.") - if hasattr(agent, 'view'): - display = agent.view() - display.set('status', 'Bettercap connection failure. Restarting Bettercap.') - try: - # Delete brain /root/brain.nn and restarting pwnagotchi service - cmd_output = subprocess.check_output("systemctl restart bettercap", - shell=True) - self._status = "up" - logging.info("[FixBRCMF bettercap]: %s" % repr(cmd_output)) - except Exception as err: - logging.error("[FixBRCMF bettercap]: %s" % repr(err)) else: print("logs look good") diff --git a/pwnagotchi/plugins/default/led.py b/pwnagotchi/plugins/default/led.py deleted file mode 100644 index 4ac1acc5..00000000 --- a/pwnagotchi/plugins/default/led.py +++ /dev/null @@ -1,159 +0,0 @@ -from threading import Event -import _thread -import logging -import time - -import pwnagotchi.plugins as plugins - - -class Led(plugins.Plugin): - __author__ = 'evilsocket@gmail.com' - __version__ = '1.0.0' - __license__ = 'GPL3' - __description__ = 'This plugin blinks the PWR led with different patterns depending on the event.' - - def __init__(self): - self._is_busy = False - self._event = Event() - self._event_name = None - self._led_file = "/sys/class/leds/led0/brightness" - self._delay = 200 - - # called when the plugin is loaded - def on_loaded(self): - self._led_file = "/sys/class/leds/led%d/brightness" % self.options['led'] - self._delay = int(self.options['delay']) - - logging.info("[led] plugin loaded for %s" % self._led_file) - self._on_event('loaded') - _thread.start_new_thread(self._worker, ()) - - def _on_event(self, event): - if not self._is_busy: - self._event_name = event - self._event.set() - logging.debug("[led] event '%s' set", event) - else: - logging.debug("[led] skipping event '%s' because the worker is busy", event) - - def _led(self, on): - with open(self._led_file, 'wt') as fp: - fp.write(str(on)) - - def _blink(self, pattern): - logging.debug("[led] using pattern '%s' ..." % pattern) - for c in pattern: - if c == ' ': - self._led(1) - else: - self._led(0) - time.sleep(self._delay / 1000.0) - # reset - self._led(0) - - def _worker(self): - while True: - self._event.wait() - self._event.clear() - self._is_busy = True - - try: - if self._event_name in self.options['patterns']: - pattern = self.options['patterns'][self._event_name] - self._blink(pattern) - else: - logging.debug("[led] no pattern defined for %s" % self._event_name) - except Exception as e: - logging.exception("[led] error while blinking") - - finally: - self._is_busy = False - - # called when the unit is updating its software - def on_updating(self): - self._on_event('updating') - - # called when there's one or more unread pwnmail messages - def on_unread_inbox(self, num_unread): - self._on_event('unread_inbox') - - # called when there's internet connectivity - def on_internet_available(self, agent): - self._on_event('internet_available') - - # called when everything is ready and the main loop is about to start - def on_ready(self, agent): - self._on_event('ready') - - # called when the AI finished loading - def on_ai_ready(self, agent): - self._on_event('ai_ready') - - # called when the AI starts training for a given number of epochs - def on_ai_training_start(self, agent, epochs): - self._on_event('ai_training_start') - - # called when the AI got the best reward so far - def on_ai_best_reward(self, agent, reward): - self._on_event('ai_best_reward') - - # called when the AI got the worst reward so far - def on_ai_worst_reward(self, agent, reward): - self._on_event('ai_worst_reward') - - # called when the status is set to bored - def on_bored(self, agent): - self._on_event('bored') - - # called when the status is set to sad - def on_sad(self, agent): - self._on_event('sad') - - # called when the status is set to excited - def on_excited(self, agent): - self._on_event('excited') - - # called when the status is set to lonely - def on_lonely(self, agent): - self._on_event('lonely') - - # called when the agent is rebooting the board - def on_rebooting(self, agent): - self._on_event('rebooting') - - # called when the agent is waiting for t seconds - def on_wait(self, agent, t): - self._on_event('wait') - - # called when the agent is sleeping for t seconds - def on_sleep(self, agent, t): - self._on_event('sleep') - - # called when the agent refreshed its access points list - def on_wifi_update(self, agent, access_points): - self._on_event('wifi_update') - - # called when the agent is sending an association frame - def on_association(self, agent, access_point): - self._on_event('association') - - # called when the agent is deauthenticating a client station from an AP - def on_deauthentication(self, agent, access_point, client_station): - self._on_event('deauthentication') - - # called when a new handshake is captured, access_point and client_station are json objects - # if the agent could match the BSSIDs to the current list, otherwise they are just the strings of the BSSIDs - def on_handshake(self, agent, filename, access_point, client_station): - self._on_event('handshake') - - # called when an epoch is over (where an epoch is a single loop of the main algorithm) - def on_epoch(self, agent, epoch, epoch_data): - self._on_event('epoch') - - # called when a new peer is detected - def on_peer_detected(self, agent, peer): - self._on_event('peer_detected') - - # called when a known peer is lost - def on_peer_lost(self, agent, peer): - self._on_event('peer_lost') diff --git a/pwnagotchi/ui/hw/base.py b/pwnagotchi/ui/hw/base.py index 72793b0f..f4cdd8a6 100644 --- a/pwnagotchi/ui/hw/base.py +++ b/pwnagotchi/ui/hw/base.py @@ -3,6 +3,8 @@ import pwnagotchi.ui.fonts as fonts class DisplayImpl(object): def __init__(self, config, name): + if fonts.Medium is None: + fonts.init(config) self.name = name self.config = config['ui']['display'] self._layout = { diff --git a/scripts/preview.py b/scripts/preview.py index 4890ecab..4c717306 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -101,6 +101,10 @@ def main(): main: lang: {lang} ui: + font: + name: 'DejaVuSansMono' + size_offset: 0 + size: 0 fps: 0.3 display: enabled: false @@ -110,7 +114,7 @@ def main(): type: {display} web: enabled: true - address: "0.0.0.0" + address: '::' port: 8080 faces: