From 0587c4b09a3f605a838f70bf3129909237b354f9 Mon Sep 17 00:00:00 2001 From: dadav <33197631+dadav@users.noreply.github.com> Date: Sun, 22 Dec 2019 13:17:07 +0100 Subject: [PATCH] Add switcher plugin --- pwnagotchi/defaults.yml | 9 ++ pwnagotchi/plugins/__init__.py | 4 + pwnagotchi/plugins/default/switcher.py | 147 +++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 pwnagotchi/plugins/default/switcher.py diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index bb0672d..8fe9bdd 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -13,6 +13,15 @@ main: custom_plugins: # which plugins to load and enable plugins: + switcher: + enabled: false + tasks: + bored: + enabled: false + reboot: true + commands: + - systemctl start fisch # see https://github.com/dadav/fisch + stopwatch: 15 # timeout of the task (in minutes) grid: enabled: true report: false # don't report pwned networks by default! diff --git a/pwnagotchi/plugins/__init__.py b/pwnagotchi/plugins/__init__.py index 72a3909..c8a70c6 100644 --- a/pwnagotchi/plugins/__init__.py +++ b/pwnagotchi/plugins/__init__.py @@ -60,6 +60,10 @@ def on(event_name, *args, **kwargs): def locked_cb(lock_name, cb, *args, **kwargs): global locks + + if lock_name not in locks: + locks[lock_name] = threading.Lock() + with locks[lock_name]: cb(*args, *kwargs) diff --git a/pwnagotchi/plugins/default/switcher.py b/pwnagotchi/plugins/default/switcher.py new file mode 100644 index 0000000..2faceed --- /dev/null +++ b/pwnagotchi/plugins/default/switcher.py @@ -0,0 +1,147 @@ +import os +import logging +from threading import Lock +from functools import partial +from pwnagotchi import plugins +from pwnagotchi import reboot + + +def systemd_dropin(name, content): + if not name.endswith('.service'): + name = '%s.service' % name + + dropin_dir = "/etc/systemd/system/%s.d/" % name + os.makedirs(dropin_dir, exist_ok=True) + + with open(os.path.join(dropin_dir, "switcher.conf"), "wt") as dropin: + dropin.write(content) + + systemctl("daemon-reload") + +def systemctl(command, unit=None): + if unit: + os.system("/bin/systemctl %s %s" % (command, unit)) + else: + os.system("/bin/systemctl %s" % command) + +def run_task(name, options): + task_service_name = "switcher-%s-task.service" % name + # save all the commands to a shell script + script_dir = '/usr/local/bin/' + script_path = os.path.join(script_dir, 'switcher-%s.sh' % name) + os.makedirs(script_dir, exist_ok=True) + + with open(script_path, 'wt') as script_file: + script_file.write('#!/bin/bash\n') + for cmd in options['commands']: + script_file.write('%s\n' % cmd) + + os.system("chmod a+x %s" % script_path) + + # here we create the service which runs the tasks + with open('/etc/systemd/system/%s' % task_service_name, 'wt') as task_service: + task_service.write(""" + [Unit] + Description=Executes the tasks of the pwnagotchi switcher plugin + After=pwnagotchi.service bettercap.service + + [Service] + Type=oneshot + RemainAfterExit=yes + ExecStart=-/usr/local/bin/switcher-%s.sh + ExecStart=-/bin/rm /etc/systemd/system/%s + ExecStart=-/bin/rm /usr/local/bin/switcher-%s.sh + + [Install] + WantedBy=multi-user.target + """ % (name, task_service_name, name)) + + if 'reboot' in options and options['reboot']: + # create a indication file! + # if this file is set, we want the switcher-tasks to run + open('/root/.switcher', 'a').close() + + # add condition + systemd_dropin("pwnagotchi.service", """ + [Unit] + ConditionPathExists=!/root/.switcher""") + + systemd_dropin("bettercap.service", """ + [Unit] + ConditionPathExists=!/root/.switcher""") + + systemd_dropin(task_service_name, """ + [Service] + ExecStart=-/bin/rm /root/.switcher + ExecStart=-/bin/rm /etc/systemd/system/switcher-reboot.timer""") + + with open('/etc/systemd/system/switcher-reboot.timer', 'wt') as reboot_timer: + reboot_timer.write(""" + [Unit] + Description=Reboot when time is up + ConditionPathExists=/root/.switcher + + [Timer] + OnBootSec=%sm + Unit=reboot.target + + [Install] + WantedBy=timers.target + """ % options['stopwatch']) + + systemctl("daemon-reload") + systemctl("enable", "switcher-reboot.timer") + systemctl("enable", task_service_name) + reboot() + return + + systemctl("daemon-reload") + systemctl("start", task_service_name) + +class Switcher(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __version__ = '0.0.1' + __name__ = 'switcher' + __license__ = 'GPL3' + __description__ = 'This plugin is a generic task scheduler.' + + def __init__(self): + self.ready = False + self.lock = Lock() + + def trigger(self, name, *args, **kwargs): + with self.lock: + function_name = name.lstrip('on_') + if function_name in self.tasks: + task = self.tasks[function_name] + + # is this task enabled? + if 'enabled' not in task or ('enabled' in task and not task['enabled']): + return + + run_task(function_name, task) + + def on_loaded(self): + if 'tasks' in self.options and self.options['tasks']: + self.tasks = self.options['tasks'] + else: + logging.debug('[switcher] No tasks found...') + return + + logging.info("[switcher] is loaded.") + + # create hooks + logging.debug("[switcher] creating hooks...") + methods = ['webhook', 'internet_available', 'ui_setup', 'ui_update', + 'unload', 'display_setup', 'ready', 'ai_ready', 'ai_policy', + 'ai_training_start', 'ai_training_step', 'ai_training_end', + 'ai_best_reward', 'ai_worst_reward', 'free_channel', + 'bored', 'sad', 'excited', 'lonely', 'rebooting', 'wait', + 'sleep', 'wifi_update', 'unfiltered_ap_list', 'association', + 'deauthentication', 'channel_hop', 'handshake', 'epoch', + 'peer_detected', 'peer_lost'] + + for m in methods: + setattr(Switcher, 'on_%s' % m, partial(self.trigger, m)) + + logging.debug("[switcher] triggers are ready to fire...")