def on_webhook(self, path, request): if not self.ready: return "Plugin not ready" if path == "/" or not path: pcapfiles = glob.glob( os.path.join(self.config['bettercap']['handshakes'], "*.pcap")) data = [] for path in pcapfiles: name = os.path.basename(path)[:-5] fullpathNoExt = path[:-5] possibleExt = ['.2500', '.16800', '.22000'] foundExt = ['.pcap'] for ext in possibleExt: if os.path.isfile(fullpathNoExt + ext): foundExt.append(ext) data.append(handshakes(name, fullpathNoExt, foundExt)) return render_template_string(TEMPLATE, title="Handshakes | " + pwnagotchi.name(), handshakes=data) else: dir = self.config['bettercap']['handshakes'] try: logging.info(f"[HandshakesDL] serving {dir}/{path}") return send_from_directory(directory=dir, filename=path, as_attachment=True) except FileNotFoundError: abort(404)
def on_webhook(self, path, request): if not self.ready: return "Plugin not ready" if path == "/" or not path: handshakes = glob.glob(os.path.join(self.config['bettercap']['handshakes'], "*.pcap")) handshakes = [os.path.basename(path)[:-5] for path in handshakes] return render_template_string(TEMPLATE, title="Handshakes | " + pwnagotchi.name(), handshakes=handshakes) elif path == "all": logging.info(f"[hndshk-dl-all] creating Zip-File in memory") memory_file = BytesIO() with zipfile.ZipFile(memory_file, 'w') as zf: files = glob.glob(os.path.join(self.config['bettercap']['handshakes'], "*.*")) try: for individualFile in files: zf.write(individualFile) except Exception as e: logging.error(f"[hndshk-dl-all] {e}") abort(404) memory_file.seek(0) logging.info(f"[hndshk-dl-all] serving handshakes.zip") return send_file(memory_file, attachment_filename='handshakesALL.zip', as_attachment=True) else: dir = self.config['bettercap']['handshakes'] try: logging.info(f"[hndshk-dl-all] serving {dir}/{path}.pcap") return send_from_directory(directory=dir, filename=path+'.pcap', as_attachment=True) except FileNotFoundError: abort(404)
def do_GET(self): if self.path == '/': self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() try: self.wfile.write( bytes(self._index % (pwnagotchi.name(), 1000), "utf8")) except: pass elif self.path.startswith('/shutdown'): pwnagotchi.shutdown() elif self.path.startswith('/ui'): with self._lock: self.send_response(200) self.send_header('Content-type', 'image/png') self.end_headers() try: with open("/root/pwnagotchi.png", 'rb') as fp: shutil.copyfileobj(fp, self.wfile) except: pass else: self.send_response(404)
def __init__(self, view, config, keypair): Client.__init__(self, config['bettercap']['hostname'], config['bettercap']['scheme'], config['bettercap']['port'], config['bettercap']['username'], config['bettercap']['password']) Automata.__init__(self, config, view) AsyncAdvertiser.__init__(self, config, view, keypair) AsyncTrainer.__init__(self, config) self._started_at = time.time() self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter']) self._current_channel = 0 self._tot_aps = 0 self._aps_on_channel = 0 self._supported_channels = utils.iface_channels(config['main']['iface']) self._view = view self._view.set_agent(self) self._web_ui = Server(self, config['ui']) self._access_points = [] self._last_pwnd = None self._history = {} self._handshakes = {} self.last_session = LastSession(self._config) self.mode = 'auto' if not os.path.exists(config['bettercap']['handshakes']): os.makedirs(config['bettercap']['handshakes']) logging.info("%s@%s (v%s)", pwnagotchi.name(), self.fingerprint(), pwnagotchi.__version__) for _, plugin in plugins.loaded.items(): logging.debug("plugin '%s' v%s", plugin.__class__.__name__, plugin.__version__)
def shutdown(self): try: return render_template('status.html', title=pwnagotchi.name(), go_back_after=60, message='Shutting down ...') finally: _thread.start_new_thread(pwnagotchi.shutdown, ())
def reboot(self): try: return render_template('status.html', title=pwnagotchi.name(), go_back_after=60, message='Rebooting ...') finally: _thread.start_new_thread(pwnagotchi.reboot, ())
def index(self): theme = self._config['theme'] theme_page = "theme-" + theme + ".html" return render_template( theme_page, title=pwnagotchi.name(), other_mode='AUTO' if self._agent.mode == 'manual' else 'MANU', fingerprint=self._agent.fingerprint())
def restart(self): mode = request.form['mode'] if mode not in ('AUTO', 'MANU'): mode = 'MANU' try: return render_template('status.html', title=pwnagotchi.name(), go_back_after=30, message='Restarting in %s mode ...' % mode) finally: _thread.start_new_thread(pwnagotchi.restart, (mode,))
def get_api_token(last_session, keys): global AUTH if AUTH.newer_then_minutes( 25) and AUTH.data is not None and 'token' in AUTH.data: return AUTH.data['token'] if AUTH.data is None: logging.info("grid: enrolling unit ...") else: logging.info("grid: refreshing token ...") identity = "%s@%s" % (pwnagotchi.name(), keys.fingerprint) # sign the identity string to prove we own both keys _, signature_b64 = keys.sign(identity) brain = {} try: with open('/root/brain.json') as fp: brain = json.load(fp) except: pass api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll' enrollment = { 'identity': identity, 'public_key': keys.pub_key_pem_b64, 'signature': signature_b64, 'data': { 'duration': last_session.duration, 'epochs': last_session.epochs, 'train_epochs': last_session.train_epochs, 'avg_reward': last_session.avg_reward, 'min_reward': last_session.min_reward, 'max_reward': last_session.max_reward, 'deauthed': last_session.deauthed, 'associated': last_session.associated, 'handshakes': last_session.handshakes, 'peers': last_session.peers, 'uname': subprocess.getoutput("uname -a"), 'brain': brain, 'version': pwnagotchi.version } } r = requests.post(api_address, json=enrollment) if r.status_code != 200: raise Exception("(status %d) %s" % (r.status_code, r.json())) AUTH.update(data=r.json()) logging.info("grid: done") return AUTH.data["token"]
def inbox_peers(self): peers = {} error = None try: peers = grid.memory() except Exception as e: logging.exception('error while reading pwngrid peers') error = str(e) return render_template('peers.html', name=pwnagotchi.name(), peers=peers, error=error)
def inbox_profile(self): data = {} error = None try: data = grid.get_advertisement_data() except Exception as e: logging.exception('error while reading pwngrid data') error = str(e) return render_template('profile.html', name=pwnagotchi.name(), fingerprint=self._agent.fingerprint(), data=json.dumps(data, indent=2), error=error)
def inbox(self): page = request.args.get("p", default=1, type=int) inbox = {"pages": 1, "records": 0, "messages": []} error = None try: inbox = grid.inbox(page, with_pager=True) except Exception as e: logging.exception('error while reading pwnmail inbox') error = str(e) return render_template('inbox.html', name=pwnagotchi.name(), page=page, error=error, inbox=inbox)
def exec_update(self, ui): try: Beacons._busy = True #TODO parse and send peers in another beacon frame packedInfo = self.pack_info(self.get_unsafe_unsync(ui, 'channel'), self.get_unsafe_unsync(ui, 'aps'), self.get_unsafe_unsync(ui, 'shakes'), pwnagotchi.uptime(), self.get_unsafe_unsync(ui, 'face'), self.get_unsafe_unsync(ui, 'mode'), pwnagotchi.name()) self.broadcast_info(packedInfo, self._packet_type['report']) except Exception as e: logging.warning(" *beacons* -> exec_update exception: ") logging.warning(" *beacons* -> " + str(type(e))) logging.warning(" *beacons* -> " + str(e)) Beacons._busy = False
def __init__(self, config, view, keypair): self._config = config self._view = view self._keypair = keypair self._advertisement = { 'name': pwnagotchi.name(), 'version': pwnagotchi.version, 'identity': self._keypair.fingerprint, 'face': faces.FRIEND, 'pwnd_run': 0, 'pwnd_tot': 0, 'uptime': 0, 'epoch': 0, 'policy': self._config['personality'] } self._peers = {} self._closest_peer = None
def _adv_worker(self): # this will take some time due to scapy being slow to be imported ... from pwnagotchi.mesh.advertise import Advertiser self._advertiser = Advertiser(self._config['main']['iface'], pwnagotchi.name(), pwnagotchi.version, self._keypair.fingerprint, period=0.3, data=self._config['personality']) self._advertiser.on_peer(self._on_new_unit, self._on_lost_unit) if self._config['personality']['advertise']: self._advertiser.start() self._view.on_state_change('face', self._advertiser.on_face_change) else: logging.warning("advertising is disabled")
def show_message(self, id): message = {} error = None try: message = grid.inbox_message(id) if message['data']: message['data'] = base64.b64decode( message['data']).decode("utf-8") except Exception as e: logging.exception('error while reading pwnmail message %d' % int(id)) error = str(e) return render_template('message.html', name=pwnagotchi.name(), error=error, message=message)
def on_webhook(self, path, request): if not self.ready: return "Plugin not ready" if path == "/" or not path: handshakes = glob.glob(os.path.join( self.config['bettercap']['handshakes'], "*.pcap")) handshakes = [os.path.basename(path)[:-5] for path in handshakes] return render_template_string(TEMPLATE, title="Handshakes | " + pwnagotchi.name(), handshakes=handshakes) else: dir = self.config['bettercap']['handshakes'] try: logging.info(f"[HandshakesDL] serving {dir}/{path}.pcap") return send_from_directory(directory=dir, filename=path + '.pcap', as_attachment=True) except FileNotFoundError: abort(404)
def do_GET(self): if self._buffer is None: self.send_response(404) elif self.path == '/': self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self._w(bytes(self._index % (pwnagotchi.name(), 1000), "utf8")) elif self.path.startswith('/ui'): with self._lock: self.send_response(200) self.send_header('Content-type', 'image/png') self.send_header('Content-length', '%d' % len(self._buffer)) self.end_headers() self._w(self._buffer) else: self.send_response(404)
def _return_json(self): if self.DISPLAY is None: return jsonify({"initialised": "false"}) # All these fall under the local API # https://pwnagotchi.ai/api/local/ # Typically on http://127.0.0.1:8666 # BUT the local API can trigger calls to the wider grid # so bear in mind that grid calls could fail # TODO: Break this up into function calls! Keep it SOLID. total_messages = "-" unread_messages = "-" peers_response = None try: response = requests.get('http://0.0.0.0:8666/api/v1/mesh/peers') peers_response = response.json() except HTTPError as http_err: logging.error(f'HTTP error occurred: {http_err}') except Exception as err: logging.error(f'Other error occurred: {err}') peers = [] for peer in peers_response: peers.append({ "fingerprint": peer['advertisement']['identity'], "name": peer['advertisement']['name'], "face": peer['advertisement']['face'], "pwnd_run": peer['advertisement']['pwnd_run'], "pwnd_tot": peer['advertisement']['pwnd_tot'] }) mesh_data_response = None try: response = requests.get('http://0.0.0.0:8666/api/v1/mesh/data') mesh_data_response = response.json() except HTTPError as http_err: logging.error(f'HTTP error occurred: {http_err}') except Exception as err: logging.error(f'Other error occurred: {err}') # Get mail data (if connected to internet) try: if grid.is_connected: messages = grid.inbox() total_messages = len(messages) unread_messages = len([m for m in messages if m['seen_at'] is None]) except Exception as e: logging.exception('error while reading state-api: %s' % str(e)) # TODO: Need a better way of getting this rather than referencing the display handshakes_display = self.DISPLAY.get('shakes').split(" ", 2) # In general, any underlying state within the state machine should be used. # The display is fluid and unreliable. pwnd_run = handshakes_display[0] pwnd_tot = utils.total_unique_handshakes(self.AGENT.config()['bettercap']['handshakes']) pwnd_last = None if len(handshakes_display) > 2: pwnd_last = handshakes_display[2][1:-1] result = { "fingerprint": self.AGENT.fingerprint(), "epoch": "-" if mesh_data_response is None else mesh_data_response["epoch"], "status": self.DISPLAY.get('status'), "channel_text": self.DISPLAY.get('channel'), "aps_text": self.DISPLAY.get('aps'), "apt_tot": self.AGENT.get_total_aps(), "aps_on_channel": self.AGENT.get_aps_on_channel(), "channel": self.AGENT.get_current_channel(), "uptime": self.DISPLAY.get('uptime'), "mode": self.DISPLAY.get('mode'), "name": pwnagotchi.name(), "face": self.DISPLAY.get('face'), "num_peers": len(peers), "peers": peers, "total_messages": total_messages, "unread_messages": unread_messages, "friend_face_text": self.DISPLAY.get('friend_face'), "friend_name_text": self.DISPLAY.get('friend_name'), "pwnd_last": pwnd_last, "pwnd_run": pwnd_run, "pwnd_tot": pwnd_tot, "version": pwnagotchi.__version__, "memory": pwnagotchi.mem_usage(), # Scale 0-1 "cpu": pwnagotchi.cpu_load(), # Scale 0-1 "temperature": pwnagotchi.temperature() # Degrees C } # TODO See if there is any way of getting a list of plugins and their associated UI components # so we can incorporate it into the feedback. return jsonify(result)
def _shutdown(self): self._html(SHUTDOWN % pwnagotchi.name()) pwnagotchi.shutdown()
def _index(self): self._html(INDEX % (pwnagotchi.name(), 1000))
def index(self): return render_template('index.html', title=pwnagotchi.name(), other_mode='AUTO' if self._agent.mode == 'manual' else 'MANU', dark_theme=self._config['dark'])
def index(self): return render_template('index.html', title=pwnagotchi.name(), other_mode='AUTO' if self._agent.mode == 'manual' else 'MANU', fingerprint=self._agent.fingerprint())
args = parser.parse_args() if args.do_clear: print("clearing the display ...") from pwnagotchi.ui.waveshare import EPD epd = EPD() epd.init(epd.FULL_UPDATE) epd.Clear(0xff) quit() with open(args.config, 'rt') as fp: config = yaml.safe_load(fp) display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()}) agent = Agent(view=display, config=config) core.log("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version)) # for key, value in config['personality'].items(): # core.log(" %s: %s" % (key, value)) if args.do_manual: core.log("entering manual mode ...") log = SessionParser(config['main']['log']) core.log("the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % ( log.duration_human, log.epochs, log.train_epochs,
epd.init(epd.lut_full_update) epd.Clear(0xFF) elif cleardisplay in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2'): print("waveshare v2 display") from pwnagotchi.ui.waveshare.v2.waveshare import EPD epd = EPD() epd.init(epd.FULL_UPDATE) epd.Clear(0xff) else: print("unknown display type %s" % cleardisplay) quit() with open(args.config, 'rt') as fp: config = yaml.safe_load(fp) display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()}) agent = Agent(view=display, config=config) core.log("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version)) # for key, value in config['personality'].items(): # core.log(" %s: %s" % (key, value)) if args.do_manual: core.log("entering manual mode ...") log = SessionParser(config['main']['log']) core.log( "the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (log.duration_human, log.epochs, log.train_epochs, log.avg_reward,