2.1.9: --pmkid option, cleaned up --cracked, other bug fixes.
PMKID: * `--pmkid` option only attacks WPA networks with PMKID capture + crack * Decreased PMKID capture time from 60 seconds to 15 seconds. * Ignores PMKID attack if `--wps-only` is set. WPS: * Ctrl+C while waiting for `bully` to fetch PSK = remembers PIN, PSK is unknown. Misc: * `--cracked` prints results on single lines (much easier to read) * Fixed typo when required dependencies are not found (closes #127)
This commit is contained in:
@@ -280,6 +280,12 @@ class Arguments(object):
|
||||
help=self._verbose('Time to wait before failing WPA attack (default: {G}%d sec{W})' % self.config.wpa_attack_timeout))
|
||||
wpa.add_argument('-wpat', help=argparse.SUPPRESS, action='store', dest='wpa_attack_timeout', type=int)
|
||||
|
||||
wpa.add_argument('--pmkid',
|
||||
'-pmkid',
|
||||
action='store_true',
|
||||
dest='use_pmkid_only',
|
||||
help=Color.s('ONLY use PMKID capture on WPA endpoints (default: {G}off{W})'))
|
||||
|
||||
wpa.add_argument('--new-hs',
|
||||
action='store_true',
|
||||
dest='ignore_old_handshakes',
|
||||
|
||||
@@ -67,7 +67,8 @@ class AttackAll(object):
|
||||
Color.pl('{!} {R}Error: {O}unable to attack: encryption not WEP or WPA')
|
||||
return
|
||||
|
||||
for attack in attacks:
|
||||
while len(attacks) > 0:
|
||||
attack = attacks.pop(0)
|
||||
try:
|
||||
result = attack.run()
|
||||
if result:
|
||||
@@ -77,7 +78,7 @@ class AttackAll(object):
|
||||
continue
|
||||
except KeyboardInterrupt:
|
||||
Color.pl('\n{!} {O}interrupted{W}\n')
|
||||
if not cls.user_wants_to_continue(targets_remaining, 1):
|
||||
if not cls.user_wants_to_continue(targets_remaining, len(attacks)):
|
||||
return False # Stop attacking other targets
|
||||
|
||||
if attack.success:
|
||||
|
||||
@@ -62,6 +62,12 @@ class AttackPMKID(Attack):
|
||||
Returns:
|
||||
True if handshake is captured. False otherwise.
|
||||
'''
|
||||
# Skip if user only wants to run PixieDust attack
|
||||
if Configuration.wps_only and self.target.wps:
|
||||
Color.pl('\r{!} {O}Skipping PMKID attack on {R}%s{O} because {R}--wps-only{O} is set{W}' % self.target.essid)
|
||||
self.success = False
|
||||
return False
|
||||
|
||||
from ..util.process import Process
|
||||
# Check that we have all hashcat programs
|
||||
dependencies = [
|
||||
@@ -103,7 +109,7 @@ class AttackPMKID(Attack):
|
||||
The PMKID hash (str) if found, otherwise None.
|
||||
'''
|
||||
self.keep_capturing = True
|
||||
self.timer = Timer(60)
|
||||
self.timer = Timer(15)
|
||||
|
||||
# Start hcxdumptool
|
||||
t = Thread(target=self.dumptool_thread)
|
||||
@@ -159,10 +165,11 @@ class AttackPMKID(Attack):
|
||||
|
||||
if key is None:
|
||||
# Failed to crack.
|
||||
Color.clear_entire_line()
|
||||
Color.pattack('PMKID', self.target, '{R}CRACK',
|
||||
'{R}Failed{O}: passphrase not found in dictionary.\n')
|
||||
Color.pl('')
|
||||
if Configuration.wordlist is not None:
|
||||
Color.clear_entire_line()
|
||||
Color.pattack('PMKID', self.target, '{R}CRACK',
|
||||
'{R}Failed {O}Passphrase not found in dictionary.\n')
|
||||
Color.pl('')
|
||||
return False
|
||||
else:
|
||||
# Successfully cracked.
|
||||
|
||||
@@ -26,9 +26,12 @@ class AttackWPA(Attack):
|
||||
def run(self):
|
||||
'''Initiates full WPA handshake capture attack.'''
|
||||
|
||||
if Configuration.use_pmkid_only:
|
||||
self.success = False
|
||||
return False
|
||||
# Skip if user only wants to run PixieDust attack
|
||||
if Configuration.wps_only and self.target.wps:
|
||||
Color.pl('\r{!} {O}--wps-only{R} set, ignoring WPA-handshake attack on {O}%s{W}' % self.target.essid)
|
||||
Color.pl('\r{!} {O}Skipping WPA-Handshake attack on {R}%s{O} because {R}--wps-only{O} is set{W}' % self.target.essid)
|
||||
self.success = False
|
||||
return self.success
|
||||
|
||||
@@ -110,7 +113,12 @@ class AttackWPA(Attack):
|
||||
handshake = Handshake(temp_file, bssid=bssid, essid=essid)
|
||||
if handshake.has_handshake():
|
||||
# We got a handshake
|
||||
Color.pl('\n\n{+} {G}successfully captured handshake{W}')
|
||||
Color.clear_entire_line()
|
||||
Color.pattack('WPA',
|
||||
airodump_target,
|
||||
'Handshake capture',
|
||||
'{G}Captured handshake{W}')
|
||||
Color.pl('')
|
||||
break
|
||||
|
||||
# There is no handshake
|
||||
|
||||
@@ -15,10 +15,14 @@ class AttackWPS(Attack):
|
||||
''' Run all WPS-related attacks '''
|
||||
|
||||
# Drop out if user specified to not use Reaver/Bully
|
||||
if Configuration.use_pmkid_only:
|
||||
self.success = False
|
||||
return False
|
||||
|
||||
if Configuration.no_wps:
|
||||
Color.pl('\r{!} {O}--no-wps{R} set, ignoring WPS attack on {O}%s{W}' % self.target.essid)
|
||||
self.success = False
|
||||
return self.success
|
||||
return False
|
||||
|
||||
if Configuration.use_bully:
|
||||
return self.run_bully()
|
||||
|
||||
@@ -8,7 +8,7 @@ from .tools.macchanger import Macchanger
|
||||
|
||||
class Configuration(object):
|
||||
''' Stores configuration variables and functions for Wifite. '''
|
||||
version = '2.1.8'
|
||||
version = '2.1.9'
|
||||
|
||||
initialized = False # Flag indicating config has been initialized
|
||||
temp_dir = None # Temporary directory
|
||||
@@ -79,6 +79,7 @@ class Configuration(object):
|
||||
cls.wpa_handshake_dir = 'hs' # Dir to store handshakes
|
||||
cls.wpa_strip_handshake = False # Strip non-handshake packets
|
||||
cls.ignore_old_handshakes = False # Always fetch a new handshake
|
||||
cls.use_pmkid_only = False # Only use PMKID Capture+Crack attack
|
||||
|
||||
# Default dictionary for cracking
|
||||
cls.wordlist = None
|
||||
@@ -227,6 +228,9 @@ class Configuration(object):
|
||||
if args.ignore_old_handshakes:
|
||||
cls.ignore_old_handshakes = True
|
||||
Color.pl('{+} {C}option:{W} will {O}ignore{W} existing handshakes (force capture)')
|
||||
if args.use_pmkid_only:
|
||||
cls.use_pmkid_only = True
|
||||
Color.pl('{+} {C}option:{W} will ONLY use {C}PMKID{W} attack on WPA networks')
|
||||
if args.wpa_handshake_dir:
|
||||
cls.wpa_handshake_dir = args.wpa_handshake_dir
|
||||
Color.pl('{+} {C}option:{W} will store handshakes to {G}%s{W}' % args.wpa_handshake_dir)
|
||||
|
||||
@@ -30,6 +30,13 @@ class CrackResultPMKID(CrackResult):
|
||||
else:
|
||||
Color.pl('{!} %s {O}key unknown{W}' % ''.rjust(19))
|
||||
|
||||
def print_single_line(self, longest_essid):
|
||||
self.print_single_line_prefix(longest_essid)
|
||||
Color.p('{G}%s{W}' % 'PMKID'.ljust(5))
|
||||
Color.p(' ')
|
||||
Color.p('Key: {G}%s{W}' % self.key)
|
||||
Color.pl('')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'type' : self.result_type,
|
||||
|
||||
@@ -15,6 +15,7 @@ class CrackResult(object):
|
||||
|
||||
def __init__(self):
|
||||
self.date = int(time.time())
|
||||
self.readable_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.date))
|
||||
|
||||
def dump(self):
|
||||
raise Exception('Unimplemented method: dump()')
|
||||
@@ -22,6 +23,19 @@ class CrackResult(object):
|
||||
def to_dict(self):
|
||||
raise Exception('Unimplemented method: to_dict()')
|
||||
|
||||
def print_single_line(self, longest_essid):
|
||||
raise Exception('Unimplemented method: print_single_line()')
|
||||
|
||||
def print_single_line_prefix(self, longest_essid):
|
||||
essid = self.essid if self.essid else 'N/A'
|
||||
Color.p('{W} ')
|
||||
Color.p('{C}%s{W}' % essid.ljust(longest_essid))
|
||||
Color.p(' ')
|
||||
Color.p('{GR}%s{W}' % self.bssid.ljust(17))
|
||||
Color.p(' ')
|
||||
Color.p('{D}%s{W}' % self.readable_date.ljust(19))
|
||||
Color.p(' ')
|
||||
|
||||
def save(self):
|
||||
''' Adds this crack result to the cracked file and saves it. '''
|
||||
name = CrackResult.cracked_file
|
||||
@@ -52,12 +66,32 @@ class CrackResult(object):
|
||||
|
||||
if len(cracked_targets) == 0:
|
||||
Color.pl('{!} {R}no results found in {O}%s{W}' % name)
|
||||
else:
|
||||
Color.pl('{+} displaying {G}%d {C}cracked target(s){W}\n' % len(cracked_targets))
|
||||
for item in cracked_targets:
|
||||
cr = cls.load(item)
|
||||
cr.dump()
|
||||
Color.pl('')
|
||||
return
|
||||
|
||||
Color.pl('\n{+} Displaying {G}%d{W} cracked target(s) from {C}%s{W}\n' % (
|
||||
len(cracked_targets), name))
|
||||
|
||||
results = sorted([cls.load(item) for item in cracked_targets], key=lambda x: x.date, reverse=True)
|
||||
longest_essid = max([len(result.essid or 'ESSID') for result in results])
|
||||
|
||||
# Header
|
||||
Color.p('{D} ')
|
||||
Color.p('ESSID'.ljust(longest_essid))
|
||||
Color.p(' ')
|
||||
Color.p('BSSID'.ljust(17))
|
||||
Color.p(' ')
|
||||
Color.p('DATE'.ljust(19))
|
||||
Color.p(' ')
|
||||
Color.p('TYPE'.ljust(5))
|
||||
Color.p(' ')
|
||||
Color.p('KEY')
|
||||
Color.pl('{D}')
|
||||
Color.p(' ' + '-' * (longest_essid + 17 + 19 + 5 + 11 + 12))
|
||||
Color.pl('{W}')
|
||||
# Results
|
||||
for result in results:
|
||||
result.print_single_line(longest_essid)
|
||||
Color.pl('')
|
||||
|
||||
|
||||
@classmethod
|
||||
@@ -97,6 +131,7 @@ class CrackResult(object):
|
||||
json['pmkid_file'],
|
||||
json['key'])
|
||||
result.date = json['date']
|
||||
result.readable_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(result.date))
|
||||
return result
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -24,6 +24,15 @@ class CrackResultWEP(CrackResult):
|
||||
if self.ascii_key:
|
||||
Color.pl('{+} Ascii Key: {G}%s{W}' % self.ascii_key)
|
||||
|
||||
def print_single_line(self, longest_essid):
|
||||
self.print_single_line_prefix(longest_essid)
|
||||
Color.p('{G}%s{W}' % 'WEP'.ljust(5))
|
||||
Color.p(' ')
|
||||
Color.p('Hex: {G}%s{W}' % self.hex_key.replace(':', ''))
|
||||
if self.ascii_key:
|
||||
Color.p(' (ASCII: {G}%s{W})' % self.ascii_key)
|
||||
Color.pl('')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'type' : self.result_type,
|
||||
|
||||
@@ -30,6 +30,13 @@ class CrackResultWPA(CrackResult):
|
||||
else:
|
||||
Color.pl('{!} %s {O}key unknown{W}' % ''.rjust(19))
|
||||
|
||||
def print_single_line(self, longest_essid):
|
||||
self.print_single_line_prefix(longest_essid)
|
||||
Color.p('{G}%s{W}' % 'WPA'.ljust(5))
|
||||
Color.p(' ')
|
||||
Color.p('Key: {G}%s{W}' % self.key)
|
||||
Color.pl('')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'type' : self.result_type,
|
||||
|
||||
@@ -27,6 +27,15 @@ class CrackResultWPS(CrackResult):
|
||||
Color.pl('{+} %s: {G}%s{W}' % ( 'WPS PIN'.rjust(12), self.pin))
|
||||
Color.pl('{+} %s: {G}%s{W}' % ('PSK/Password'.rjust(12), psk))
|
||||
|
||||
def print_single_line(self, longest_essid):
|
||||
self.print_single_line_prefix(longest_essid)
|
||||
Color.p('{G}%s{W}' % 'WPS'.ljust(5))
|
||||
Color.p(' ')
|
||||
if self.psk:
|
||||
Color.p('Key: {G}%s{W} ' % self.psk)
|
||||
Color.p('PIN: {G}%s{W}' % self.pin)
|
||||
Color.pl('')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'type' : self.result_type,
|
||||
|
||||
@@ -50,7 +50,7 @@ class Dependency(object):
|
||||
missing_required = any([app.fails_dependency_check() for app in apps])
|
||||
|
||||
if missing_required:
|
||||
Color.pl('{!} {R}required app(s) were not found, exiting.{W}')
|
||||
Color.pl('{!} {O}At least 1 Required app is missing. Wifite needs Required apps to run{W}')
|
||||
import sys
|
||||
sys.exit(-1)
|
||||
|
||||
@@ -64,11 +64,11 @@ class Dependency(object):
|
||||
return False
|
||||
|
||||
if cls.dependency_required:
|
||||
Color.pp('{!} {R}error: required app {O}%s{R} was not found' % cls.dependency_name)
|
||||
Color.p('{!} {O}Error: Required app {R}%s{O} was not found' % cls.dependency_name)
|
||||
Color.pl('. {W}install @ {C}%s{W}' % cls.dependency_url)
|
||||
return True
|
||||
|
||||
else:
|
||||
Color.p('{!} {O}warning: recommended app {R}%s{O} was not found' % cls.dependency_name)
|
||||
Color.p('{!} {O}Warning: Recommended app {R}%s{O} was not found' % cls.dependency_name)
|
||||
Color.pl('. {W}install @ {C}%s{W}' % cls.dependency_url)
|
||||
return False
|
||||
|
||||
@@ -157,7 +157,11 @@ class Reaver(Attack, Dependency):
|
||||
|
||||
# Try to derive PSK from PIN using Bully
|
||||
self.pattack('{W}Retrieving PSK using {C}bully{W}...')
|
||||
psk = Bully.get_psk_from_pin(self.target, pin)
|
||||
psk = None
|
||||
try:
|
||||
psk = Bully.get_psk_from_pin(self.target, pin)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
if psk is None:
|
||||
Color.pl('')
|
||||
self.pattack('{R}Failed {O}to get PSK using bully', newline=True)
|
||||
|
||||
@@ -11,8 +11,9 @@ from datetime import datetime
|
||||
import os
|
||||
|
||||
|
||||
# TODO: Bring back the 'print' option, for easy copy/pasting.
|
||||
# Just one-liners people can paste into terminal.
|
||||
# TODO: Bring back the 'print' option, for easy copy/pasting. Just one-liners people can paste into terminal.
|
||||
|
||||
# TODO: Do not show handshake files that are in cracked.txt with a key (match on filename).
|
||||
|
||||
class CrackHelper:
|
||||
'''Manages handshake retrieval, selection, and running the cracking commands.'''
|
||||
@@ -36,6 +37,9 @@ class CrackHelper:
|
||||
Color.pl('')
|
||||
|
||||
handshakes = cls.get_handshakes()
|
||||
if len(handshakes) == 0:
|
||||
Color.pl('{!} {O}No handshakes found{W}')
|
||||
return
|
||||
hs_to_crack = cls.get_user_selection(handshakes)
|
||||
|
||||
# TODO: Ask what method to use for WPA (aircrack, pyrit, john, hashcat, cowpatty)
|
||||
@@ -50,6 +54,10 @@ class CrackHelper:
|
||||
skipped_pmkid_files = 0
|
||||
|
||||
hs_dir = Configuration.wpa_handshake_dir
|
||||
if not os.path.exists(hs_dir) or not os.path.isdir(hs_dir):
|
||||
Color.pl('\n{!} {O}directory not found: {R}%s{W}' % hs_dir)
|
||||
return []
|
||||
|
||||
Color.pl('\n{+} Listing captured handshakes from {C}%s{W} ...\n' % os.path.abspath(hs_dir))
|
||||
for hs_file in os.listdir(hs_dir):
|
||||
if hs_file.count('_') != 3:
|
||||
@@ -102,12 +110,12 @@ class CrackHelper:
|
||||
@classmethod
|
||||
def print_handshakes(cls, handshakes):
|
||||
# Header
|
||||
max_essid_len = max(max([len(hs['essid']) for hs in handshakes]), len('ESSID (truncated)'))
|
||||
max_essid_len = max([len(hs['essid']) for hs in handshakes] + [len('ESSID (truncated)')])
|
||||
Color.p('{D} NUM')
|
||||
Color.p(' ESSID (truncated)'.ljust(max_essid_len))
|
||||
Color.p(' BSSID'.ljust(19))
|
||||
Color.p(' TYPE'.ljust(7))
|
||||
Color.p(' DATE CAPTURED\n')
|
||||
Color.p(' ' + 'ESSID (truncated)'.ljust(max_essid_len))
|
||||
Color.p(' ' + 'BSSID'.ljust(17))
|
||||
Color.p(' ' + 'TYPE'.ljust(5))
|
||||
Color.p(' ' + 'DATE CAPTURED\n')
|
||||
Color.p(' ---')
|
||||
Color.p(' ' + ('-' * max_essid_len))
|
||||
Color.p(' ' + ('-' * 17))
|
||||
|
||||
Reference in New Issue
Block a user