diff --git a/pwnagotchi/plugins/default/net-pos.py b/pwnagotchi/plugins/default/net-pos.py index 27f04d9..d0c54ae 100644 --- a/pwnagotchi/plugins/default/net-pos.py +++ b/pwnagotchi/plugins/default/net-pos.py @@ -1,5 +1,5 @@ __author__ = 'zenzen san' -__version__ = '1.0.0' +__version__ = '2.0.0' __name__ = 'net-pos' __license__ = 'GPL3' __description__ = """Saves a json file with the access points with more signal @@ -11,33 +11,24 @@ import logging import json import os import requests +from pwnagotchi.utils import StatusFile MOZILLA_API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}' -ALREADY_SAVED = None -SKIP = None +REPORT = StatusFile('/root/.net_pos_saved', data_format='json') +SKIP = list() READY = False -OPTIONS = {} +OPTIONS = dict() def on_loaded(): - global ALREADY_SAVED - global SKIP global READY - SKIP = list() - if 'api_key' not in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None): logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.") return - try: - with open('/root/.net_pos_saved', 'r') as f: - ALREADY_SAVED = f.read().splitlines() - except OSError: - logging.warning('NET-POS: No net-pos-file found.') - ALREADY_SAVED = [] - READY = True + logging.info("net-pos plugin loaded.") def _append_saved(path): @@ -55,20 +46,22 @@ def _append_saved(path): def on_internet_available(agent): global SKIP + global REPORT if READY: config = agent.config() display = agent.view() + reported = REPORT.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] all_files = os.listdir(handshake_dir) all_np_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.net-pos.json')] - new_np_files = set(all_np_files) - set(ALREADY_SAVED) - set(SKIP) + new_np_files = set(all_np_files) - set(reported) - set(SKIP) if new_np_files: - logging.info("NET-POS: Found {num} new net-pos files. Fetching positions ...", len(new_np_files)) + logging.info("NET-POS: Found %d new net-pos files. Fetching positions ...", len(new_np_files)) display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...") display.update(force=True) for idx, np_file in enumerate(new_np_files): @@ -76,8 +69,8 @@ def on_internet_available(agent): geo_file = np_file.replace('.net-pos.json', '.geo.json') if os.path.exists(geo_file): # got already the position - ALREADY_SAVED.append(np_file) - _append_saved(np_file) + reported.append(np_file) + REPORT.update(data={'reported': reported}) continue try: @@ -85,18 +78,21 @@ def on_internet_available(agent): except requests.exceptions.RequestException as req_e: logging.error("NET-POS: %s", req_e) SKIP += np_file + continue except json.JSONDecodeError as js_e: logging.error("NET-POS: %s", js_e) SKIP += np_file + continue except OSError as os_e: logging.error("NET-POS: %s", os_e) SKIP += np_file + continue with open(geo_file, 'w+t') as sf: json.dump(geo_data, sf) - ALREADY_SAVED.append(np_file) - _append_saved(np_file) + reported.append(np_file) + REPORT.update(data={'reported': reported}) display.set('status', f"Fetching positions ({idx+1}/{len(new_np_files)})") display.update(force=True) @@ -108,15 +104,15 @@ def on_handshake(agent, filename, access_point, client_station): logging.info("NET-POS: Saving net-location to %s", netpos_filename) try: - with open(netpos_filename, 'w+t') as fp: - json.dump(netpos, fp) + with open(netpos_filename, 'w+t') as net_pos_file: + json.dump(netpos, net_pos_file) except OSError as os_e: logging.error("NET-POS: %s", os_e) def _get_netpos(agent): aps = agent.get_access_points() - netpos = {} + netpos = dict() netpos['wifiAccessPoints'] = list() # 6 seems a good number to save a wifi networks location for access_point in sorted(aps, key=lambda i: i['rssi'], reverse=True)[:6]: diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py index 25a18d6..dbf8a4e 100644 --- a/pwnagotchi/plugins/default/onlinehashcrack.py +++ b/pwnagotchi/plugins/default/onlinehashcrack.py @@ -1,5 +1,5 @@ __author__ = '33197631+dadav@users.noreply.github.com' -__version__ = '1.0.0' +__version__ = '2.0.0' __name__ = 'onlinehashcrack' __license__ = 'GPL3' __description__ = 'This plugin automatically uploades handshakes to https://onlinehashcrack.com' @@ -7,9 +7,11 @@ __description__ = 'This plugin automatically uploades handshakes to https://onli import os import logging import requests +from pwnagotchi.utils import StatusFile READY = False -ALREADY_UPLOADED = None +REPORT = StatusFile('/root/.ohc_uploads', data_format='json') +SKIP = list() OPTIONS = dict() @@ -18,20 +20,11 @@ def on_loaded(): Gets called when the plugin gets loaded """ global READY - global EMAIL - global ALREADY_UPLOADED - if not 'email' in OPTIONS or ('email' in OPTIONS and OPTIONS['email'] is None): + if 'email' not in OPTIONS or ('email' in OPTIONS and OPTIONS['email'] is None): logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com") return - try: - with open('/root/.ohc_uploads', 'r') as f: - ALREADY_UPLOADED = f.read().splitlines() - except OSError: - logging.warning('OHC: No upload-file found.') - ALREADY_UPLOADED = [] - READY = True @@ -59,14 +52,17 @@ def on_internet_available(agent): """ Called in manual mode when there's internet connectivity """ + global REPORT + global SKIP if READY: display = agent.view() config = agent.config() + reported = REPORT.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] - handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED) + handshake_new = set(handshake_paths) - set(reported) - set(SKIP) if handshake_new: logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onelinehashcrack.com") @@ -76,12 +72,15 @@ def on_internet_available(agent): display.update(force=True) try: _upload_to_ohc(handshake) - ALREADY_UPLOADED.append(handshake) - with open('/root/.ohc_uploads', 'a') as f: - f.write(handshake + "\n") + reported.append(handshake) + REPORT.update(data={'reported': reported}) logging.info(f"OHC: Successfuly uploaded {handshake}") - except requests.exceptions.RequestException: - pass + except requests.exceptions.RequestException as req_e: + SKIP.append(handshake) + logging.error("OHC: %s", req_e) + continue except OSError as os_e: - logging.error(f"OHC: Got the following error: {os_e}") + SKIP.append(handshake) + logging.error("OHC: %s", os_e) + continue diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py index b3f3329..61bc3de 100644 --- a/pwnagotchi/plugins/default/wigle.py +++ b/pwnagotchi/plugins/default/wigle.py @@ -1,5 +1,5 @@ __author__ = '33197631+dadav@users.noreply.github.com' -__version__ = '1.0.0' +__version__ = '2.0.0' __name__ = 'wigle' __license__ = 'GPL3' __description__ = 'This plugin automatically uploades collected wifis to wigle.net' @@ -11,111 +11,24 @@ from io import StringIO import csv from datetime import datetime import requests -from pwnagotchi.mesh.wifi import freq_to_channel -from pwnagotchi.utils import WifiInfo, FieldNotFoundError, extract_from_pcap +from pwnagotchi.utils import WifiInfo, FieldNotFoundError, extract_from_pcap, StatusFile READY = False -ALREADY_UPLOADED = None -SKIP = None +REPORT = StatusFile('/root/.wigle_uploads', data_format='json') +SKIP = list() OPTIONS = dict() -AKMSUITE_TYPES = { - 0x00: "Reserved", - 0x01: "802.1X", - 0x02: "PSK", -} - -def _handle_packet(packet, result): - from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ - Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA - """ - Analyze each packet and extract the data from Dot11 layers - """ - - if hasattr(packet, 'cap') and 'privacy' in packet.cap: - # packet is encrypted - if 'encryption' not in result: - result['encryption'] = set() - - if packet.haslayer(Dot11Beacon): - if packet.haslayer(Dot11Beacon)\ - or packet.haslayer(Dot11ProbeResp)\ - or packet.haslayer(Dot11AssoReq)\ - or packet.haslayer(Dot11ReassoReq): - if 'bssid' not in result and hasattr(packet[Dot11], 'addr3'): - result['bssid'] = packet[Dot11].addr3 - if 'essid' not in result and hasattr(packet[Dot11Elt], 'info'): - result['essid'] = packet[Dot11Elt].info - if 'channel' not in result and hasattr(packet[Dot11Elt:3], 'info'): - result['channel'] = int(ord(packet[Dot11Elt:3].info)) - - if packet.haslayer(RadioTap): - if 'rssi' not in result and hasattr(packet[RadioTap], 'dBm_AntSignal'): - result['rssi'] = packet[RadioTap].dBm_AntSignal - if 'channel' not in result and hasattr(packet[RadioTap], 'ChannelFrequency'): - result['channel'] = freq_to_channel(packet[RadioTap].ChannelFrequency) - - # see: https://fossies.org/linux/scapy/scapy/layers/dot11.py - if packet.haslayer(Dot11EltRSN): - if hasattr(packet[Dot11EltRSN], 'akm_suites'): - auth = AKMSUITE_TYPES.get(packet[Dot11EltRSN].akm_suites[0].suite) - result['encryption'].add(f"WPA2/{auth}") - else: - result['encryption'].add("WPA2") - - if packet.haslayer(Dot11EltVendorSpecific)\ - and (packet.haslayer(Dot11EltMicrosoftWPA) - or packet.info.startswith(b'\x00P\xf2\x01\x01\x00')): - - if hasattr(packet, 'akm_suites'): - auth = AKMSUITE_TYPES.get(packet.akm_suites[0].suite) - result['encryption'].add(f"WPA2/{auth}") - else: - result['encryption'].add("WPA2") - # end see - - return result - - -def _analyze_pcap(pcap): - from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ - Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA - """ - Iterate over the packets and extract data - """ - result = dict() - - try: - packets = rdpcap(pcap) - for packet in packets: - result = _handle_packet(packet, result) - except Scapy_Exception as sc_e: - raise sc_e - - return result - def on_loaded(): """ Gets called when the plugin gets loaded """ global READY - global ALREADY_UPLOADED - global SKIP - - SKIP = list() if 'api_key' not in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None): logging.error("WIGLE: api_key isn't set. Can't upload to wigle.net") return - try: - with open('/root/.wigle_uploads', 'r') as f: - ALREADY_UPLOADED = f.read().splitlines() - except OSError: - logging.warning('WIGLE: No upload-file found.') - ALREADY_UPLOADED = [] - READY = True @@ -197,24 +110,24 @@ def _send_to_wigle(lines, api_key, timeout=30): def on_internet_available(agent): - from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, \ - Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA + from scapy.all import Scapy_Exception """ Called in manual mode when there's internet connectivity """ - global ALREADY_UPLOADED + global REPORT global SKIP if READY: config = agent.config() display = agent.view() + reported = REPORT.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] all_files = os.listdir(handshake_dir) all_gps_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.gps.json')] - new_gps_files = set(all_gps_files) - set(ALREADY_UPLOADED) - set(SKIP) + new_gps_files = set(all_gps_files) - set(reported) - set(SKIP) if new_gps_files: logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") @@ -271,10 +184,8 @@ def on_internet_available(agent): display.update(force=True) try: _send_to_wigle(csv_entries, OPTIONS['api_key']) - ALREADY_UPLOADED += no_err_entries - with open('/root/.wigle_uploads', 'a') as up_file: - for gps in no_err_entries: - up_file.write(gps + "\n") + reported += no_err_entries + REPORT.update(data={'reported': reported}) logging.info("WIGLE: Successfuly uploaded %d files", len(no_err_entries)) except requests.exceptions.RequestException as re_e: SKIP += no_err_entries diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py index 0f6b854..8ab7351 100644 --- a/pwnagotchi/plugins/default/wpa-sec.py +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -7,9 +7,12 @@ __description__ = 'This plugin automatically uploades handshakes to https://wpa- import os import logging import requests +from pwnagotchi.utils import StatusFile READY = False -ALREADY_UPLOADED = None +REPORT = StatusFile('/root/.wpa_sec_uploads', data_format='json') +OPTIONS = dict() +SKIP = list() def on_loaded(): @@ -17,20 +20,11 @@ def on_loaded(): Gets called when the plugin gets loaded """ global READY - global API_KEY - global ALREADY_UPLOADED - if not 'api_key' in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None): + if 'api_key' not in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None): logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org") return - try: - with open('/root/.wpa_sec_uploads', 'r') as f: - ALREADY_UPLOADED = f.read().splitlines() - except OSError: - logging.warning('WPA_SEC: No upload-file found.') - ALREADY_UPLOADED = [] - READY = True @@ -48,39 +42,42 @@ def _upload_to_wpasec(path, timeout=30): files=payload, timeout=timeout) if ' already submitted' in result.text: - logging.warning(f"{path} was already submitted.") - except requests.exceptions.RequestException as e: - logging.error(f"WPA_SEC: Got an exception while uploading {path} -> {e}") - raise e + logging.warning("%s was already submitted.", path) + except requests.exceptions.RequestException as req_e: + raise req_e def on_internet_available(agent): """ Called in manual mode when there's internet connectivity """ + global REPORT + global SKIP if READY: config = agent.config() display = agent.view() + reported = REPORT.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] - handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED) + handshake_new = set(handshake_paths) - set(reported) - set(SKIP) if handshake_new: - logging.info("WPA_SEC: Internet connectivity detected.\ - Uploading new handshakes to wpa-sec.stanev.org") + logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes to wpa-sec.stanev.org") for idx, handshake in enumerate(handshake_new): display.set('status', f"Uploading handshake to wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})") display.update(force=True) try: _upload_to_wpasec(handshake) - ALREADY_UPLOADED.append(handshake) - with open('/root/.wpa_sec_uploads', 'a') as f: - f.write(handshake + "\n") - logging.info(f"WPA_SEC: Successfuly uploaded {handshake}") - except requests.exceptions.RequestException: - pass + reported.append(handshake) + REPORT.update(data={'reported': reported}) + logging.info("WPA_SEC: Successfuly uploaded %s", handshake) + except requests.exceptions.RequestException as req_e: + SKIP.append(handshake) + logging.error("WPA_SEC: %s", req_e) + continue except OSError as os_e: - logging.error(f"WPA_SEC: Got the following error: {os_e}") + logging.error("WPA_SEC: %s", os_e) + continue