mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
337
bin/pwnagotchi
337
bin/pwnagotchi
@ -1,337 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
import logging
|
|
||||||
import argparse
|
|
||||||
import time
|
|
||||||
import signal
|
|
||||||
import sys
|
|
||||||
import toml
|
|
||||||
import requests
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
import pwnagotchi
|
|
||||||
from pwnagotchi import utils
|
|
||||||
from pwnagotchi.google import cmd as google_cmd
|
|
||||||
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, parse_version as version_to_tuple
|
|
||||||
|
|
||||||
|
|
||||||
def pwnagotchi_cli():
|
|
||||||
def do_clear(display):
|
|
||||||
logging.info("clearing the display ...")
|
|
||||||
display.clear()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def do_manual_mode(agent):
|
|
||||||
logging.info("entering manual mode ...")
|
|
||||||
|
|
||||||
agent.mode = 'manual'
|
|
||||||
agent.last_session.parse(agent.view(), args.skip_session)
|
|
||||||
if not args.skip_session:
|
|
||||||
logging.info(
|
|
||||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
|
||||||
agent.last_session.duration_human,
|
|
||||||
agent.last_session.epochs,
|
|
||||||
agent.last_session.train_epochs,
|
|
||||||
agent.last_session.avg_reward,
|
|
||||||
agent.last_session.min_reward,
|
|
||||||
agent.last_session.max_reward))
|
|
||||||
|
|
||||||
while True:
|
|
||||||
display.on_manual_mode(agent.last_session)
|
|
||||||
time.sleep(5)
|
|
||||||
if grid.is_connected():
|
|
||||||
plugins.on('internet_available', agent)
|
|
||||||
|
|
||||||
def do_auto_mode(agent):
|
|
||||||
logging.info("entering auto mode ...")
|
|
||||||
|
|
||||||
agent.mode = 'auto'
|
|
||||||
agent.start()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
# recon on all channels
|
|
||||||
agent.recon()
|
|
||||||
# get nearby access points grouped by channel
|
|
||||||
channels = agent.get_access_points_by_channel()
|
|
||||||
# for each channel
|
|
||||||
for ch, aps in channels:
|
|
||||||
time.sleep(1)
|
|
||||||
agent.set_channel(ch)
|
|
||||||
|
|
||||||
if not agent.is_stale() and agent.any_activity():
|
|
||||||
logging.info("%d access points on channel %d" % (len(aps), ch))
|
|
||||||
|
|
||||||
# for each ap on this channel
|
|
||||||
for ap in aps:
|
|
||||||
# send an association frame in order to get for a PMKID
|
|
||||||
agent.associate(ap)
|
|
||||||
# deauth all client stations in order to get a full handshake
|
|
||||||
for sta in ap['clients']:
|
|
||||||
agent.deauth(ap, sta)
|
|
||||||
time.sleep(1) # delay to not trigger nexmon firmware bugs
|
|
||||||
|
|
||||||
# An interesting effect of this:
|
|
||||||
#
|
|
||||||
# From Pwnagotchi's perspective, the more new access points
|
|
||||||
# and / or client stations nearby, the longer one epoch of
|
|
||||||
# its relative time will take ... basically, in Pwnagotchi's universe,
|
|
||||||
# Wi-Fi electromagnetic fields affect time like gravitational fields
|
|
||||||
# affect ours ... neat ^_^
|
|
||||||
agent.next_epoch()
|
|
||||||
|
|
||||||
if grid.is_connected():
|
|
||||||
plugins.on('internet_available', agent)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
if str(e).find("wifi.interface not set") > 0:
|
|
||||||
logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
|
|
||||||
logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
|
|
||||||
time.sleep(60)
|
|
||||||
agent.next_epoch()
|
|
||||||
else:
|
|
||||||
logging.exception("main loop exception (%s)", e)
|
|
||||||
|
|
||||||
def add_parsers(parser):
|
|
||||||
"""
|
|
||||||
Adds the plugins and google subcommands
|
|
||||||
"""
|
|
||||||
subparsers = parser.add_subparsers()
|
|
||||||
|
|
||||||
# Add parsers from plugins_cmd
|
|
||||||
plugins_cmd.add_parsers(subparsers)
|
|
||||||
|
|
||||||
# Add parsers from google_cmd
|
|
||||||
google_cmd.add_parsers(subparsers)
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog="pwnagotchi")
|
|
||||||
# pwnagotchi --help
|
|
||||||
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml',
|
|
||||||
help='Main configuration file.')
|
|
||||||
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml',
|
|
||||||
help='If this file exists, configuration will be merged and this will override default values.')
|
|
||||||
|
|
||||||
parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.")
|
|
||||||
parser.add_argument('--skip-session', dest="skip_session", action="store_true", default=False,
|
|
||||||
help="Skip last session parsing in manual mode.")
|
|
||||||
|
|
||||||
parser.add_argument('--clear', dest="do_clear", action="store_true", default=False,
|
|
||||||
help="Clear the ePaper display and exit.")
|
|
||||||
|
|
||||||
parser.add_argument('--debug', dest="debug", action="store_true", default=False,
|
|
||||||
help="Enable debug logs.")
|
|
||||||
|
|
||||||
parser.add_argument('--version', dest="version", action="store_true", default=False,
|
|
||||||
help="Print the version.")
|
|
||||||
|
|
||||||
parser.add_argument('--print-config', dest="print_config", action="store_true", default=False,
|
|
||||||
help="Print the configuration.")
|
|
||||||
|
|
||||||
# Jayofelony added these
|
|
||||||
parser.add_argument('--wizard', dest="wizard", action="store_true", default=False,
|
|
||||||
help="Interactive installation of your personal configuration.")
|
|
||||||
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
|
|
||||||
help="Check for updates on Pwnagotchi. And tells current version.")
|
|
||||||
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
|
|
||||||
help="How to donate to this project.")
|
|
||||||
|
|
||||||
# pwnagotchi plugins --help
|
|
||||||
add_parsers(parser)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if plugins_cmd.used_plugin_cmd(args):
|
|
||||||
config = utils.load_config(args)
|
|
||||||
log.setup_logging(args, config)
|
|
||||||
rc = plugins_cmd.handle_cmd(args, config)
|
|
||||||
sys.exit(rc)
|
|
||||||
if google_cmd.used_google_cmd(args):
|
|
||||||
config = utils.load_config(args)
|
|
||||||
log.setup_logging(args, config)
|
|
||||||
rc = google_cmd.handle_cmd(args)
|
|
||||||
sys.exit(rc)
|
|
||||||
|
|
||||||
if args.version:
|
|
||||||
print(pwnagotchi.__version__)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if args.wizard:
|
|
||||||
def is_valid_hostname(hostname):
|
|
||||||
if len(hostname) > 255:
|
|
||||||
return False
|
|
||||||
if hostname[-1] == ".":
|
|
||||||
hostname = hostname[:-1] # strip exactly one dot from the right, if present
|
|
||||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
|
||||||
return all(allowed.match(x) for x in hostname.split("."))
|
|
||||||
|
|
||||||
pwn_restore = input("Do you want to restore the previous configuration?\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_restore in ('y', 'yes'):
|
|
||||||
os.system("cp -f /etc/pwnagotchi/config.toml.bak /etc/pwnagotchi/config.toml")
|
|
||||||
print("Your previous configuration is restored, and I will restart in 5 seconds.")
|
|
||||||
time.sleep(5)
|
|
||||||
os.system("service pwnagotchi restart")
|
|
||||||
else:
|
|
||||||
pwn_check = input("This will create a new configuration file and overwrite your current backup, are you sure?\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_check.lower() in ('y', 'yes'):
|
|
||||||
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
|
|
||||||
with open("/etc/pwnagotchi/config.toml", "a+") as f:
|
|
||||||
f.write("# Do not edit this file if you do not know what you are doing!!!\n\n")
|
|
||||||
# Set pwnagotchi name
|
|
||||||
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
|
|
||||||
"My name is Jayofelony, how may I call you?\n\n")
|
|
||||||
pwn_name = input("Pwnagotchi name (no spaces): ")
|
|
||||||
if pwn_name == "":
|
|
||||||
pwn_name = "Pwnagotchi"
|
|
||||||
print("I shall go by Pwnagotchi from now on!")
|
|
||||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
|
||||||
f.write(pwn_name)
|
|
||||||
else:
|
|
||||||
if is_valid_hostname(pwn_name):
|
|
||||||
print(f"I shall go by {pwn_name} from now on!")
|
|
||||||
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
|
||||||
f.write(pwn_name)
|
|
||||||
else:
|
|
||||||
print("You have chosen an invalid name. Please start over.")
|
|
||||||
exit()
|
|
||||||
pwn_whitelist = input("How many networks do you want to whitelist? "
|
|
||||||
"We will also ask a MAC for each network?\n"
|
|
||||||
"Each SSID and BSSID count as 1 network. \n\n"
|
|
||||||
"Be sure to use digits as your answer.\n\n"
|
|
||||||
"Amount of networks: ")
|
|
||||||
if int(pwn_whitelist) > 0:
|
|
||||||
f.write("main.whitelist = [\n")
|
|
||||||
for x in range(int(pwn_whitelist)):
|
|
||||||
ssid = input("SSID (Name): ")
|
|
||||||
bssid = input("BSSID (MAC): ")
|
|
||||||
f.write(f"\t\"{ssid}\",\n")
|
|
||||||
f.write(f"\t\"{bssid}\",\n")
|
|
||||||
f.write("]\n")
|
|
||||||
# set bluetooth tether
|
|
||||||
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
|
|
||||||
"[Y/N] ")
|
|
||||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
|
||||||
f.write("main.plugins.bt-tether.enabled = true\n\n")
|
|
||||||
pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
|
|
||||||
"Device: ")
|
|
||||||
if pwn_bluetooth_device.lower() == "android":
|
|
||||||
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
|
|
||||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
|
||||||
"MAC: ")
|
|
||||||
if pwn_bluetooth_mac != "":
|
|
||||||
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
|
||||||
elif pwn_bluetooth_device.lower() == "ios":
|
|
||||||
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
|
|
||||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
|
||||||
"MAC: ")
|
|
||||||
if pwn_bluetooth_mac != "":
|
|
||||||
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
|
||||||
# set up display settings
|
|
||||||
pwn_display_enabled = input("Do you want to enable a display?\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_display_enabled.lower() in ('y', 'yes'):
|
|
||||||
f.write("ui.display.enabled = true\n")
|
|
||||||
pwn_display_type = input("What display do you use?\n\n"
|
|
||||||
"Be sure to check for the correct display type @ \n"
|
|
||||||
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L501\n\n"
|
|
||||||
"Display type: ")
|
|
||||||
if pwn_display_type != "":
|
|
||||||
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
|
|
||||||
pwn_display_invert = input("Do you want to invert the display colors?\n"
|
|
||||||
"N = Black background\n"
|
|
||||||
"Y = White background\n\n"
|
|
||||||
"[Y/N]: ")
|
|
||||||
if pwn_display_invert.lower() in ('y', 'yes'):
|
|
||||||
f.write("ui.invert = true\n")
|
|
||||||
f.close()
|
|
||||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
|
||||||
if pwn_bluetooth_device.lower == "android":
|
|
||||||
print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
|
|
||||||
"Your configuration is done, and I will restart in 5 seconds.")
|
|
||||||
elif pwn_bluetooth_device.lower == "ios":
|
|
||||||
print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
|
|
||||||
"Your configuration is done, and I will restart in 5 seconds.")
|
|
||||||
else:
|
|
||||||
print("Your configuration is done, and I will restart in 5 seconds.")
|
|
||||||
time.sleep(5)
|
|
||||||
os.system("service pwnagotchi restart")
|
|
||||||
else:
|
|
||||||
print("Ok, doing nothing.")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if args.donate:
|
|
||||||
print("Donations can be made @ \n "
|
|
||||||
"https://github.com/sponsors/jayofelony \n\n"
|
|
||||||
"But only if you really want to!")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
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 from v%s to v%s?\n[Y/N] " % (pwnagotchi.__version__, latest_ver))
|
|
||||||
# input validation
|
|
||||||
if user_input.lower() in ('y', 'yes'):
|
|
||||||
if os.path.exists('/root/.auto-update'):
|
|
||||||
os.system("rm /root/.auto-update && systemctl restart pwnagotchi")
|
|
||||||
else:
|
|
||||||
logging.error("You should make sure auto-update is enabled!")
|
|
||||||
print("Okay, give me a couple minutes. Just watch pwnlog while you wait.")
|
|
||||||
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, v%s." % pwnagotchi.__version__)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
config = utils.load_config(args)
|
|
||||||
|
|
||||||
if args.print_config:
|
|
||||||
print(toml.dumps(config, encoder=DottedTomlEncoder()))
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
from pwnagotchi.identity import KeyPair
|
|
||||||
from pwnagotchi.agent import Agent
|
|
||||||
from pwnagotchi.ui import fonts
|
|
||||||
from pwnagotchi.ui.display import Display
|
|
||||||
from pwnagotchi import grid
|
|
||||||
from pwnagotchi import plugins
|
|
||||||
|
|
||||||
pwnagotchi.config = config
|
|
||||||
fs.setup_mounts(config)
|
|
||||||
log.setup_logging(args, config)
|
|
||||||
fonts.init(config)
|
|
||||||
|
|
||||||
pwnagotchi.set_name(config['main']['name'])
|
|
||||||
|
|
||||||
plugins.load(config)
|
|
||||||
|
|
||||||
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
|
||||||
|
|
||||||
if args.do_clear:
|
|
||||||
do_clear(display)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
agent = Agent(view=display, config=config, keypair=KeyPair(view=display))
|
|
||||||
|
|
||||||
def usr1_handler(*unused):
|
|
||||||
logging.info('Received USR1 signal. Restart process ...')
|
|
||||||
agent._restart("MANU" if args.do_manual else "AUTO")
|
|
||||||
|
|
||||||
signal.signal(signal.SIGUSR1, usr1_handler)
|
|
||||||
|
|
||||||
if args.do_manual:
|
|
||||||
do_manual_mode(agent)
|
|
||||||
else:
|
|
||||||
do_auto_mode(agent)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pwnagotchi_cli()
|
|
@ -2,13 +2,340 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import argparse
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import toml
|
||||||
|
import requests
|
||||||
|
import utils
|
||||||
|
from pwnagotchi.google import cmd as google_cmd
|
||||||
|
from pwnagotchi.plugins import cmd as plugins_cmd
|
||||||
|
from pwnagotchi import log
|
||||||
|
from pwnagotchi import fs
|
||||||
|
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
||||||
from pwnagotchi._version import __version__
|
from pwnagotchi._version import __version__
|
||||||
|
|
||||||
_name = None
|
_name = None
|
||||||
config = None
|
config = None
|
||||||
_cpu_stats = {}
|
_cpu_stats = {}
|
||||||
|
|
||||||
|
def pwnagotchi_cli():
|
||||||
|
def do_clear(display):
|
||||||
|
logging.info("clearing the display ...")
|
||||||
|
display.clear()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def do_manual_mode(agent):
|
||||||
|
logging.info("entering manual mode ...")
|
||||||
|
|
||||||
|
agent.mode = 'manual'
|
||||||
|
agent.last_session.parse(agent.view(), args.skip_session)
|
||||||
|
if not args.skip_session:
|
||||||
|
logging.info(
|
||||||
|
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||||
|
agent.last_session.duration_human,
|
||||||
|
agent.last_session.epochs,
|
||||||
|
agent.last_session.train_epochs,
|
||||||
|
agent.last_session.avg_reward,
|
||||||
|
agent.last_session.min_reward,
|
||||||
|
agent.last_session.max_reward))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
display.on_manual_mode(agent.last_session)
|
||||||
|
time.sleep(5)
|
||||||
|
if grid.is_connected():
|
||||||
|
plugins.on('internet_available', agent)
|
||||||
|
|
||||||
|
def do_auto_mode(agent):
|
||||||
|
logging.info("entering auto mode ...")
|
||||||
|
|
||||||
|
agent.mode = 'auto'
|
||||||
|
agent.start()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# recon on all channels
|
||||||
|
agent.recon()
|
||||||
|
# get nearby access points grouped by channel
|
||||||
|
channels = agent.get_access_points_by_channel()
|
||||||
|
# for each channel
|
||||||
|
for ch, aps in channels:
|
||||||
|
time.sleep(1)
|
||||||
|
agent.set_channel(ch)
|
||||||
|
|
||||||
|
if not agent.is_stale() and agent.any_activity():
|
||||||
|
logging.info("%d access points on channel %d" % (len(aps), ch))
|
||||||
|
|
||||||
|
# for each ap on this channel
|
||||||
|
for ap in aps:
|
||||||
|
# send an association frame in order to get for a PMKID
|
||||||
|
agent.associate(ap)
|
||||||
|
# deauth all client stations in order to get a full handshake
|
||||||
|
for sta in ap['clients']:
|
||||||
|
agent.deauth(ap, sta)
|
||||||
|
time.sleep(1) # delay to not trigger nexmon firmware bugs
|
||||||
|
|
||||||
|
# An interesting effect of this:
|
||||||
|
#
|
||||||
|
# From Pwnagotchi's perspective, the more new access points
|
||||||
|
# and / or client stations nearby, the longer one epoch of
|
||||||
|
# its relative time will take ... basically, in Pwnagotchi's universe,
|
||||||
|
# Wi-Fi electromagnetic fields affect time like gravitational fields
|
||||||
|
# affect ours ... neat ^_^
|
||||||
|
agent.next_epoch()
|
||||||
|
|
||||||
|
if grid.is_connected():
|
||||||
|
plugins.on('internet_available', agent)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if str(e).find("wifi.interface not set") > 0:
|
||||||
|
logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
|
||||||
|
logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
|
||||||
|
time.sleep(60)
|
||||||
|
agent.next_epoch()
|
||||||
|
else:
|
||||||
|
logging.exception("main loop exception (%s)", e)
|
||||||
|
|
||||||
|
def add_parsers(parser):
|
||||||
|
"""
|
||||||
|
Adds the plugins and google subcommands
|
||||||
|
"""
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
|
||||||
|
# Add parsers from plugins_cmd
|
||||||
|
plugins_cmd.add_parsers(subparsers)
|
||||||
|
|
||||||
|
# Add parsers from google_cmd
|
||||||
|
google_cmd.add_parsers(subparsers)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(prog="pwnagotchi")
|
||||||
|
# pwnagotchi --help
|
||||||
|
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml',
|
||||||
|
help='Main configuration file.')
|
||||||
|
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml',
|
||||||
|
help='If this file exists, configuration will be merged and this will override default values.')
|
||||||
|
|
||||||
|
parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.")
|
||||||
|
parser.add_argument('--skip-session', dest="skip_session", action="store_true", default=False,
|
||||||
|
help="Skip last session parsing in manual mode.")
|
||||||
|
|
||||||
|
parser.add_argument('--clear', dest="do_clear", action="store_true", default=False,
|
||||||
|
help="Clear the ePaper display and exit.")
|
||||||
|
|
||||||
|
parser.add_argument('--debug', dest="debug", action="store_true", default=False,
|
||||||
|
help="Enable debug logs.")
|
||||||
|
|
||||||
|
parser.add_argument('--version', dest="version", action="store_true", default=False,
|
||||||
|
help="Print the version.")
|
||||||
|
|
||||||
|
parser.add_argument('--print-config', dest="print_config", action="store_true", default=False,
|
||||||
|
help="Print the configuration.")
|
||||||
|
|
||||||
|
# Jayofelony added these
|
||||||
|
parser.add_argument('--wizard', dest="wizard", action="store_true", default=False,
|
||||||
|
help="Interactive installation of your personal configuration.")
|
||||||
|
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
|
||||||
|
help="Check for updates on Pwnagotchi. And tells current version.")
|
||||||
|
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
|
||||||
|
help="How to donate to this project.")
|
||||||
|
|
||||||
|
# pwnagotchi plugins --help
|
||||||
|
add_parsers(parser)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if plugins_cmd.used_plugin_cmd(args):
|
||||||
|
config = utils.load_config(args)
|
||||||
|
log.setup_logging(args, config)
|
||||||
|
rc = plugins_cmd.handle_cmd(args, config)
|
||||||
|
sys.exit(rc)
|
||||||
|
if google_cmd.used_google_cmd(args):
|
||||||
|
config = utils.load_config(args)
|
||||||
|
log.setup_logging(args, config)
|
||||||
|
rc = google_cmd.handle_cmd(args)
|
||||||
|
sys.exit(rc)
|
||||||
|
|
||||||
|
if args.version:
|
||||||
|
print(__version__)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if args.wizard:
|
||||||
|
def is_valid_hostname(hostname):
|
||||||
|
if len(hostname) > 255:
|
||||||
|
return False
|
||||||
|
if hostname[-1] == ".":
|
||||||
|
hostname = hostname[:-1] # strip exactly one dot from the right, if present
|
||||||
|
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||||
|
return all(allowed.match(x) for x in hostname.split("."))
|
||||||
|
|
||||||
|
pwn_restore = input("Do you want to restore the previous configuration?\n\n"
|
||||||
|
"[Y/N]: ")
|
||||||
|
if pwn_restore in ('y', 'yes'):
|
||||||
|
os.system("cp -f /etc/pwnagotchi/config.toml.bak /etc/pwnagotchi/config.toml")
|
||||||
|
print("Your previous configuration is restored, and I will restart in 5 seconds.")
|
||||||
|
time.sleep(5)
|
||||||
|
os.system("service pwnagotchi restart")
|
||||||
|
else:
|
||||||
|
pwn_check = input("This will create a new configuration file and overwrite your current backup, are you sure?\n\n"
|
||||||
|
"[Y/N]: ")
|
||||||
|
if pwn_check.lower() in ('y', 'yes'):
|
||||||
|
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
|
||||||
|
with open("/etc/pwnagotchi/config.toml", "a+") as f:
|
||||||
|
f.write("# Do not edit this file if you do not know what you are doing!!!\n\n")
|
||||||
|
# Set pwnagotchi name
|
||||||
|
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
|
||||||
|
"My name is Jayofelony, how may I call you?\n\n")
|
||||||
|
pwn_name = input("Pwnagotchi name (no spaces): ")
|
||||||
|
if pwn_name == "":
|
||||||
|
pwn_name = "Pwnagotchi"
|
||||||
|
print("I shall go by Pwnagotchi from now on!")
|
||||||
|
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
||||||
|
f.write(pwn_name)
|
||||||
|
else:
|
||||||
|
if is_valid_hostname(pwn_name):
|
||||||
|
print(f"I shall go by {pwn_name} from now on!")
|
||||||
|
pwn_name = f"main.name = \"{pwn_name}\"\n"
|
||||||
|
f.write(pwn_name)
|
||||||
|
else:
|
||||||
|
print("You have chosen an invalid name. Please start over.")
|
||||||
|
exit()
|
||||||
|
pwn_whitelist = input("How many networks do you want to whitelist? "
|
||||||
|
"We will also ask a MAC for each network?\n"
|
||||||
|
"Each SSID and BSSID count as 1 network. \n\n"
|
||||||
|
"Be sure to use digits as your answer.\n\n"
|
||||||
|
"Amount of networks: ")
|
||||||
|
if int(pwn_whitelist) > 0:
|
||||||
|
f.write("main.whitelist = [\n")
|
||||||
|
for x in range(int(pwn_whitelist)):
|
||||||
|
ssid = input("SSID (Name): ")
|
||||||
|
bssid = input("BSSID (MAC): ")
|
||||||
|
f.write(f"\t\"{ssid}\",\n")
|
||||||
|
f.write(f"\t\"{bssid}\",\n")
|
||||||
|
f.write("]\n")
|
||||||
|
# set bluetooth tether
|
||||||
|
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
|
||||||
|
"[Y/N] ")
|
||||||
|
if pwn_bluetooth.lower() in ('y', 'yes'):
|
||||||
|
f.write("main.plugins.bt-tether.enabled = true\n\n")
|
||||||
|
pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
|
||||||
|
"Device: ")
|
||||||
|
if pwn_bluetooth_device.lower() == "android":
|
||||||
|
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
|
||||||
|
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||||
|
"MAC: ")
|
||||||
|
if pwn_bluetooth_mac != "":
|
||||||
|
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
||||||
|
elif pwn_bluetooth_device.lower() == "ios":
|
||||||
|
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
|
||||||
|
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||||
|
"MAC: ")
|
||||||
|
if pwn_bluetooth_mac != "":
|
||||||
|
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
||||||
|
# set up display settings
|
||||||
|
pwn_display_enabled = input("Do you want to enable a display?\n\n"
|
||||||
|
"[Y/N]: ")
|
||||||
|
if pwn_display_enabled.lower() in ('y', 'yes'):
|
||||||
|
f.write("ui.display.enabled = true\n")
|
||||||
|
pwn_display_type = input("What display do you use?\n\n"
|
||||||
|
"Be sure to check for the correct display type @ \n"
|
||||||
|
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L501\n\n"
|
||||||
|
"Display type: ")
|
||||||
|
if pwn_display_type != "":
|
||||||
|
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
|
||||||
|
pwn_display_invert = input("Do you want to invert the display colors?\n"
|
||||||
|
"N = Black background\n"
|
||||||
|
"Y = White background\n\n"
|
||||||
|
"[Y/N]: ")
|
||||||
|
if pwn_display_invert.lower() in ('y', 'yes'):
|
||||||
|
f.write("ui.invert = true\n")
|
||||||
|
f.close()
|
||||||
|
if pwn_bluetooth.lower() in ('y', 'yes'):
|
||||||
|
if pwn_bluetooth_device.lower == "android":
|
||||||
|
print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
|
||||||
|
"Your configuration is done, and I will restart in 5 seconds.")
|
||||||
|
elif pwn_bluetooth_device.lower == "ios":
|
||||||
|
print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
|
||||||
|
"Your configuration is done, and I will restart in 5 seconds.")
|
||||||
|
else:
|
||||||
|
print("Your configuration is done, and I will restart in 5 seconds.")
|
||||||
|
time.sleep(5)
|
||||||
|
os.system("service pwnagotchi restart")
|
||||||
|
else:
|
||||||
|
print("Ok, doing nothing.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if args.donate:
|
||||||
|
print("Donations can be made @ \n "
|
||||||
|
"https://github.com/sponsors/jayofelony \n\n"
|
||||||
|
"But only if you really want to!")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
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(__version__)
|
||||||
|
remote = version_to_tuple(latest_ver)
|
||||||
|
if remote > local:
|
||||||
|
user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] " % (__version__, latest_ver))
|
||||||
|
# input validation
|
||||||
|
if user_input.lower() in ('y', 'yes'):
|
||||||
|
if os.path.exists('/root/.auto-update'):
|
||||||
|
os.system("rm /root/.auto-update && systemctl restart pwnagotchi")
|
||||||
|
else:
|
||||||
|
logging.error("You should make sure auto-update is enabled!")
|
||||||
|
print("Okay, give me a couple minutes. Just watch pwnlog while you wait.")
|
||||||
|
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, v%s." % __version__)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
config = utils.load_config(args)
|
||||||
|
|
||||||
|
if args.print_config:
|
||||||
|
print(toml.dumps(config, encoder=DottedTomlEncoder()))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
from identity import KeyPair
|
||||||
|
from agent import Agent
|
||||||
|
from ui import fonts
|
||||||
|
from ui.display import Display
|
||||||
|
import grid
|
||||||
|
import plugins
|
||||||
|
|
||||||
|
_config = config
|
||||||
|
fs.setup_mounts(_config)
|
||||||
|
log.setup_logging(args, _config)
|
||||||
|
fonts.init(_config)
|
||||||
|
|
||||||
|
set_name(_config['main']['name'])
|
||||||
|
|
||||||
|
plugins.load(_config)
|
||||||
|
|
||||||
|
display = Display(config=_config, state={'name': '%s>' % name()})
|
||||||
|
|
||||||
|
if args.do_clear:
|
||||||
|
do_clear(display)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
agent = Agent(view=display, config=_config, keypair=KeyPair(view=display))
|
||||||
|
|
||||||
|
def usr1_handler(*unused):
|
||||||
|
logging.info('Received USR1 signal. Restart process ...')
|
||||||
|
agent._restart("MANU" if args.do_manual else "AUTO")
|
||||||
|
|
||||||
|
signal.signal(signal.SIGUSR1, usr1_handler)
|
||||||
|
|
||||||
|
if args.do_manual:
|
||||||
|
do_manual_mode(agent)
|
||||||
|
else:
|
||||||
|
do_auto_mode(agent)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pwnagotchi_cli()
|
||||||
|
|
||||||
|
|
||||||
def set_name(new_name):
|
def set_name(new_name):
|
||||||
if new_name is None:
|
if new_name is None:
|
||||||
|
Reference in New Issue
Block a user