Compare commits
46 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
63dc672b11 | ||
|
0dac137df0 | ||
|
3db9ccb47e | ||
|
a7164ea742 | ||
|
cae2a18016 | ||
|
9d63eba232 | ||
|
f141e15ba3 | ||
|
e68165ce06 | ||
|
3758806919 | ||
|
1f91c6f09e | ||
|
3e8b6eafbd | ||
|
44138ba463 | ||
|
4b71fea404 | ||
|
6babad0d02 | ||
|
e8513240ea | ||
|
00101ccd07 | ||
|
e0a66f5c99 | ||
|
81061cea24 | ||
|
93bb633010 | ||
|
dbb64e0fab | ||
|
fa8751017d | ||
|
9d56c97aa5 | ||
|
1523dfc1ef | ||
|
a02960b56d | ||
|
b6f59f99d4 | ||
|
09a00adab9 | ||
|
7fa30c2868 | ||
|
774d9c693c | ||
|
87b6cf7d40 | ||
|
88928eec82 | ||
|
60f7849838 | ||
|
3cf041617c | ||
|
ee6c06f306 | ||
|
2e22a17610 | ||
|
f8ffab426b | ||
|
f563d71477 | ||
|
7b219fd139 | ||
|
42ed698583 | ||
|
6df7bcd885 | ||
|
1c299832ae | ||
|
11476433ca | ||
|
b1ad247e11 | ||
|
30b1874a0a | ||
|
b903f636d2 | ||
|
92c1b6b005 | ||
|
eddfdb3ebc |
@@ -10,6 +10,8 @@ PermissionsStartOnly=true
|
||||
ExecStart=/usr/bin/pwnagotchi-launcher
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
TasksMax=infinity
|
||||
LimitNPROC=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import subprocess
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
import re
|
||||
|
||||
import pwnagotchi.ui.view as view
|
||||
import pwnagotchi
|
||||
|
||||
version = '1.4.1'
|
||||
version = '1.4.3'
|
||||
|
||||
_name = None
|
||||
|
||||
@@ -27,17 +27,17 @@ def set_name(new_name):
|
||||
if new_name != current:
|
||||
global _name
|
||||
|
||||
logging.info("setting unit hostname '%s' -> '%s'" % (current, new_name))
|
||||
logging.info("setting unit hostname '%s' -> '%s'", current, new_name)
|
||||
with open('/etc/hostname', 'wt') as fp:
|
||||
fp.write(new_name)
|
||||
|
||||
with open('/etc/hosts', 'rt') as fp:
|
||||
prev = fp.read()
|
||||
logging.debug("old hosts:\n%s\n" % prev)
|
||||
logging.debug("old hosts:\n%s\n", prev)
|
||||
|
||||
with open('/etc/hosts', 'wt') as fp:
|
||||
patched = prev.replace(current, new_name, -1)
|
||||
logging.debug("new hosts:\n%s\n" % patched)
|
||||
logging.debug("new hosts:\n%s\n", patched)
|
||||
fp.write(patched)
|
||||
|
||||
os.system("hostname '%s'" % new_name)
|
||||
@@ -109,7 +109,7 @@ def shutdown():
|
||||
|
||||
|
||||
def restart(mode):
|
||||
logging.warning("restarting in %s mode ..." % mode)
|
||||
logging.warning("restarting in %s mode ...", mode)
|
||||
|
||||
if mode == 'AUTO':
|
||||
os.system("touch /root/.pwnagotchi-auto")
|
||||
@@ -123,7 +123,7 @@ def restart(mode):
|
||||
def reboot(mode=None):
|
||||
if mode is not None:
|
||||
mode = mode.upper()
|
||||
logging.warning("rebooting in %s mode ..." % mode)
|
||||
logging.warning("rebooting in %s mode ...", mode)
|
||||
else:
|
||||
logging.warning("rebooting ...")
|
||||
|
||||
|
@@ -212,7 +212,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
ch = ap['channel']
|
||||
# if we're sticking to a channel, skip anything
|
||||
# which is not on that channel
|
||||
if not channels and ch not in channels:
|
||||
if channels and ch not in channels:
|
||||
continue
|
||||
|
||||
if ch not in grouped:
|
||||
|
@@ -12,19 +12,18 @@ class Automata(object):
|
||||
self._epoch = Epoch(config)
|
||||
|
||||
def _on_miss(self, who):
|
||||
logging.info("it looks like %s is not in range anymore :/" % who)
|
||||
logging.info("it looks like %s is not in range anymore :/", who)
|
||||
self._epoch.track(miss=True)
|
||||
self._view.on_miss(who)
|
||||
|
||||
def _on_error(self, who, e):
|
||||
error = "%s" % e
|
||||
# when we're trying to associate or deauth something that is not in range anymore
|
||||
# (if we are moving), we get the following error from bettercap:
|
||||
# error 400: 50:c7:bf:2e:d3:37 is an unknown BSSID or it is in the association skip list.
|
||||
if 'is an unknown BSSID' in error:
|
||||
if 'is an unknown BSSID' in str(e):
|
||||
self._on_miss(who)
|
||||
else:
|
||||
logging.error("%s" % e)
|
||||
logging.error(e)
|
||||
|
||||
def set_starting(self):
|
||||
self._view.on_starting()
|
||||
@@ -58,7 +57,7 @@ class Automata(object):
|
||||
def set_bored(self):
|
||||
factor = self._epoch.inactive_for / self._config['personality']['bored_num_epochs']
|
||||
if not self._has_support_network_for(factor):
|
||||
logging.warning("%d epochs with no activity -> bored" % self._epoch.inactive_for)
|
||||
logging.warning("%d epochs with no activity -> bored", self._epoch.inactive_for)
|
||||
self._view.on_bored()
|
||||
plugins.on('bored', self)
|
||||
else:
|
||||
@@ -68,7 +67,7 @@ class Automata(object):
|
||||
def set_sad(self):
|
||||
factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs']
|
||||
if not self._has_support_network_for(factor):
|
||||
logging.warning("%d epochs with no activity -> sad" % self._epoch.inactive_for)
|
||||
logging.warning("%d epochs with no activity -> sad", self._epoch.inactive_for)
|
||||
self._view.on_sad()
|
||||
plugins.on('sad', self)
|
||||
else:
|
||||
@@ -77,7 +76,7 @@ class Automata(object):
|
||||
|
||||
def set_angry(self, factor):
|
||||
if not self._has_support_network_for(factor):
|
||||
logging.warning("%d epochs with no activity -> angry" % self._epoch.inactive_for)
|
||||
logging.warning("%d epochs with no activity -> angry", self._epoch.inactive_for)
|
||||
self._view.on_angry()
|
||||
plugins.on('angry', self)
|
||||
else:
|
||||
@@ -85,7 +84,7 @@ class Automata(object):
|
||||
self.set_grateful()
|
||||
|
||||
def set_excited(self):
|
||||
logging.warning("%d epochs with activity -> excited" % self._epoch.active_for)
|
||||
logging.warning("%d epochs with activity -> excited", self._epoch.active_for)
|
||||
self._view.on_excited()
|
||||
plugins.on('excited', self)
|
||||
|
||||
@@ -118,7 +117,7 @@ class Automata(object):
|
||||
if factor >= 2.0:
|
||||
self.set_angry(factor)
|
||||
else:
|
||||
logging.warning("agent missed %d interactions -> lonely" % did_miss)
|
||||
logging.warning("agent missed %d interactions -> lonely", did_miss)
|
||||
self.set_lonely()
|
||||
# after X times being bored, the status is set to sad or angry
|
||||
elif self._epoch.inactive_for >= self._config['personality']['sad_num_epochs']:
|
||||
@@ -139,6 +138,6 @@ class Automata(object):
|
||||
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
|
||||
|
||||
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
|
||||
logging.critical("%d epochs without visible access points -> rebooting ..." % self._epoch.blind_for)
|
||||
logging.critical("%d epochs without visible access points -> rebooting ...", self._epoch.blind_for)
|
||||
self._reboot()
|
||||
self._epoch.blind_for = 0
|
||||
|
@@ -1,5 +1,4 @@
|
||||
# WARNING WARNING WARNING WARNING
|
||||
#
|
||||
#
|
||||
# This file is recreated with default settings on every pwnagotchi restart,
|
||||
# use /etc/pwnagotchi/config.yml to configure this unit.
|
||||
#
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import glob
|
||||
import _thread
|
||||
import threading
|
||||
import importlib, importlib.util
|
||||
import logging
|
||||
from pwnagotchi.ui import view
|
||||
@@ -8,17 +9,27 @@ from pwnagotchi.ui import view
|
||||
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
||||
loaded = {}
|
||||
database = {}
|
||||
locks = {}
|
||||
|
||||
|
||||
class Plugin:
|
||||
@classmethod
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
global loaded
|
||||
global loaded, locks
|
||||
|
||||
plugin_name = cls.__module__.split('.')[0]
|
||||
plugin_instance = cls()
|
||||
logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance))
|
||||
loaded[plugin_name] = plugin_instance
|
||||
|
||||
for attr_name in plugin_instance.__dir__():
|
||||
if attr_name.startswith('on_'):
|
||||
cb = getattr(plugin_instance, attr_name, None)
|
||||
if cb is not None and callable(cb):
|
||||
locks["%s::%s" % (plugin_name, attr_name)] = threading.Lock()
|
||||
|
||||
|
||||
def toggle_plugin(name, enable=True):
|
||||
"""
|
||||
Load or unload a plugin
|
||||
@@ -47,15 +58,24 @@ def on(event_name, *args, **kwargs):
|
||||
one(plugin_name, event_name, *args, **kwargs)
|
||||
|
||||
|
||||
def locked_cb(lock_name, cb, *args, **kwargs):
|
||||
global locks
|
||||
with locks[lock_name]:
|
||||
cb(*args, *kwargs)
|
||||
|
||||
|
||||
def one(plugin_name, event_name, *args, **kwargs):
|
||||
global loaded
|
||||
|
||||
if plugin_name in loaded:
|
||||
plugin = loaded[plugin_name]
|
||||
cb_name = 'on_%s' % event_name
|
||||
callback = getattr(plugin, cb_name, None)
|
||||
if callback is not None and callable(callback):
|
||||
try:
|
||||
_thread.start_new_thread(callback, (*args, *kwargs))
|
||||
lock_name = "%s::%s" % (plugin_name, cb_name)
|
||||
locked_cb_args = (lock_name, callback, *args, *kwargs)
|
||||
_thread.start_new_thread(locked_cb, locked_cb_args)
|
||||
except Exception as e:
|
||||
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
||||
logging.error(e, exc_info=True)
|
||||
|
@@ -7,6 +7,7 @@ import platform
|
||||
import shutil
|
||||
import glob
|
||||
import pkg_resources
|
||||
from threading import Lock
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.plugins as plugins
|
||||
@@ -150,6 +151,7 @@ class AutoUpdate(plugins.Plugin):
|
||||
def __init__(self):
|
||||
self.ready = False
|
||||
self.status = StatusFile('/root/.auto-update')
|
||||
self.lock = Lock()
|
||||
|
||||
def on_loaded(self):
|
||||
if 'interval' not in self.options or ('interval' in self.options and self.options['interval'] is None):
|
||||
@@ -159,60 +161,61 @@ class AutoUpdate(plugins.Plugin):
|
||||
logging.info("[update] plugin loaded.")
|
||||
|
||||
def on_internet_available(self, agent):
|
||||
logging.debug("[update] internet connectivity is available (ready %s)" % self.ready)
|
||||
with self.lock:
|
||||
logging.debug("[update] internet connectivity is available (ready %s)" % self.ready)
|
||||
|
||||
if not self.ready:
|
||||
return
|
||||
if not self.ready:
|
||||
return
|
||||
|
||||
if self.status.newer_then_hours(self.options['interval']):
|
||||
logging.debug("[update] last check happened less than %d hours ago" % self.options['interval'])
|
||||
return
|
||||
if self.status.newer_then_hours(self.options['interval']):
|
||||
logging.debug("[update] last check happened less than %d hours ago" % self.options['interval'])
|
||||
return
|
||||
|
||||
logging.info("[update] checking for updates ...")
|
||||
logging.info("[update] checking for updates ...")
|
||||
|
||||
display = agent.view()
|
||||
prev_status = display.get('status')
|
||||
display = agent.view()
|
||||
prev_status = display.get('status')
|
||||
|
||||
try:
|
||||
display.update(force=True, new_data={'status': 'Checking for updates ...'})
|
||||
try:
|
||||
display.update(force=True, new_data={'status': 'Checking for updates ...'})
|
||||
|
||||
to_install = []
|
||||
to_check = [
|
||||
('bettercap/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
|
||||
('evilsocket/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
|
||||
('evilsocket/pwnagotchi', pwnagotchi.version, False, 'pwnagotchi')
|
||||
]
|
||||
to_install = []
|
||||
to_check = [
|
||||
('bettercap/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
|
||||
('evilsocket/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
|
||||
('evilsocket/pwnagotchi', pwnagotchi.version, False, 'pwnagotchi')
|
||||
]
|
||||
|
||||
for repo, local_version, is_native, svc_name in to_check:
|
||||
info = check(local_version, repo, is_native)
|
||||
if info['url'] is not None:
|
||||
logging.warning(
|
||||
"update for %s available (local version is '%s'): %s" % (
|
||||
repo, info['current'], info['url']))
|
||||
info['service'] = svc_name
|
||||
to_install.append(info)
|
||||
for repo, local_version, is_native, svc_name in to_check:
|
||||
info = check(local_version, repo, is_native)
|
||||
if info['url'] is not None:
|
||||
logging.warning(
|
||||
"update for %s available (local version is '%s'): %s" % (
|
||||
repo, info['current'], info['url']))
|
||||
info['service'] = svc_name
|
||||
to_install.append(info)
|
||||
|
||||
num_updates = len(to_install)
|
||||
num_installed = 0
|
||||
num_updates = len(to_install)
|
||||
num_installed = 0
|
||||
|
||||
if num_updates > 0:
|
||||
if self.options['install']:
|
||||
for update in to_install:
|
||||
plugins.on('updating')
|
||||
if install(display, update):
|
||||
num_installed += 1
|
||||
else:
|
||||
prev_status = '%d new update%c available!' % (num_updates, 's' if num_updates > 1 else '')
|
||||
if num_updates > 0:
|
||||
if self.options['install']:
|
||||
for update in to_install:
|
||||
plugins.on('updating')
|
||||
if install(display, update):
|
||||
num_installed += 1
|
||||
else:
|
||||
prev_status = '%d new update%c available!' % (num_updates, 's' if num_updates > 1 else '')
|
||||
|
||||
logging.info("[update] done")
|
||||
logging.info("[update] done")
|
||||
|
||||
self.status.update()
|
||||
self.status.update()
|
||||
|
||||
if num_installed > 0:
|
||||
display.update(force=True, new_data={'status': 'Rebooting ...'})
|
||||
pwnagotchi.reboot()
|
||||
if num_installed > 0:
|
||||
display.update(force=True, new_data={'status': 'Rebooting ...'})
|
||||
pwnagotchi.reboot()
|
||||
|
||||
except Exception as e:
|
||||
logging.error("[update] %s" % e)
|
||||
except Exception as e:
|
||||
logging.error("[update] %s" % e)
|
||||
|
||||
display.update(force=True, new_data={'status': prev_status if prev_status is not None else ''})
|
||||
display.update(force=True, new_data={'status': prev_status if prev_status is not None else ''})
|
||||
|
@@ -54,6 +54,10 @@ class GPS(plugins.Plugin):
|
||||
lat_pos = (127, 75)
|
||||
lon_pos = (122, 84)
|
||||
alt_pos = (127, 94)
|
||||
elif ui.is_waveshare_v1():
|
||||
lat_pos = (130, 70)
|
||||
lon_pos = (125, 80)
|
||||
alt_pos = (130, 90)
|
||||
elif ui.is_inky():
|
||||
# guessed values, add tested ones if you can
|
||||
lat_pos = (112, 30)
|
||||
@@ -109,6 +113,13 @@ class GPS(plugins.Plugin):
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def on_unload(self, ui):
|
||||
with ui._lock:
|
||||
ui.remove_element('latitude')
|
||||
ui.remove_element('longitude')
|
||||
ui.remove_element('altitude')
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if self.coordinates and all([
|
||||
# avoid 0.000... measurements
|
||||
|
@@ -44,12 +44,18 @@ class MemTemp(plugins.Plugin):
|
||||
if ui.is_waveshare_v2():
|
||||
h_pos = (180, 80)
|
||||
v_pos = (180, 61)
|
||||
elif ui.is_waveshare_v1():
|
||||
h_pos = (170, 80)
|
||||
v_pos = (170, 61)
|
||||
elif ui.is_waveshare144lcd():
|
||||
h_pos = (53, 77)
|
||||
v_pos = (78, 67)
|
||||
elif ui.is_inky():
|
||||
h_pos = (140, 68)
|
||||
v_pos = (165, 54)
|
||||
elif ui.is_waveshare27inch():
|
||||
h_pos = (192, 138)
|
||||
v_pos = (216, 122)
|
||||
else:
|
||||
h_pos = (155, 76)
|
||||
v_pos = (180, 61)
|
||||
@@ -64,6 +70,10 @@ class MemTemp(plugins.Plugin):
|
||||
position=h_pos,
|
||||
label_font=fonts.Small, text_font=fonts.Small))
|
||||
|
||||
def on_unload(self, ui):
|
||||
with ui._lock:
|
||||
ui.remove_element('memtemp')
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if self.options['scale'] == "fahrenheit":
|
||||
temp = (pwnagotchi.temperature() * 9 / 5) + 32
|
||||
@@ -72,7 +82,7 @@ class MemTemp(plugins.Plugin):
|
||||
temp = pwnagotchi.temperature() + 273.15
|
||||
symbol = "k"
|
||||
else:
|
||||
# default to celsius
|
||||
# default to celsius
|
||||
temp = pwnagotchi.temperature()
|
||||
symbol = "c"
|
||||
|
||||
|
@@ -58,5 +58,9 @@ class UPSLite(plugins.Plugin):
|
||||
ui.add_element('ups', LabeledValue(color=BLACK, label='UPS', value='0%/0V', position=(ui.width() / 2 + 15, 0),
|
||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||
|
||||
def on_unload(self, ui):
|
||||
with ui._lock:
|
||||
ui.remove_element('ups')
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
ui.set('ups', "%2i%%" % self.ups.capacity())
|
||||
|
@@ -304,6 +304,9 @@ class PositionFile:
|
||||
elif 'Updated' in self._json:
|
||||
# convert gps datetime to unix timestamp: "2019-10-05T23:12:40.422996+01:00"
|
||||
date_iso_formated = self._json['Updated']
|
||||
#fix timezone "Z": "2019-11-28T04:44:46.79231Z" >> "2019-11-28T04:44:46.79231+00:00"
|
||||
if date_iso_formated.endswith("Z"):
|
||||
date_iso_formated = date_iso_formated[:-1] + "+00:00"
|
||||
# bad microseconds fix: fill/cut microseconds to 6 numbers
|
||||
part1, part2, part3 = re.split('\.|\+', date_iso_formated)
|
||||
part2 = part2.ljust(6, '0')[:6]
|
||||
|
@@ -6,7 +6,7 @@ from pwnagotchi.ui.hw.base import DisplayImpl
|
||||
|
||||
class Waveshare27inch(DisplayImpl):
|
||||
def __init__(self, config):
|
||||
super(Waveshare27inch, self).__init__(config, 'waveshare_2_7inch')
|
||||
super(Waveshare27inch, self).__init__(config, 'waveshare27inch')
|
||||
self._display = None
|
||||
|
||||
def layout(self):
|
||||
|
@@ -10,6 +10,8 @@ import yaml
|
||||
import json
|
||||
import shutil
|
||||
import gzip
|
||||
import contextlib
|
||||
import tempfile
|
||||
|
||||
import pwnagotchi
|
||||
|
||||
@@ -345,6 +347,17 @@ def extract_from_pcap(path, fields):
|
||||
|
||||
return results
|
||||
|
||||
@contextlib.contextmanager
|
||||
def ensure_write(filename, mode='w'):
|
||||
path = os.path.dirname(filename)
|
||||
fd, tmp = tempfile.mkstemp(dir=path)
|
||||
|
||||
with os.fdopen(fd, mode) as f:
|
||||
yield f
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
|
||||
os.replace(tmp, filename)
|
||||
|
||||
class StatusFile(object):
|
||||
def __init__(self, path, data_format='raw'):
|
||||
@@ -378,7 +391,7 @@ class StatusFile(object):
|
||||
def update(self, data=None):
|
||||
self._updated = datetime.now()
|
||||
self.data = data
|
||||
with open(self._path, 'w') as fp:
|
||||
with ensure_write(self._path, 'w') as fp:
|
||||
if data is None:
|
||||
fp.write(str(self._updated))
|
||||
|
||||
|
@@ -161,7 +161,10 @@ class Voice:
|
||||
|
||||
def on_last_session_data(self, last_session):
|
||||
status = self._('Kicked {num} stations\n').format(num=last_session.deauthed)
|
||||
status += self._('Made {num} new friends\n').format(num=last_session.associated)
|
||||
if last_session.associated > 999:
|
||||
status += self._('Made >999 new friends\n')
|
||||
else:
|
||||
status += self._('Made {num} new friends\n').format(num=last_session.associated)
|
||||
status += self._('Got {num} handshakes\n').format(num=last_session.handshakes)
|
||||
if last_session.peers == 1:
|
||||
status += self._('Met 1 peer')
|
||||
|
@@ -3,6 +3,7 @@ requests==2.21.0
|
||||
PyYAML==5.1
|
||||
scapy==2.4.3
|
||||
gym==0.14.0
|
||||
scipy==1.3.1
|
||||
stable-baselines==2.7.0
|
||||
tensorflow==1.13.1
|
||||
tensorflow-estimator==1.14.0
|
||||
@@ -16,4 +17,4 @@ spidev==3.4
|
||||
gast==0.2.2
|
||||
flask==1.0.2
|
||||
flask-cors==3.0.7
|
||||
flask-wtf==0.14.2
|
||||
flask-wtf==0.14.2
|
||||
|
Reference in New Issue
Block a user