Skip to content

Commit ebbfb44

Browse files
committed
WPA-SEC download into .pcap.cracked single files
This commit implements two new settings for the WPA-SEC plugin: - "download_interval", which allows you to decide how often to download passwords cracked by wpa-sec - "single_files". This option (which already existed for the Onlinehashcrack plugin), if set to true, downloads the cracked passwords from the wpasec site into individual files with the .pcap.cracked extension, so you can see the cracked WiFi passwords directly in the webgpsmap plugin map. Additionally, while rewriting the code I improved the log messages and exception handling (for example, by calling raise_for_status() after making HTTP requests and using the logging.exception() method, which prints the exception stacktrace to the logs for easier debugging).
1 parent ef0f35d commit ebbfb44

File tree

2 files changed

+63
-42
lines changed

2 files changed

+63
-42
lines changed

pwnagotchi/defaults.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ main.plugins.wpa-sec.enabled = false
4747
main.plugins.wpa-sec.api_key = ""
4848
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
4949
main.plugins.wpa-sec.download_results = false
50+
main.plugins.wpa-sec.single_files = false
51+
main.plugins.wpa-sec.download_interval = 3600
5052
main.plugins.wpa-sec.whitelist = []
5153

5254
main.plugins.wigle.enabled = false
Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import re
23
import logging
34
import requests
45
from datetime import datetime
@@ -27,26 +28,25 @@ def __init__(self):
2728

2829
def _upload_to_wpasec(self, path, timeout=30):
2930
"""
30-
Uploads the file to https://wpa-sec.stanev.org, or another endpoint.
31+
Uploads the file to wpasec
3132
"""
3233
with open(path, 'rb') as file_to_upload:
3334
cookie = {'key': self.options['api_key']}
3435
payload = {'file': file_to_upload}
3536

36-
try:
37-
result = requests.post(self.options['api_url'],
38-
cookies=cookie,
39-
files=payload,
40-
timeout=timeout)
41-
if ' already submitted' in result.text:
42-
logging.debug("%s was already submitted.", path)
43-
except requests.exceptions.RequestException as req_e:
44-
raise req_e
37+
logging.info("WPA_SEC: Uploading %s...", path)
4538

39+
result = requests.post(self.options['api_url'],
40+
cookies=cookie,
41+
files=payload,
42+
timeout=timeout)
43+
44+
result.raise_for_status()
45+
logging.info("WPA_SEC: Uploaded %s. Response was: %s.", path, result.text.partition('\n')[0])
4646

4747
def _download_from_wpasec(self, output, timeout=30):
4848
"""
49-
Downloads the results from wpasec and safes them to output
49+
Downloads the results from wpasec and saves them to output
5050
5151
Output-Format: bssid, station_mac, ssid, password
5252
"""
@@ -56,33 +56,56 @@ def _download_from_wpasec(self, output, timeout=30):
5656
api_url = f"{api_url}?api&dl=1"
5757

5858
cookie = {'key': self.options['api_key']}
59-
try:
60-
result = requests.get(api_url, cookies=cookie, timeout=timeout)
61-
with open(output, 'wb') as output_file:
62-
output_file.write(result.content)
63-
except requests.exceptions.RequestException as req_e:
64-
raise req_e
65-
except OSError as os_e:
66-
raise os_e
6759

