Cleaning up PMKID attack.

This commit is contained in:
derv82
2018-08-15 15:58:59 -07:00
parent 936230dd50
commit dd7e93666a
6 changed files with 93 additions and 131 deletions

View File

@@ -3,6 +3,7 @@
from ..model.attack import Attack from ..model.attack import Attack
from ..config import Configuration from ..config import Configuration
from ..tools.hashcat import HcxDumpTool, HcxPcapTool, Hashcat
from ..util.color import Color from ..util.color import Color
from ..util.process import Process from ..util.process import Process
from ..util.timer import Timer from ..util.timer import Timer
@@ -11,155 +12,105 @@ from ..model.pmkid_result import CrackResultPMKID
from threading import Thread from threading import Thread
import os import os
import time import time
import re
'''
TODO:
1. Rename AttackWPA to AttackWpaHandshake
2. Rename AttackWPS to AttackWpaPixie
3. Rename AttackPMKID to AttackWpaPMKID
4. Use AttackWPA to try any or all of the above depending on config/args.
Greatly simplifies the attack loop in wifite.py
Or consolidate all attacks under "Attack" which selects the correct attack based on the target.
Might be hard to capture KeyboardInterrupt, but we can always propagate it.
Could move a ton of stuff in wifite.py (display_cracked, check_handshake) to a 'util'
'''
class AttackPMKID(Attack): class AttackPMKID(Attack):
def __init__(self, target): def __init__(self, target):
super(AttackPMKID, self).__init__(target) super(AttackPMKID, self).__init__(target)
self.crack_result = None self.crack_result = None
self.success = False self.success = False
self.pcapng_output = Configuration.temp('pmkid.pcapng') self.pcapng_file = Configuration.temp('pmkid.pcapng')
self.pmkid_output = Configuration.temp('pmkid.16800')
def run(self): def run(self):
# TODO: Check cracked.txt for previously-captured PMKID, skip to crack if found. # TODO: Check ./hs/ for previously-captured PMKID, skip to crack if found.
self.keep_capturing = True self.keep_capturing = True
self.timer = Timer(60) self.timer = Timer(60)
# Start hcxdumptool # Start hcxdumptool
t = Thread(target=self.hcxdump_thread) t = Thread(target=self.dumptool_thread)
t.start() t.start()
# Regularly check hcxpcaptool output for hash for self.target.essid # Repeatedly run pcaptool & check output for hash for self.target.essid
pmkid_hash = None pmkid_hash = None
pcaptool = HcxPcapTool(self.target)
while self.timer.remaining() > 0: while self.timer.remaining() > 0:
pmkid_hash = self.get_pmkid_hash() pmkid_hash = pcaptool.get_pmkid_hash(self.pcapng_file)
if pmkid_hash is not None: if pmkid_hash is not None:
# Got PMKID break # Got PMKID
break
Color.pattack('PMKID', self.target, 'capture', Color.pattack('PMKID', self.target, 'CAPTURE',
'Waiting for PMKID ({C}%s{W})' % str(self.timer)) 'Waiting for PMKID ({C}%s{W})' % str(self.timer))
time.sleep(1) time.sleep(1)
if pmkid_hash is not None:
Color.pattack('PMKID', self.target, str(self.timer), 'Captured PMKID, cracking...')
# When hash is found, start cracking
if self.crack_pmkid(pmkid_hash):
self.success = True
self.keep_capturing = False self.keep_capturing = False
return self.success if pmkid_hash is None:
Color.pattack('PMKID', self.target, 'CAPTURE',
'{R}Failed{O} to capture PMKID.')
return False # No hash found.
def crack_pmkid(self, pmkid_hash): Color.pattack('PMKID', self.target, 'CAPTURE', '{G}Captured PMKID{W}')
# Write hash to file pmkid_file = self.save_pmkid(pmkid_hash)
# TODO: Should we write this to ./hs/ with .pmkid suffix?
hash_file = Configuration.temp('pmkid.hash')
with open(hash_file, 'w') as f:
f.write(pmkid_hash)
f.write('\n')
# Run hashcat # Check that wordlist exists before cracking.
command = [ if Configuration.wordlist is None:
'hashcat', Color.pl('\n{!} {O}Not cracking because {R}wordlist{O} is not set.')
'--force', Color.pl('{!} {O}Run Wifite with the {R}--crack{O} and {R}--dict{O} options to try again.')
'--quiet', key = None
'-m', '16800', else:
'-a', '0', # TODO: Configure Color.pattack('PMKID', self.target, 'CRACK', 'Cracking PMKID... ')
'-w', '2', # TODO: Configure key = Hashcat.crack_pmkid(pmkid_file)
hash_file,
Configuration.wordlist
]
# TODO: Check status of hashcat (%); it's impossible with --quiet if key is None:
# Failed to crack.
try: Color.pattack('PMKID', self.target, 'CRACK',
hashcat_proc = Process(command) '{R}Failed{O} to crack PMKID ')
hashcat_proc.wait()
stdout = hashcat_proc.stdout()
except KeyboardInterrupt: # In case user gets impatient
stdout = ''
if ':' not in stdout:
# Failed
Color.pattack('PMKID', self.target, '{R}failed', '{O}Failed to crack PMKID ')
Color.pl("") Color.pl("")
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid, pmkid_hash, None)
#self.crack_result.dump()
return False return False
else: else:
# Update Crack Result # Successfully cracked.
key = stdout.strip().split(':', 1)[1] Color.pattack('PMKID', self.target, '',
Color.pl("\n\n{+} Cracked PMKID! Key: {G}%s{W}\n" % key) '{C}Cracked PMKID. Key: {G}%s{W}' % key)
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid, pmkid_hash, key) Color.pl("")
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid,
pmkid_hash, pmkid_file, key)
self.crack_result.dump() self.crack_result.dump()
self.success = True
return True return True
def get_pmkid_hash(self):
if os.path.exists(self.pmkid_output):
os.remove(self.pmkid_output)
command = [ def dumptool_thread(self):
'hcxpcaptool', dumptool = HcxDumpTool(self.target, self.pcapng_file)
'-z', self.pmkid_output,
self.pcapng_output
]
hcxpcap_proc = Process(command)
hcxpcap_proc.wait()
if not os.path.exists(self.pmkid_output):
return None
with open(self.pmkid_output, 'r') as f:
output = f.read()
# Check that we got the right target
# pmkid_hash*bssid*station_mac*essid(hex)
for line in output.split('\n'):
fields = line.split('*')
if len(fields) < 3:
continue
if fields[1].lower() != self.target.bssid.lower().replace(':', ''):
continue
output = line
break
os.remove(self.pmkid_output)
return output
def hcxdump_thread(self):
# Create filterlist
filterlist = Configuration.temp('pmkid.filterlist')
with open(filterlist, 'w') as filter_file:
filter_file.write(self.target.bssid.replace(':', ''))
if os.path.exists(self.pcapng_output):
os.remove(self.pcapng_output)
command = [
"hcxdumptool",
"-i", Configuration.interface,
"--filterlist", filterlist,
"--filtermode", "2",
"-c", self.target.channel,
"-o", self.pcapng_output
]
hcxdump_proc = Process(command)
# Let the dump tool run until we have the hash. # Let the dump tool run until we have the hash.
while self.keep_capturing and hcxdump_proc.poll() == None: while self.keep_capturing and dumptool.poll() == None:
time.sleep(0.5) time.sleep(0.5)
hcxdump_proc.interrupt() dumptool.interrupt()
def save_pmkid(self, pmkid_hash):
'''
Saves a copy of the pmkid (handshake) to hs/
'''
# Create handshake dir
if not os.path.exists(Configuration.wpa_handshake_dir):
os.mkdir(Configuration.wpa_handshake_dir)
# Generate filesystem-safe filename from bssid, essid and date
essid_safe = re.sub('[^a-zA-Z0-9]', '', self.target.essid)
bssid_safe = self.target.bssid.replace(':', '-')
date = time.strftime('%Y-%m-%dT%H-%M-%S')
pmkid_file = 'pmkid_%s_%s_%s.16800' % (essid_safe, bssid_safe, date)
pmkid_file = os.path.join(Configuration.wpa_handshake_dir, pmkid_file)
Color.p('\n{+} Saving copy of {C}PMKID Hash{W} to {C}%s{W} ' % pmkid_file)
with open(pmkid_file, 'w') as pmkid_handle:
pmkid_handle.write(pmkid_hash)
pmkid_handle.write('\n')
Color.pl('{G}saved{W}')
return pmkid_file

