Skip to content

Commit

Permalink
2.1.9: --pmkid option, cleaned up --cracked, other bug fixes.
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
derv82 committed Aug 19, 2018
1 parent ebb7cac commit a157132
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 30 deletions.
6 changes: 6 additions & 0 deletions wifite/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ def _add_wpa_args(self, wpa):
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',
Expand Down
5 changes: 3 additions & 2 deletions wifite/attack/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ def attack_single(cls, target, targets_remaining):
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:
Expand All @@ -77,7 +78,7 @@ def attack_single(cls, target, targets_remaining):
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:
Expand Down
17 changes: 12 additions & 5 deletions wifite/attack/pmkid.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ def run(self):
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 = [
Expand Down Expand Up @@ -103,7 +109,7 @@ def capture_pmkid(self):
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)
Expand Down Expand Up @@ -159,10 +165,11 @@ def crack_pmkid_file(self, pmkid_file):

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.
Expand Down
12 changes: 10 additions & 2 deletions wifite/attack/wpa.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ def __init__(self, target):
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

Expand Down Expand Up @@ -110,7 +113,12 @@ def capture_handshake(self):
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
Expand Down
6 changes: 5 additions & 1 deletion wifite/attack/wps.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ def run(self):
''' 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()
Expand Down
6 changes: 5 additions & 1 deletion wifite/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

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
Expand Down Expand Up @@ -79,6 +79,7 @@ def initialize(cls, load_interface=True):
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
Expand Down Expand Up @@ -227,6 +228,9 @@ def load_from_arguments(cls):
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)
Expand Down
7 changes: 7 additions & 0 deletions wifite/model/pmkid_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ def dump(self):
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,
Expand Down
47 changes: 41 additions & 6 deletions wifite/model/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,27 @@ 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()')

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
Expand Down Expand Up @@ -52,12 +66,32 @@ def display(cls):

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
Expand Down Expand Up @@ -97,6 +131,7 @@ def load(json):
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__':
Expand Down
11 changes: 10 additions & 1 deletion wifite/model/wep_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@ def dump(self):
Color.pl('{+} Hex Key: {G}%s{W}' % self.hex_key)
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,
Expand Down
9 changes: 8 additions & 1 deletion wifite/model/wpa_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@ def dump(self):
Color.pl('{+} %s: {G}%s{W}' % ('PSK (password)'.rjust(19), self.key))
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,
Expand Down
9 changes: 9 additions & 0 deletions wifite/model/wps_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ def dump(self):
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,
Expand Down
6 changes: 3 additions & 3 deletions wifite/tools/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def run_dependency_check(cls):
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)

Expand All @@ -64,11 +64,11 @@ def fails_dependency_check(cls):
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
6 changes: 5 additions & 1 deletion wifite/tools/reaver.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,11 @@ def parse_crack_result(self, stdout):

# 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)
Expand Down
22 changes: 15 additions & 7 deletions wifite/util/crack.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
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.'''
Expand All @@ -36,6 +37,9 @@ def run(cls):
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)
Expand All @@ -50,6 +54,10 @@ def get_handshakes(cls):
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:
Expand Down Expand Up @@ -102,12 +110,12 @@ def get_handshakes(cls):
@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))
Expand Down

0 comments on commit a157132

Please sign in to comment.