60+
logging.info("WPA_SEC: Downloading cracked passwords...")
61+
62+
result = requests.get(api_url, cookies=cookie, timeout=timeout)
63+
result.raise_for_status()
64+
65+
with open(output, 'wb') as output_file:
66+
output_file.write(result.content)
67+
68+
logging.info("WPA_SEC: Downloaded cracked passwords.")
69+
70+
def _write_cracked_single_files(self, cracked_file_path, handshake_dir):
71+
"""
72+
Splits download results from wpasec into individual .pcap..cracked files in handshake_dir
73+
74+
Each .pcap.cracked file will contain the cracked handshake password
75+
"""
76+
logging.info("WPA_SEC: Writing cracked single files...")
77+
78+
with open(cracked_file_path, 'r') as cracked_file:
79+
for line in cracked_file:
80+
try:
81+
bssid,station_mac,ssid,password = line.split(":")
82+
if password:
83+
filename = re.sub(r'[^a-zA-Z0-9]', '', ssid) + '_' + bssid
84+
if os.path.exists( os.path.join(handshake_dir, filename+'.pcap') ) and not os.path.exists( os.path.join(handshake_dir, filename+'.pcap.cracked') ):
85+
with open(os.path.join(handshake_dir, filename+'.pcap.cracked'), 'w') as f:
86+
f.write(password)
87+
except Exception:
88+
logging.exception("WPA_SEC: Exception writing cracked single file.")
89+
90+
logging.info("WPA_SEC: Wrote cracked single files.")
6891

6992
def on_loaded(self):
7093
"""
7194
Gets called when the plugin gets loaded
7295
"""
7396
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):
74-
logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org")
97+
logging.error("WPA_SEC: API-KEY isn't set. Can't upload.")
7598
return
7699

77100
if 'api_url' not in self.options or ('api_url' in self.options and not self.options['api_url']):
78-
logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.")
101+
logging.error("WPA_SEC: API-URL isn't set. Can't upload.")
79102
return
80103

81104
if 'whitelist' not in self.options:
82105
self.options['whitelist'] = list()
83106

84107
self.ready = True
85-
logging.info("WPA_SEC: plugin loaded")
108+
logging.info("WPA_SEC: plugin loaded.")
86109

87110
def on_webhook(self, path, request):
88111
from flask import make_response, redirect
@@ -109,35 +132,31 @@ def on_internet_available(self, agent):
109132
handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
110133

111134
if handshake_new:
112-
logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes to wpa-sec.stanev.org")
135+
logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes...")
113136
for idx, handshake in enumerate(handshake_new):
114-
display.on_uploading(f"wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})")
137+
display.on_uploading(f"WPA-SEC ({idx + 1}/{len(handshake_new)})")
115138

116139
try:
117140
self._upload_to_wpasec(handshake)
118141
reported.append(handshake)
119142
self.report.update(data={'reported': reported})
120-
logging.debug("WPA_SEC: Successfully uploaded %s", handshake)
121-
except requests.exceptions.RequestException as req_e:
122-
self.skip.append(handshake)
123-
logging.debug("WPA_SEC: %s", req_e)
124-
continue
125-
except OSError as os_e:
126-
logging.debug("WPA_SEC: %s", os_e)
127-
continue
143+
except Exception:
144+
logging.exception("WPA_SEC: Exception uploading %s.", handshake)
128145

129146
display.on_normal()
130147

131148
if 'download_results' in self.options and self.options['download_results']:
132-
cracked_file = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')
133-
if os.path.exists(cracked_file):
134-
last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file))
135-
if last_check is not None and ((datetime.now() - last_check).seconds / (60 * 60)) < 1:
149+
cracked_file_path = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')
150+
151+
if os.path.exists(cracked_file_path):
152+
last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file_path))
153+
download_interval = int(self.options.get('download_interval', 3600))
154+
if last_check is not None and ((datetime.now() - last_check).seconds / download_interval) < 1:
136155
return
156+
137157
try:
138-
self._download_from_wpasec(os.path.join(handshake_dir, 'wpa-sec.cracked.potfile'))
139-
logging.info("WPA_SEC: Downloaded cracked passwords.")
140-
except requests.exceptions.RequestException as req_e:
141-
logging.debug("WPA_SEC: %s", req_e)
142-
except OSError as os_e:
143-
logging.debug("WPA_SEC: %s", os_e)
158+
self._download_from_wpasec(cracked_file_path)
159+
if 'single_files' in self.options and self.options['single_files']:
160+
self._write_cracked_single_files(cracked_file_path, handshake_dir)
161+
except Exception:
162+
logging.exception("WPA_SEC: Exception downloading results.")

0 commit comments

Comments
 (0)