mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
172
bin/pwnagotchi
172
bin/pwnagotchi
@ -19,85 +19,83 @@ from pwnagotchi import fs
|
|||||||
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
|
||||||
|
|
||||||
|
|
||||||
def do_clear(display):
|
def pwnagotchi_cli():
|
||||||
logging.info("clearing the display ...")
|
def do_clear(display):
|
||||||
display.clear()
|
logging.info("clearing the display ...")
|
||||||
sys.exit(0)
|
display.clear()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def do_manual_mode(agent):
|
||||||
|
logging.info("entering manual mode ...")
|
||||||
|
|
||||||
def do_manual_mode(agent):
|
agent.mode = 'manual'
|
||||||
logging.info("entering manual mode ...")
|
agent.last_session.parse(agent.view(), args.skip_session)
|
||||||
|
if not args.skip_session:
|
||||||
agent.mode = 'manual'
|
logging.info(
|
||||||
agent.last_session.parse(agent.view(), args.skip_session)
|
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||||
if not args.skip_session:
|
agent.last_session.duration_human,
|
||||||
logging.info(
|
agent.last_session.epochs,
|
||||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
agent.last_session.train_epochs,
|
||||||
agent.last_session.duration_human,
|
agent.last_session.avg_reward,
|
||||||
agent.last_session.epochs,
|
agent.last_session.min_reward,
|
||||||
agent.last_session.train_epochs,
|
agent.last_session.max_reward))
|
||||||
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:
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
display.on_manual_mode(agent.last_session)
|
||||||
|
time.sleep(5)
|
||||||
if grid.is_connected():
|
if grid.is_connected():
|
||||||
plugins.on('internet_available', agent)
|
plugins.on('internet_available', agent)
|
||||||
|
|
||||||
except Exception as e:
|
def do_auto_mode(agent):
|
||||||
if str(e).find("wifi.interface not set") > 0:
|
logging.info("entering auto mode ...")
|
||||||
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")
|
agent.mode = 'auto'
|
||||||
time.sleep(60)
|
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(0.2)
|
||||||
|
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()
|
agent.next_epoch()
|
||||||
else:
|
|
||||||
logging.exception("main loop exception (%s)", e)
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
def add_parsers(parser):
|
def add_parsers(parser):
|
||||||
"""
|
"""
|
||||||
Adds the plugins and google subcommands
|
Adds the plugins and google subcommands
|
||||||
@ -169,21 +167,23 @@ if __name__ == '__main__':
|
|||||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||||
return all(allowed.match(x) for x in hostname.split("."))
|
return all(allowed.match(x) for x in hostname.split("."))
|
||||||
|
|
||||||
pwn_restore = input("Do you want to restore the previous configuration? [Y/N]\n")
|
pwn_restore = input("Do you want to restore the previous configuration?\n\n"
|
||||||
|
"[Y/N]: ")
|
||||||
if pwn_restore in ('y', 'yes'):
|
if pwn_restore in ('y', 'yes'):
|
||||||
os.system("cp -f /etc/pwnagotchi/config.toml.bak /etc/pwnagotchi/config.toml")
|
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.")
|
print("Your previous configuration is restored, and I will restart in 5 seconds.")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
os.system("service pwnagotchi restart")
|
os.system("service pwnagotchi restart")
|
||||||
else:
|
else:
|
||||||
pwn_check = input("This will create a new configuration file and overwrite your current backup, are you sure? [Y/N]\n")
|
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'):
|
if pwn_check.lower() in ('y', 'yes'):
|
||||||
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
|
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
|
||||||
with open("/etc/pwnagotchi/config.toml", "a+") as f:
|
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")
|
f.write("# Do not edit this file if you do not know what you are doing!!!\n\n")
|
||||||
# Set pwnagotchi name
|
# Set pwnagotchi name
|
||||||
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
|
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
|
||||||
"My name is Jayofelony, how may I call you?\n")
|
"My name is Jayofelony, how may I call you?\n\n")
|
||||||
pwn_name = input("Pwnagotchi name (no spaces): ")
|
pwn_name = input("Pwnagotchi name (no spaces): ")
|
||||||
if pwn_name == "":
|
if pwn_name == "":
|
||||||
pwn_name = "Pwnagotchi"
|
pwn_name = "Pwnagotchi"
|
||||||
@ -212,32 +212,39 @@ if __name__ == '__main__':
|
|||||||
f.write(f"\t\"{bssid}\",\n")
|
f.write(f"\t\"{bssid}\",\n")
|
||||||
f.write("]\n")
|
f.write("]\n")
|
||||||
# set bluetooth tether
|
# set bluetooth tether
|
||||||
pwn_bluetooth = input("Do you want to enable BT-Tether? [Y/N] ")
|
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
|
||||||
|
"[Y/N] ")
|
||||||
if pwn_bluetooth.lower() in ('y', 'yes'):
|
if pwn_bluetooth.lower() in ('y', 'yes'):
|
||||||
f.write("main.plugins.bt-tether.enabled = true\n\n")
|
f.write("main.plugins.bt-tether.enabled = true\n\n")
|
||||||
pwn_bluetooth_device = input("What device do you use? Android or iOS? ")
|
pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
|
||||||
|
"Device: ")
|
||||||
if pwn_bluetooth_device.lower() == "android":
|
if pwn_bluetooth_device.lower() == "android":
|
||||||
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
|
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
|
||||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device? ")
|
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||||
|
"MAC: ")
|
||||||
if pwn_bluetooth_mac != "":
|
if pwn_bluetooth_mac != "":
|
||||||
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
||||||
elif pwn_bluetooth_device.lower() == "ios":
|
elif pwn_bluetooth_device.lower() == "ios":
|
||||||
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
|
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
|
||||||
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device? ")
|
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
|
||||||
|
"MAC: ")
|
||||||
if pwn_bluetooth_mac != "":
|
if pwn_bluetooth_mac != "":
|
||||||
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
|
||||||
# set up display settings
|
# set up display settings
|
||||||
pwn_display_enabled = input("Do you use a display? [Y/N] ")
|
pwn_display_enabled = input("Do you want to enable a display?\n\n"
|
||||||
|
"[Y/N]: ")
|
||||||
if pwn_display_enabled.lower() in ('y', 'yes'):
|
if pwn_display_enabled.lower() in ('y', 'yes'):
|
||||||
f.write("ui.display.enabled = true\n")
|
f.write("ui.display.enabled = true\n")
|
||||||
pwn_display_type = input("What display do you use?\n\n"
|
pwn_display_type = input("What display do you use?\n\n"
|
||||||
"Be sure to check for the correct display type @ \n"
|
"Be sure to check for the correct display type @ \n"
|
||||||
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L431\n")
|
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L431\n\n"
|
||||||
|
"Display type: ")
|
||||||
if pwn_display_type != "":
|
if pwn_display_type != "":
|
||||||
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
|
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
|
||||||
pwn_display_invert = input("Do you want to invert the display colors? [Y/N]\n\n"
|
pwn_display_invert = input("Do you want to invert the display colors?\n"
|
||||||
"N = Black background\n"
|
"N = Black background\n"
|
||||||
"Y = White background\n")
|
"Y = White background\n\n"
|
||||||
|
"[Y/N]: ")
|
||||||
if pwn_display_invert.lower() in ('y', 'yes'):
|
if pwn_display_invert.lower() in ('y', 'yes'):
|
||||||
f.write("ui.invert = true\n")
|
f.write("ui.invert = true\n")
|
||||||
f.close()
|
f.close()
|
||||||
@ -326,3 +333,6 @@ if __name__ == '__main__':
|
|||||||
do_manual_mode(agent)
|
do_manual_mode(agent)
|
||||||
else:
|
else:
|
||||||
do_auto_mode(agent)
|
do_auto_mode(agent)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pwnagotchi_cli()
|
60
pyproject.toml
Normal file
60
pyproject.toml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "pwnagotchi"
|
||||||
|
dynamic = ["version"]
|
||||||
|
dependencies = [
|
||||||
|
"Pillow",
|
||||||
|
"PyYAML",
|
||||||
|
"RPi.GPIO",
|
||||||
|
"file-read-backwards",
|
||||||
|
"flask",
|
||||||
|
"flask-cors",
|
||||||
|
"flask-wtf",
|
||||||
|
"gast",
|
||||||
|
"gym",
|
||||||
|
"inky",
|
||||||
|
"pycryptodome",
|
||||||
|
"pydrive2",
|
||||||
|
"python-dateutil",
|
||||||
|
"requests",
|
||||||
|
"rpi_hardware_pwm",
|
||||||
|
"scapy",
|
||||||
|
"shimmy",
|
||||||
|
"smbus2",
|
||||||
|
"spidev",
|
||||||
|
"stable_baselines3",
|
||||||
|
"toml",
|
||||||
|
"torch",
|
||||||
|
"torchvision",
|
||||||
|
"tweepy",
|
||||||
|
"websockets"
|
||||||
|
]
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
authors = [
|
||||||
|
{name = "Evilsocket", email = "evilsocket@gmail.com"},
|
||||||
|
{name = "Jayofelony", email = "oudshoorn.jeroen@gmail.com"}
|
||||||
|
]
|
||||||
|
maintainers = [
|
||||||
|
{name = "Jayofelony", email = "oudshoorn.jeroen@gmail.com"}
|
||||||
|
]
|
||||||
|
description = "(⌐■_■) - Deep Reinforcement Learning instrumenting bettercap for WiFI pwning."
|
||||||
|
readme = "README.md"
|
||||||
|
license = {file = "LICENSE.md"}
|
||||||
|
classifiers = [
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
'License :: OSI Approved :: GNU General Public License (GPL)',
|
||||||
|
'Environment :: Console',
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://pwnagotchi.org/"
|
||||||
|
Documentation = "https://pwnagotchi.org/"
|
||||||
|
Repository = "https://github.com/jayofelony/pwnagotchi.git"
|
||||||
|
Issues = "https://github.com/jayofelony/pwnagotchi/issues"
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
pwnagotchi_cli = "bin.pwnagotchi:pwnagotchi_cli"
|
@ -1,5 +1,5 @@
|
|||||||
gym
|
gym
|
||||||
shimmy; platform_machine!="armv6l"
|
shimmy
|
||||||
pycryptodome
|
pycryptodome
|
||||||
requests
|
requests
|
||||||
PyYAML
|
PyYAML
|
||||||
@ -18,17 +18,9 @@ dbus-python
|
|||||||
toml
|
toml
|
||||||
python-dateutil
|
python-dateutil
|
||||||
websockets
|
websockets
|
||||||
torch; platform_machine=="aarch64"
|
torch
|
||||||
torch>=2.0.1; platform_machine!="aarch64"
|
torchvision
|
||||||
|
stable_baselines3
|
||||||
torchvision; platform_machine=="aarch64"
|
RPi.GPIO
|
||||||
torchvision>=0.15.2; platform_machine!="aarch64"
|
rpi_hardware_pwm
|
||||||
|
|
||||||
stable_baselines3==1.8.0; platform_machine=="armv6l"
|
|
||||||
stable_baselines3; platform_machine!="armv6l"
|
|
||||||
|
|
||||||
RPi.GPIO; platform_release!="6.1.31-sun50iw9"
|
|
||||||
OPi.GPIO; platform_release=="6.1.31-sun50iw9"
|
|
||||||
|
|
||||||
rpi_hardware_pwm; platform_release!="6.1.31-sun50iw9"
|
|
||||||
pydrive2
|
pydrive2
|
Reference in New Issue
Block a user