View File

@@ -5,11 +5,12 @@ from ..util.color import Color
from .result import CrackResult from .result import CrackResult
class CrackResultPMKID(CrackResult): class CrackResultPMKID(CrackResult):
def __init__(self, bssid, essid, pmkid_hash, key): def __init__(self, bssid, essid, pmkid_hash, pmkid_file, key):
self.result_type = 'PMKID' self.result_type = 'PMKID'
self.bssid = bssid self.bssid = bssid
self.essid = essid self.essid = essid
self.pmkid_hash = pmkid_hash self.pmkid_hash = pmkid_hash
self.pmkid_file = pmkid_file
self.key = key self.key = key
super(CrackResultPMKID, self).__init__() super(CrackResultPMKID, self).__init__()
@@ -24,7 +25,10 @@ class CrackResultPMKID(CrackResult):
('Encryption'.rjust(19), self.result_type)) ('Encryption'.rjust(19), self.result_type))
if self.pmkid_hash: if self.pmkid_hash:
Color.pl('{+} %s: {C}%s{W}' % Color.pl('{+} %s: {C}%s{W}' %
('PMKID'.rjust(19), self.pmkid_hash)) ('PMKID Hash'.rjust(19), self.pmkid_hash))
if self.pmkid_file:
Color.pl('{+} %s: {C}%s{W}' %
('PMKID File'.rjust(19), self.pmkid_file))
if self.key: if self.key:
Color.pl('{+} %s: {G}%s{W}' % ('PSK (password)'.rjust(19), self.key)) Color.pl('{+} %s: {G}%s{W}' % ('PSK (password)'.rjust(19), self.key))
else: else:
@@ -37,14 +41,15 @@ class CrackResultPMKID(CrackResult):
'essid' : self.essid, 'essid' : self.essid,
'bssid' : self.bssid, 'bssid' : self.bssid,
'key' : self.key, 'key' : self.key,
'pmkid' : self.pmkid_hash 'pmkid_hash' : self.pmkid_hash,
'pmkid_file' : self.pmkid_file
} }
if __name__ == '__main__': if __name__ == '__main__':
w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'abc*def*ghi*jkl', 'abcd1234') w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'abc*def*ghi*jkl', 'hs/pmkid_blah-123213.16800', 'abcd1234')
w.dump() w.dump()
w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'abc*def*ghi*jkl', 'Key') w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'abc*def*ghi*jkl', 'hs/pmkid_blah-123213.16800', 'Key')
print('\n') print('\n')
w.dump() w.dump()
w.save() w.save()

