Added Hashcat library, PMKID is persisted in ./hs/ and re-used
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -34,11 +34,8 @@ 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)
|
||||||
|
handshake = self.capture_handshake()
|
||||||
# Capture the handshake ("do it live!")
|
|
||||||
if handshake is None:
|
|
||||||
handshake = self.capture_handshake()
|
|
||||||
|
|
||||||
if handshake is None:
|
if handshake is None:
|
||||||
# Failed to capture handshake
|
# Failed to capture handshake
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
128
wifite/tools/hashcat.py
Normal 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
|
||||||
Reference in New Issue
Block a user