├── my-cracked.txt ├── assets ├── colab.png ├── injecting.png └── pwnBjornCoop.png ├── .gitignore ├── README.md ├── actions └── WpaSecHarvester.py └── BjornWpaSecHarvester.py /my-cracked.txt: -------------------------------------------------------------------------------- 1 | SSID:PASSWORD -------------------------------------------------------------------------------- /assets/colab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LOCOSP/BjornWpaSecHarvester/HEAD/assets/colab.png -------------------------------------------------------------------------------- /assets/injecting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LOCOSP/BjornWpaSecHarvester/HEAD/assets/injecting.png -------------------------------------------------------------------------------- /assets/pwnBjornCoop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LOCOSP/BjornWpaSecHarvester/HEAD/assets/pwnBjornCoop.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | networks.txt 3 | wpa-sec.founds.potfile 4 | networks_done.txt 5 | .DS_Store 6 | actions/wpa_sec_harvesting.py 7 | config/actions.json 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pwnagotchi Cyber Viking Integration 2 | 3 | Welcome to the Pwnagotchi Cyber Viking Cooperation project! 🚀 This script is designed to download and process Wi-Fi network data caputered by Pwnagotchi, processed with wpa-sec, and inject the networks into your Bjorn system. Additionally, it integrates with Discord to send the collected networks to your server! 🌐 4 | Optional ofcourse 🤓. 5 | 6 | ![Pwnagotch&BjornCoop](/assets/colab.png) 7 | 8 | But that's not all—prepare yourself for an epic journey as Pwnagotchi teams up with the legendary cyber Viking, **Bjorn**, to hack the planet together! ⚔️ 9 | 10 | ## Features 11 | - Downloads Wi-Fi network data from [wpa-sec.stanev.org](https://wpa-sec.stanev.org/). 12 | - Processes the data and removes duplicate networks. 13 | - Sends the unique networks to Discord using a webhook (optional). 14 | - Injects the networks into your system with `nmcli`. 15 | - Uses environment variables for configuration. 16 | - If you already know the network you want to test just put it's SSID and PASSWORD in order: `SSID:PASSWORD` 17 | and run the script. 18 | 19 | ## Screens 20 | 21 | ![processing](/assets/injecting.png) 22 | 23 | 24 | ## Installation 25 | 26 | ### Requirements 27 | - Python 3.x 28 | - `requests` library 29 | - `python-dotenv` library 30 | - Bjorn with Internet connection 31 | 32 | ### Steps to Install 33 | 34 | 1. Install required Python libraries: 35 | 36 | 64bit OS 37 | 38 | ```bash 39 | sudo apt update && sudo apt install python3-dotenv -y 40 | ``` 41 | 42 | 32bit OS 43 | 44 | ```bash 45 | sudo apt update && sudo pip install python-dotenv --break-system-packages 46 | ``` 47 | 48 | 2. Clone the repository: 49 | ```bash 50 | git clone https://github.com/LOCOSP/BjornWpaSecHarvester.git 51 | cd BjornWpaSecHarvester 52 | ``` 53 | 54 | 55 | 3. Create a `.env` file in the root directory of the project. 56 | 57 | ```bash 58 | nano .env 59 | ``` 60 | 61 | 4. Populate the `.env` file with the following variables: 62 | 63 | ```env 64 | COOKIE_VALUE= #wpa-sec key here 65 | URL=https://wpa-sec.stanev.org/?api&dl=1 66 | DISCORD_WEBHOOK_URL= #discord webhook here 67 | 68 | #if you don't want to use wpa-sec service leave it blank but than my-cracked.txt required. 69 | #if you don't want to use discord webhooks leave it blank. 70 | ``` 71 | 72 | Make sure to replace the placeholders with your actual values: 73 | - `COOKIE_VALUE`: The value of [wpa-sec Key](https://wpa-sec.stanev.org/?get_key) . 74 | - `URL`: The URL from which to download the Wi-Fi network data. 75 | - `DISCORD_WEBHOOK_URL`: The Discord webhook URL to send the processed file. 76 | 77 | 78 | 5. **`my-cracked.txt`** file is needed if you are not using wpa-sec service. 79 | 80 | `nano my-cracked.txt` 81 | 82 | enter your known networks in format: 83 | ``` 84 | SSID:PASSWORD 85 | SSID:PASSWORD 86 | ``` 87 | 88 | 6. You're ready to go! Run the script: 89 | 90 | ```bash 91 | python3 BjornWpaSecHarvester.py 92 | ``` 93 | 94 | ## Note 95 | 96 | - Ensure that `nmcli` is installed and available on your system. This tool is used to inject the networks into your Wi-Fi configuration. 97 | 98 | ## Bjorn's Blessing 🏰⚔️ 99 | 100 | You've just embarked on a journey of great conquest! Pwnagotchi and Bjorn, the cyber Viking, have joined forces to bring you the most secure and automated Wi-Fi network processing system! With their powers combined, no Wi-Fi network is safe from their reach! ⚡ 101 | 102 | Get ready for a legendary partnership where Pwnagotchi handles the tech and Bjorn handles... well, everything else! 😎 103 | 104 | Let the **cyber Viking revolution** begin! 🔥 105 | 106 | ### Contributions 107 | 108 | Feel free to contribute to the project! Open an issue or submit a pull request if you have any improvements or fixes to share. 109 | 110 | --- 111 | 112 | Bjorn and Pwnagotchi wish you smooth and secure network hunting! 🛡️🌍 113 | -------------------------------------------------------------------------------- /actions/WpaSecHarvester.py: -------------------------------------------------------------------------------- 1 | from rich.console import Console 2 | from urllib.request import Request, urlopen 3 | from dotenv import load_dotenv 4 | import os 5 | import requests 6 | import time 7 | import shutil 8 | import subprocess 9 | 10 | 11 | b_class = "WpaSecHarvesting" 12 | b_module = "wpa_sec_harvesting" 13 | b_status = "wpa_sec_harvesting_action" 14 | b_port = 0 15 | b_parent = None 16 | 17 | class WpaSecHarvesting: 18 | def __init__(self, shared_data): 19 | self.shared_data = shared_data 20 | self.console = Console() 21 | 22 | def execute(self): 23 | try: 24 | self.console.log(f"Starting action: {b_status}") 25 | self.download_and_process_file() 26 | self.process_networks() 27 | self.console.log(f"Action '{b_status}' completed successfully!") 28 | except Exception as e: 29 | self.console.log(f"Error in action '{b_status}': {e}", style="bold red") 30 | 31 | def download_and_process_file(self): 32 | self.console.log("Downloading and processing WPA-SEC file...") 33 | # Load variables from the .env file 34 | load_dotenv() 35 | 36 | # Read values from the .env file 37 | cookie_value = os.getenv('COOKIE_VALUE') 38 | url = os.getenv('URL') 39 | discord_webhook_url = os.getenv('DISCORD_WEBHOOK_URL') 40 | 41 | # Create a request with a cookie header 42 | req = Request(url, headers={'Cookie': f'key={cookie_value}'}) 43 | 44 | # Download the file 45 | with urlopen(req) as response, open('wpa-sec.founds.potfile', 'wb') as out_file: 46 | out_file.write(response.read()) 47 | 48 | print("File downloaded successfully.") 49 | 50 | # Read the data from the downloaded file wpa-sec.founds.potfile with proper encoding (utf-8) 51 | with open("wpa-sec.founds.potfile", "r", encoding="utf-8") as potfile: 52 | lines = potfile.readlines() 53 | 54 | # Store unique networks (SSID + password) 55 | unique_networks = set() 56 | 57 | # Process lines from wpa-sec.founds.potfile 58 | for line in lines: 59 | # Split the line by the ":" separator 60 | parts = line.strip().split(":") 61 | 62 | # Skip lines with fewer than 4 parts 63 | if len(parts) < 4: 64 | continue 65 | 66 | # Retain the last two parts (SSID + password) 67 | network_info = f"{parts[2]}:{parts[3]}" 68 | 69 | # Add to the set of unique networks 70 | unique_networks.add(network_info) 71 | 72 | # Try to read data from my-cracked.txt and add to the set of unique networks 73 | try: 74 | with open("my-cracked.txt", "r", encoding="utf-8") as cracked_file: 75 | cracked_lines = cracked_file.readlines() 76 | 77 | for line in cracked_lines: 78 | # Split the line by the ":" separator 79 | network_info = line.strip() 80 | 81 | # Add to the set of unique networks 82 | unique_networks.add(network_info) 83 | except FileNotFoundError: 84 | print("File my-cracked.txt not found. Continuing without it.") 85 | 86 | # Save unique networks to networks.txt with utf-8 encoding 87 | with open("networks.txt", "w", encoding="utf-8") as output_file: 88 | for network in sorted(unique_networks): 89 | output_file.write(f"{network}\n") 90 | 91 | print("Duplicates removed and data saved to networks.txt.") 92 | 93 | # Send the networks.txt file to Discord using the webhook 94 | with open("networks.txt", "rb") as file: 95 | response = requests.post(discord_webhook_url, files={"file": file}) 96 | 97 | if response.status_code == 204: 98 | print("File networks.txt has been sent to Discord.") 99 | else: 100 | print(f"Failed to send the file. Error code: {response.status_code}") 101 | 102 | def process_networks(self): 103 | self.console.log("Processing networks...") 104 | input_file = "networks.txt" 105 | done_file = "networks_done.txt" 106 | 107 | if not shutil.which("nmcli"): 108 | print("nmcli is not installed. Please install it and try again.") 109 | return 110 | 111 | try: 112 | with open(input_file, "r") as f: 113 | all_networks = set(line.strip() for line in f if line.strip()) 114 | except FileNotFoundError: 115 | print(f"I can't find the {input_file} file.") 116 | return 117 | 118 | try: 119 | with open(done_file, "r") as f: 120 | processed_networks = set(line.strip() for line in f if line.strip()) 121 | except FileNotFoundError: 122 | processed_networks = set() 123 | 124 | new_networks = all_networks - processed_networks 125 | 126 | if not new_networks: 127 | print("No new networks found to process.") 128 | return 129 | 130 | # Get the default Wi-Fi interface 131 | try: 132 | result = subprocess.run( 133 | ["nmcli", "-t", "-f", "DEVICE,TYPE", "device", "status"], 134 | capture_output=True, 135 | text=True, 136 | check=True 137 | ) 138 | wifi_device = next( 139 | (line.split(":")[0] for line in result.stdout.splitlines() if "wifi" in line), 140 | None 141 | ) 142 | if not wifi_device: 143 | print("No Wi-Fi device found.") 144 | return 145 | except subprocess.CalledProcessError as e: 146 | print(f"Error while detecting Wi-Fi device: {e}") 147 | return 148 | 149 | for network in new_networks: 150 | try: 151 | ssid, password = network.split(":") 152 | 153 | # Check if the network already exists 154 | existing_connections = subprocess.run( 155 | ["nmcli", "-t", "-f", "NAME", "connection", "show"], 156 | capture_output=True, 157 | text=True 158 | ) 159 | if ssid in existing_connections.stdout.splitlines(): 160 | print(f"Network '{ssid}' already exists. Modifying...") 161 | command = ( 162 | f'sudo nmcli connection modify "{ssid}" wifi-sec.key-mgmt wpa-psk ' 163 | f'wifi-sec.psk "{password}" connection.autoconnect yes' 164 | ) 165 | else: 166 | print(f"Adding new network '{ssid}'...") 167 | command = ( 168 | f'sudo nmcli connection add type wifi ifname "{wifi_device}" con-name "{ssid}" ssid "{ssid}" ' 169 | f'&& sudo nmcli connection modify "{ssid}" wifi-sec.key-mgmt wpa-psk ' 170 | f'wifi-sec.psk "{password}" connection.autoconnect yes' 171 | ) 172 | 173 | subprocess.run(command, shell=True, check=True) 174 | time.sleep(1) # Delay between commands 175 | except ValueError: 176 | print(f"Invalid line format: {network}") 177 | except subprocess.CalledProcessError as e: 178 | print(f"Error while executing command: {e}") 179 | 180 | shutil.copyfile(input_file, done_file) 181 | print(f"Copy of the file saved under the name: {done_file}") 182 | -------------------------------------------------------------------------------- /BjornWpaSecHarvester.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import time 4 | import subprocess 5 | from urllib.request import Request, urlopen 6 | import requests 7 | from dotenv import load_dotenv 8 | import shutil # Required for file operations like copyfile 9 | 10 | # Constants for file names 11 | POTFILE = os.getenv("POTFILE", "wpa-sec.founds.potfile") 12 | CRACKED_FILE = os.getenv("CRACKED_FILE", "my-cracked.txt") 13 | NETWORKS_FILE = os.getenv("NETWORKS_FILE", "networks.txt") 14 | DONE_FILE = os.getenv("DONE_FILE", "networks_done.txt") 15 | 16 | # Setup logging 17 | logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") 18 | logger = logging.getLogger(__name__) 19 | 20 | def download_file(url, cookie_value, output_file): 21 | """ 22 | Downloads a file from the given URL using a cookie for authentication. 23 | """ 24 | try: 25 | logger.info("Starting file download...") 26 | req = Request(url, headers={'Cookie': f'key={cookie_value}'}) 27 | with urlopen(req) as response, open(output_file, "wb") as out_file: 28 | out_file.write(response.read()) 29 | logger.info(f"File {output_file} downloaded successfully.") 30 | except Exception as e: 31 | logger.error(f"Error downloading file {output_file}: {e}") 32 | raise 33 | 34 | def process_potfile(input_file, unique_networks): 35 | """ 36 | Processes the potfile and extracts unique networks. 37 | """ 38 | try: 39 | with open(input_file, "r", encoding="utf-8") as potfile: 40 | lines = potfile.readlines() 41 | 42 | for line in lines: 43 | parts = line.strip().split(":") 44 | if len(parts) >= 4: 45 | unique_networks.add(":".join(parts[2:4])) 46 | logger.info(f"Processed {input_file} and extracted unique networks.") 47 | except FileNotFoundError: 48 | logger.warning(f"File {input_file} not found. Skipping processing.") 49 | except UnicodeDecodeError as e: 50 | logger.error(f"Error decoding {input_file}: {e}") 51 | 52 | def process_cracked_file(input_file, unique_networks): 53 | """ 54 | Adds networks from the cracked file to the set of unique networks. 55 | """ 56 | try: 57 | with open(input_file, "r", encoding="utf-8") as cracked_file: 58 | lines = cracked_file.readlines() 59 | 60 | for line in lines: 61 | unique_networks.add(line.strip()) 62 | logger.info(f"Processed {input_file} and added networks to the set.") 63 | except FileNotFoundError: 64 | logger.warning(f"File {input_file} not found. Continuing without it.") 65 | 66 | def save_unique_networks(output_file, unique_networks): 67 | """ 68 | Saves the set of unique networks to a file. 69 | """ 70 | try: 71 | with open(output_file, "w", encoding="utf-8") as output: 72 | for network in sorted(unique_networks): 73 | output.write(f"{network}\n") 74 | logger.info(f"Processing {output_file}") 75 | except OSError as e: 76 | logger.error(f"Error saving {output_file}: {e}") 77 | 78 | def send_to_discord(webhook_url, file_path): 79 | """ 80 | Sends a file to Discord using a webhook URL. 81 | """ 82 | try: 83 | with open(file_path, "rb") as file: 84 | response = requests.post(webhook_url, files={"file": file}) 85 | if response.status_code == 204: 86 | logger.info(f"File {file_path} successfully sent to Discord.") 87 | else: 88 | logger.warning(f"Failed to send {file_path}. HTTP status code: {response.status_code}") 89 | except Exception as e: 90 | logger.error(f"Error sending file {file_path} to Discord: {e}") 91 | 92 | def manage_networks(input_file, done_file): 93 | """Adds new networks to the Wi-Fi configuration using nmcli.""" 94 | if not shutil.which("nmcli"): 95 | logger.error("nmcli is not installed. Please install it and try again.") 96 | return 97 | 98 | try: 99 | with open(input_file, "r") as f: 100 | all_networks = set(line.strip() for line in f if line.strip()) 101 | except FileNotFoundError: 102 | logger.error(f"Input file {input_file} not found.") 103 | return 104 | 105 | try: 106 | with open(done_file, "r") as f: 107 | processed_networks = set(line.strip() for line in f if line.strip()) 108 | except FileNotFoundError: 109 | processed_networks = set() 110 | 111 | new_networks = all_networks - processed_networks 112 | 113 | if not new_networks: 114 | logger.info("No new unique networks found. All networks have already been processed.") 115 | return 116 | 117 | # Zapisywanie nowych sieci, jeśli istnieją 118 | try: 119 | with open(done_file, "a") as f: 120 | for network in new_networks: 121 | f.write(f"{network}\n") 122 | logger.info(f"Unique networks saved to {done_file}.") 123 | except IOError as e: 124 | logger.error(f"Failed to save unique networks to {done_file}: {e}") 125 | return 126 | 127 | try: 128 | result = subprocess.run( 129 | ["nmcli", "-t", "-f", "DEVICE,TYPE", "device", "status"], 130 | capture_output=True, 131 | text=True, 132 | check=True 133 | ) 134 | wifi_device = next( 135 | (line.split(":")[0] for line in result.stdout.splitlines() if "wifi" in line), 136 | None 137 | ) 138 | if not wifi_device: 139 | logger.error("No Wi-Fi device found.") 140 | return 141 | except subprocess.CalledProcessError as e: 142 | logger.error(f"Error while detecting Wi-Fi device: {e}") 143 | return 144 | 145 | for network in new_networks: 146 | try: 147 | ssid, password = network.split(":") 148 | if not (8 <= len(password) <= 63): 149 | logger.warning(f"Skipping network {ssid}: Password must be 8-63 characters long.") 150 | continue 151 | command = [ 152 | "sudo", "nmcli", "connection", "add", "type", "wifi", "ifname", wifi_device, 153 | "con-name", ssid, "ssid", ssid, "wifi-sec.key-mgmt", "wpa-psk", "wifi-sec.psk", password, 154 | "connection.autoconnect", "yes" 155 | ] 156 | subprocess.run(command, check=True) 157 | time.sleep(1) 158 | except ValueError: 159 | logger.warning(f"Invalid line format in network: {network}") 160 | except subprocess.CalledProcessError as e: 161 | logger.error(f"Error adding network {network}: {e}") 162 | 163 | 164 | 165 | def main(): 166 | load_dotenv() # Load environment variables from .env file 167 | logger.info("The gates to Valhalla have been opened, let the Harvester begin...") 168 | # Read environment variables 169 | cookie_value = os.getenv("COOKIE_VALUE", "") 170 | url = os.getenv("URL", "") 171 | discord_webhook_url = os.getenv("DISCORD_WEBHOOK_URL", "") 172 | 173 | unique_networks = set() 174 | 175 | try: 176 | if url and cookie_value: 177 | download_file(url, cookie_value, POTFILE) 178 | process_potfile(POTFILE, unique_networks) 179 | process_cracked_file(CRACKED_FILE, unique_networks) 180 | save_unique_networks(NETWORKS_FILE, unique_networks) 181 | if discord_webhook_url: 182 | send_to_discord(discord_webhook_url, NETWORKS_FILE) 183 | manage_networks(NETWORKS_FILE, DONE_FILE) 184 | except Exception as e: 185 | logger.error(f"An error occurred: {e}") 186 | 187 | if __name__ == "__main__": 188 | main() 189 | --------------------------------------------------------------------------------