View File

@@ -94,7 +94,8 @@ class CrackResult(object):
from .pmkid_result import CrackResultPMKID from .pmkid_result import CrackResultPMKID
result = CrackResultPMKID(json['bssid'], result = CrackResultPMKID(json['bssid'],
json['essid'], json['essid'],
json['pmkid'], json['pmkid_hash'],
json['pmkid_file'],
json['key']) json['key'])
result.date = json['date'] result.date = json['date']
return result return result

View File

@@ -137,7 +137,7 @@ class Airodump(Dependency):
if fil.startswith('replay_') and fil.endswith('.cap') or fil.endswith('.xor'): if fil.startswith('replay_') and fil.endswith('.cap') or fil.endswith('.xor'):
os.remove(os.path.join(temp_dir, fil)) os.remove(os.path.join(temp_dir, fil))
def get_targets(self, apply_filter=True): def get_targets(self, old_targets=[], apply_filter=True):
''' Parses airodump's CSV file, returns list of Targets ''' ''' Parses airodump's CSV file, returns list of Targets '''
# Find the .CSV file # Find the .CSV file
@@ -150,13 +150,17 @@ class Airodump(Dependency):
return self.targets # No file found return self.targets # No file found
targets = Airodump.get_targets_from_csv(csv_filename) targets = Airodump.get_targets_from_csv(csv_filename)
for old_target in old_targets:
for target in targets:
if old_target.bssid == target.bssid:
target.wps = old_target.wps
# Check targets for WPS # Check targets for WPS
if not self.skip_wps: if not self.skip_wps:
capfile = csv_filename[:-3] + 'cap' capfile = csv_filename[:-3] + 'cap'
try: try:
Tshark.check_for_wps_and_update_targets(capfile, targets) Tshark.check_for_wps_and_update_targets(capfile, targets)
except Exception as e: except ValueError:
# No tshark, or it failed. Fall-back to wash # No tshark, or it failed. Fall-back to wash
Wash.check_for_wps_and_update_targets(capfile, targets) Wash.check_for_wps_and_update_targets(capfile, targets)
@@ -178,9 +182,6 @@ class Airodump(Dependency):
new_target.decloaked = True new_target.decloaked = True
self.decloaked_bssids.add(new_target.bssid) self.decloaked_bssids.add(new_target.bssid)
if self.pid.poll() is not None:
raise Exception('Airodump has stopped')
self.targets = targets self.targets = targets
self.deauth_hidden_targets() self.deauth_hidden_targets()

View File

@@ -161,7 +161,7 @@ class Tshark(Dependency):
''' '''
if not Tshark.exists(): if not Tshark.exists():
raise Exception('Cannot detect WPS networks: Tshark does not exist') raise ValueError('Cannot detect WPS networks: Tshark does not exist')
command = [ command = [
'tshark', 'tshark',

View File

@@ -36,15 +36,18 @@ class Scanner(object):
while True: while True:
if airodump.pid.poll() is not None: if airodump.pid.poll() is not None:
# Airodump process died # Airodump process died
self.err_msg = '\r{!} {R}Airodump exited unexpectedly (Code: %d){O} Command: {W}%s' % (airodump.pid.poll(), " ".join(airodump.pid.command)) return
raise KeyboardInterrupt
self.targets = airodump.get_targets() self.targets = airodump.get_targets(old_targets=self.targets)
if self.found_target(): if self.found_target():
# We found the target we want # We found the target we want
return return
if airodump.pid.poll() is not None:
# Airodump process died
return
for target in self.targets: for target in self.targets:
if target.bssid in airodump.decloaked_bssids: if target.bssid in airodump.decloaked_bssids:
target.decloaked = True target.decloaked = True
@@ -69,6 +72,7 @@ class Scanner(object):
return return
sleep(1) sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
@@ -193,7 +197,7 @@ class Scanner(object):
input_str += ' or {G}all{W}: ' input_str += ' or {G}all{W}: '
chosen_targets = [] chosen_targets = []
for choice in raw_input(Color.s(input_str)).split(','): for choice in raw_input(Color.s(input_str)).split(','):
choice = choice.strip() choice = choice.strip()
if choice.lower() == 'all': if choice.lower() == 'all':