Pwnagotchi has a simple plugin system that you can use to customize your unit and its behavior. You can place your plugins anywhere as Python files, and then edit the config.toml
file (main.custom_plugins
value) to point to their containing folder. Check the plugins folder in the main Pwnagotchi repo for a list of plugins included by default as well as all the callbacks that you can define for your own customizations.
These plugins are maintained in the main pwnagotchi
repository. New versions will be automatically
available when you use the auto-update
feature.
Plugin Script | Description |
---|---|
auto-update.py |
apt update && apt upgrade when internet is available. |
bt-tether.py |
Makes the display reachable over bluetooth. |
gpio_buttons.py |
GPIO Button support plugin. |
gps.py |
Saves GPS coordinates whenever a handshake is captured. |
grid.py |
Signals the unit’s cryptographic identity and (optionally) a list of pwned networks to PwnGRID at api.pwnagotchi.ai. |
led.py |
This plugin blinks the PWR led with different patterns depending on the event. |
logtail |
This plugins enables you to look at the logfile via your browser. |
memtemp.py |
Adds a memory and temperature indicator. |
net-pos.py |
Saves WiFi position whenever a handshake is captured and retrieves the geolocation when internet is next available. |
onlinehashcrack.py |
Automatically uploads handshakes to onlinehashcrack.com. |
paw-gps.py |
Saves GPS coordinates whenever an handshake is captured. The GPS data is retrieved from PAW on android. |
session-stats.py |
Shows the current session stats in nice little graphs. |
switcher.py |
You can switch to some non-pwnagotchi activity for some amount of time. |
ups_lite.py |
A plugin that will add a voltage indicator for the UPS Lite v1.1. |
webcfg.py |
With this plugin you can change the configuration via the browser. |
webgpsmap.py |
Plots the captures handshakes on a map. |
wigle.py |
Automatically uploads collected WiFi handshakes to wigle.net. |
wpa-sec.py |
Automatically uploads handshakes to wpa-sec.stanev.org. |
These are user contributed plugins for pwnagotchi, some of them have not been completely tested by the dev team, use them at your own risk.
Plugin Script | Description |
---|---|
aircrackonly.py |
Confirms pcap contains handshake/PMKID or delete it. |
auto_backup.py |
Backs up files when internet is available. |
buttonshim.py |
Pimoroni Button Shim GPIO Button and RGB LED support plugin based on the pimoroni-buttonshim-lib and the pwnagotchi-gpio-buttons-plugin. |
christmas.py |
Christmas Countdown timer for pwnagotchi. |
clock.py |
Clock/Calendar for pwnagotchi. |
gpio_shutdown.py |
GPIO Shutdown plugin. |
handshakes-dl.py |
Download handshake captures from web-ui. |
hashie.py |
Attempt to automatically convert pcaps to a crackable format. |
mostodon.py |
Periodically post status updates. Based on twitter plugin by evilsocket. |
quickdic.py |
Runs a quick dictionary scan against captured handshakes. |
screen_refresh.py |
Refreshes the e-ink display after X amount of updates. |
telegram.py |
Periodically sent messages to Telegram about the recent activity of pwnagotchi. |
twitter.py |
Creates tweets about the recent activity of Pwnagotchi. |
If you navigate to the plugins-section of your pwnagotchi-web-ui, you will see all available plugins listed in small little boxes like the following:
If the name is underlined, you can click on them and will be navigated to their web representation. If you toggle the switch, the plugin will be enabled/disabled permanently (it will be saved to the config).
Pwnagotchi also has a small plugins subcommand, which can be used to manage the plugins.
usage: pwnagotchi plugins [-h]
{search,list,update,upgrade,enable,disable,install,uninstall,edit}
...
positional arguments:
{search,list,update,upgrade,enable,disable,install,uninstall,edit}
search Search for pwnagotchi plugins
list List available pwnagotchi plugins
update Updates the database
upgrade Upgrades plugins
enable Enables a plugin
disable Disables a plugin
install Installs a plugin
uninstall Uninstalls a plugin
edit Edit the options
optional arguments:
-h, --help show this help message and exit
Example: If you want to update all your plugins to their latest version, you have to do this:
pwnagotchi plugins update
pwnagotchi plugins upgrade
Configuration files you changed, wont we overwritten but suffixed with .bak.
If you want to develop your own plugin, you have the following callbacks availaible:
Callback | Description |
---|---|
on_ai_best_reward |
Called when the AI got the best reward so far. |
on_ai_policy |
Called when the AI finds a new set of parameters. |
on_ai_ready |
Called when the AI finished loading. |
on_ai_training_end |
Called when the AI has done training. |
on_ai_training_start |
Called when the AI starts training for a given number of epochs. |
on_ai_training_step |
Called after the AI completed a training epoch. |
on_ai_worst_reward |
Called when the AI got the worst reward so far. |
on_association |
Called when the agent is sending an association frame. |
on_bored |
Called when the status is set to bored. |
on_channel_hop |
callend when the agent is tuning on a specific channel. |
on_config_changed |
This will be triggered if the config has changed (also right after on_loaded). |
on_deauthentication |
Called when the agent is deauthenticating a client station from an AP. |
on_display_setup |
Called when the hardware display setup is done, display is an hardware specific object. |
on_epoch |
Called when an epoch is over (where an epoch is a single loop of the main algorithm). |
on_excited |
Called when the status is set to excited. |
on_free_channel |
Called when a non overlapping wifi channel is found to be free. |
on_handshake |
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. |
on_internet_available |
This will be triggered every few seconds during the time pwnagotchi has internet. |
on_loaded |
The plugin got loaded and is enabled. |
on_lonely |
Called when the status is set to lonely. |
on_peer_detected |
Called when a new peer is detected. |
on_peer_lost |
Called when a known peer is lost. |
on_ready |
Called when everything is ready and the main loop is about to start. |
on_rebooting |
Called when the agent is rebooting the board. |
on_sad |
Called when the status is set to sad. |
on_sleep |
Called when the agent is sleeping for t seconds. |
on_ui_setup |
Called to setup the ui elements. |
on_ui_update |
Called when the ui is updated. |
on_unfiltered_ap_list |
Called when the agent refreshed an unfiltered access point list this list contains all access points that were detected BEFORE filtering. |
on_unload |
This will be triggered if the plugin gets unloaded (e.g. the user toggled the enable/disable switch). You should remove unneeded ui-elements here. |
on_wait |
Called when the agent is waiting for t seconds. |
on_webhook |
You can provide some web-functionality here. Will be triggered if the user opens /plugins/<pluginname> . |
on_wifi_update |
Called when the agent refreshed its access points list. |
To illustrate how easy it is to add additional functionality via the plugin system, here is the code for the GPS plugin (gps.py
):
import logging
import json
import os
import pwnagotchi.plugins as plugins
class GPS(plugins.Plugin):
__author__ = '[email protected]'
__version__ = '1.0.0'
__license__ = 'GPL3'
__description__ = 'Save GPS coordinates whenever an handshake is captured.'
def __init__(self):
self.running = False
def on_loaded(self):
logging.info("gps plugin loaded for %s" % self.options['device'])
def on_ready(self, agent):
if os.path.exists(self.options['device']):
logging.info("enabling gps bettercap's module for %s" % self.options['device'])
try:
agent.run('gps off')
except:
pass
agent.run('set gps.device %s' % self.options['device'])
agent.run('set gps.speed %d' % self.options['speed'])
agent.run('gps on')
self.running = True
else:
logging.warning("no GPS detected")
def on_handshake(self, agent, filename, access_point, client_station):
if self.running:
info = agent.session()
gps = info['gps']
gps_filename = filename.replace('.pcap', '.gps.json')
logging.info("saving GPS to %s (%s)" % (gps_filename, gps))
with open(gps_filename, 'w+t') as fp:
json.dump(gps, fp)