--crack supports hashcat, aircrack, john, cowpatty, and pyrit.

* Still not "print" option for --crack.
* Checks hashcat for devices, uses --force if no devices are found.
* Interrupting --crack stops entire process, not just a single crack attempt
* Changed wordlist location, hopefully completes #102.
This commit is contained in:
derv82
2018-08-20 19:33:42 -07:00
parent a063f08388
commit 4173ef46e5
10 changed files with 346 additions and 124 deletions

View File

@@ -97,7 +97,12 @@ class AttackPMKID(Attack):
return False # No hash found.
# Crack it.
self.success = self.crack_pmkid_file(pmkid_file)
try:
self.success = self.crack_pmkid_file(pmkid_file)
except KeyboardInterrupt:
Color.pl('\n{!} {R}Failed to crack PMKID: {O}Cracking interrupted by user{W}')
self.success = False
return False
return True # Even if we don't crack it, capturing a PMKID is 'successful'

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..tools.aircrack import Aircrack
from ..tools.airodump import Airodump
from ..tools.aireplay import Aireplay
from ..config import Configuration
@@ -47,11 +48,29 @@ class AttackWPA(Attack):
Color.pl('\n{+} analysis of captured handshake file:')
handshake.analyze()
# Check wordlist
if Configuration.wordlist is None:
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist ({R}--dict{O}) is not set')
self.success = False
return False
elif not os.path.exists(Configuration.wordlist):
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist {R}%s{O} was not found' % Configuration.wordlist)
self.success = False
return False
Color.pl('\n{+} {C}Cracking WPA Handshake:{W} Using {C}aircrack-ng{W} via' +
' {C}%s{W} wordlist' % os.path.split(Configuration.wordlist)[-1])
# Crack it
key = self.crack_handshake(handshake, Configuration.wordlist)
key = Aircrack.crack_handshake(handshake, Configuration.wordlist)
if key is None:
Color.pl('{!} {R}Failed to crack handshake: {O}%s{R} did not contain password{W}' % Configuration.wordlist.split(os.sep)[-1])
self.success = False
else:
Color.pl('{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n' % key)
self.crack_result = CrackResultWPA(handshake.bssid, handshake.essid, handshake.capfile, key)
self.crack_result.dump()
self.success = True
@@ -157,84 +176,6 @@ class AttackWPA(Attack):
self.save_handshake(handshake)
return handshake
@staticmethod
def crack_handshake(handshake, wordlist, verbose=False):
'''Tries to crack a handshake. Returns WPA key if found, otherwise None.'''
if wordlist is None:
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist ({R}--dict{O}) is not set')
return None
elif not os.path.exists(wordlist):
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist {R}%s{O} was not found' % wordlist)
return None
if not verbose:
Color.pl('\n{+} {C}Cracking WPA Handshake:{W} Using {C}aircrack-ng{W} via' +
' {C}%s{W} wordlist' % os.path.split(wordlist)[-1])
key_file = Configuration.temp('wpakey.txt')
command = [
'aircrack-ng',
'-a', '2',
'-w', wordlist,
'--bssid', handshake.bssid,
'-l', key_file,
handshake.capfile
]
if verbose:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
crack_proc = Process(command)
# Report progress of cracking
aircrack_nums_re = re.compile(r'(\d+)/(\d+) keys tested.*\(([\d.]+)\s+k/s')
aircrack_key_re = re.compile(r'Current passphrase:\s*([^\s].*[^\s])\s*$')
num_tried = num_total = 0
percent = num_kps = 0.0
eta_str = 'unknown'
current_key = ''
while crack_proc.poll() is None:
line = crack_proc.pid.stdout.readline()
match_nums = aircrack_nums_re.search(line.decode('utf-8'))
match_keys = aircrack_key_re.search(line.decode('utf-8'))
if match_nums:
num_tried = int(match_nums.group(1))
num_total = int(match_nums.group(2))
num_kps = float(match_nums.group(3))
eta_seconds = (num_total - num_tried) / num_kps
eta_str = Timer.secs_to_str(eta_seconds)
percent = 100.0 * float(num_tried) / float(num_total)
elif match_keys:
current_key = match_keys.group(1)
else:
continue
status = '\r{+} {C}Cracking WPA Handshake: %0.2f%%{W}' % percent
status += ' ETA: {C}%s{W}' % eta_str
status += ' @ {C}%0.1fkps{W}' % num_kps
#status += ' ({C}%d{W}/{C}%d{W} keys)' % (num_tried, num_total)
status += ' (current key: {C}%s{W})' % current_key
if not verbose:
Color.clear_entire_line()
Color.p(status)
if not verbose:
Color.pl('')
# Check crack result
if os.path.exists(key_file):
with open(key_file, 'r') as fid:
key = fid.read().strip()
os.remove(key_file)
if not verbose:
Color.pl('{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n' % key)
return key
else:
if not verbose:
Color.pl('{!} {R}Failed to crack handshake: {O}%s{R} did not contain password{W}' % wordlist.split(os.sep)[-1])
return None
def load_handshake(self, bssid, essid):
if not os.path.exists(Configuration.wpa_handshake_dir):
return None

View File

@@ -84,9 +84,10 @@ class Configuration(object):
# Default dictionary for cracking
cls.wordlist = None
wordlists = [
'./wordlist-top4800-probable.txt',
'/usr/share/wordlists/wordlist-top4800-probable.txt',
'/usr/local/share/wordlists/wordlist-top4800-probable.txt',
'./wordlist-top4800-probable.txt', # Local file (ran from cloned repo)
'/usr/dict/wordlists/wordlist-top4800-probable.txt', # setup.py with prefix=/usr
'/usr/local/dict/wordlists/wordlist-top4800-probable.txt', # setup.py with prefix=/usr/local
# Other passwords found on Kali
'/usr/share/wfuzz/wordlist/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt',
'/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt',
'/usr/share/wordlists/fern-wifi/common.txt'

View File

@@ -38,7 +38,7 @@ class Handshake(object):
if len(pairs) == 0 and not self.bssid and not self.essid:
# Tshark and Pyrit failed us, nothing else we can do.
raise Exception('Cannot find BSSID or ESSID in cap file')
raise ValueError('Cannot find BSSID or ESSID in cap file %s' % self.capfile)
if not self.essid and not self.bssid:
# We do not know the bssid nor the essid

View File

@@ -7,6 +7,7 @@ from ..util.input import xrange
from ..config import Configuration
import os
import re
class Aircrack(Dependency):
dependency_required = True
@@ -77,6 +78,70 @@ class Aircrack(Dependency):
if os.path.exists(self.cracked_file):
os.remove(self.cracked_file)
@staticmethod
def crack_handshake(handshake, show_command=False):
from ..util.color import Color
from ..util.timer import Timer
'''Tries to crack a handshake. Returns WPA key if found, otherwise None.'''
key_file = Configuration.temp('wpakey.txt')
command = [
'aircrack-ng',
'-a', '2',
'-w', Configuration.wordlist,
'--bssid', handshake.bssid,
'-l', key_file,
handshake.capfile
]
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
crack_proc = Process(command)
# Report progress of cracking
aircrack_nums_re = re.compile(r'(\d+)/(\d+) keys tested.*\(([\d.]+)\s+k/s')
aircrack_key_re = re.compile(r'Current passphrase:\s*([^\s].*[^\s])\s*$')
num_tried = num_total = 0
percent = num_kps = 0.0
eta_str = 'unknown'
current_key = ''
while crack_proc.poll() is None:
line = crack_proc.pid.stdout.readline()
match_nums = aircrack_nums_re.search(line.decode('utf-8'))
match_keys = aircrack_key_re.search(line.decode('utf-8'))
if match_nums:
num_tried = int(match_nums.group(1))
num_total = int(match_nums.group(2))
num_kps = float(match_nums.group(3))
eta_seconds = (num_total - num_tried) / num_kps
eta_str = Timer.secs_to_str(eta_seconds)
percent = 100.0 * float(num_tried) / float(num_total)
elif match_keys:
current_key = match_keys.group(1)
else:
continue
status = '\r{+} {C}Cracking WPA Handshake: %0.2f%%{W}' % percent
status += ' ETA: {C}%s{W}' % eta_str
status += ' @ {C}%0.1fkps{W}' % num_kps
#status += ' ({C}%d{W}/{C}%d{W} keys)' % (num_tried, num_total)
status += ' (current key: {C}%s{W})' % current_key
Color.clear_entire_line()
Color.p(status)
Color.pl('')
# Check crack result
if os.path.exists(key_file):
with open(key_file, 'r') as fid:
key = fid.read().strip()
os.remove(key_file)
return key
else:
return None
if __name__ == '__main__':
(hexkey, asciikey) = Aircrack._hex_and_ascii_key('A1B1C1D1E1')
assert hexkey == 'A1:B1:C1:D1:E1', 'hexkey was "%s", expected "A1:B1:C1:D1:E1"' % hexkey

41
wifite/tools/cowpatty.py Normal file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
from ..config import Configuration
from ..util.color import Color
from ..util.process import Process
from ..tools.hashcat import HcxPcapTool
import os
import re
class Cowpatty(Dependency):
''' Wrapper for Cowpatty program. '''
dependency_required = False
dependency_name = 'cowpatty'
dependency_url = 'https://tools.kali.org/wireless-attacks/cowpatty'
@staticmethod
def crack_handshake(handshake, show_command=False):
# Crack john file
command = [
'cowpatty',
'-f', Configuration.wordlist,
'-r', handshake.capfile,
'-s', handshake.essid
]
if show_command:
Color.pl('{+} {D}{C}Running %s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
key = None
for line in stdout.split('\n'):
if 'The PSK is "' in line:
key = line.split('"', 1)[1][:-2]
break
return key

View File

@@ -14,6 +14,47 @@ class Hashcat(Dependency):
dependency_name = 'hashcat'
dependency_url = 'https://hashcat.net/hashcat/'
@staticmethod
def should_use_force():
command = ['hashcat', '-I']
stderr = Process(command).stderr()
return 'No devices found/left' in stderr
@staticmethod
def crack_handshake(handshake, show_command=False):
# Generate hccapx
hccapx_file = HcxPcapTool.generate_hccapx_file(
handshake, show_command=show_command)
key = None
# Crack hccapx
for additional_arg in [ [], ['--show']]:
command = [
'hashcat',
'--quiet',
'-m', '2500',
hccapx_file,
Configuration.wordlist
]
if Hashcat.should_use_force():
command.append('--force')
command.extend(additional_arg)
if show_command:
Color.pl('{+} {D}{C}Running %s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
if ':' not in stdout:
continue
else:
key = stdout.split(':', 5)[-1].strip()
break
if os.path.exists(hccapx_file):
os.remove(hccapx_file)
return key
@staticmethod
def crack_pmkid(pmkid_file, verbose=False):
'''
@@ -27,27 +68,23 @@ class Hashcat(Dependency):
for additional_arg in [ [], ['--show']]:
command = [
'hashcat',
'--force',
'--quiet', # Only output the password if found.
'-m', '16800', # WPA-PMKID-PBKDF2
'-a', '0', # TODO: Configure
'-w', '2', # TODO: Configure
'-a', '0', # Wordlist attack-mode
pmkid_file,
Configuration.wordlist
]
if Hashcat.should_use_force():
command.append('--force')
command.extend(additional_arg)
if verbose and additional_arg == []:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
# 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 = ''
hashcat_proc = Process(command)
hashcat_proc.wait()
stdout = hashcat_proc.stdout()
if ':' not in stdout:
# Failed
@@ -100,6 +137,52 @@ class HcxPcapTool(Dependency):
self.bssid = self.target.bssid.lower().replace(':', '')
self.pmkid_file = Configuration.temp('pmkid-%s.16800' % self.bssid)
@staticmethod
def generate_hccapx_file(handshake, show_command=False):
hccapx_file = Configuration.temp('generated.hccapx')
if os.path.exists(hccapx_file):
os.remove(hccapx_file)
command = [
'hcxpcaptool',
'-o', hccapx_file,
handshake.capfile
]
if show_command:
Color.pl('{+} {D}{C}Running %s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
if not os.path.exists(hccapx_file):
raise ValueError('Failed to generate .hccapx file, output: \n%s\n%s' % (
stdout, stderr))
return hccapx_file
@staticmethod
def generate_john_file(handshake, show_command=False):
john_file = Configuration.temp('generated.john')
if os.path.exists(john_file):
os.remove(john_file)
command = [
'hcxpcaptool',
'-j', john_file,
handshake.capfile
]
if show_command:
Color.pl('{+} {D}{C}Running %s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
if not os.path.exists(john_file):
raise ValueError('Failed to generate .john file, output: \n%s\n%s' % (
stdout, stderr))
return john_file
def get_pmkid_hash(self, pcapng_file):
if os.path.exists(self.pmkid_file):
os.remove(self.pmkid_file)

53
wifite/tools/john.py Normal file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
from ..config import Configuration
from ..util.color import Color
from ..util.process import Process
from ..tools.hashcat import HcxPcapTool
import os
class John(Dependency):
''' Wrapper for John program. '''
dependency_required = False
dependency_name = 'john'
dependency_url = 'http://www.openwall.com/john/'
@staticmethod
def crack_handshake(handshake, show_command=False):
john_file = HcxPcapTool.generate_john_file(handshake, show_command=show_command)
# Crack john file
command = [
'john',
'--format=wpapsk', # wpapsk-cuda or wpapsk-opencl
'--wordlist', Configuration.wordlist,
john_file
]
if show_command:
Color.pl('{+} {D}{C}Running %s{W}' % ' '.join(command))
process = Process(command)
process.wait()
# Show the password (if found)
command = ['john', '--show', john_file]
if show_command:
Color.pl('{+} {D}{C}Running %s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
key = None
if not '0 password hashes cracked' in stdout:
for line in stdout.split('\n'):
if handshake.capfile in line:
key = line.split(':')[1]
break
if os.path.exists(john_file):
os.remove(john_file)
return key

View File

@@ -1,10 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from ..config import Configuration
from ..model.handshake import Handshake
from ..model.wpa_result import CrackResultWPA
from ..model.pmkid_result import CrackResultPMKID
from ..util.process import Process
from ..util.color import Color
from ..util.input import raw_input
from ..config import Configuration
from ..tools.aircrack import Aircrack
from ..tools.cowpatty import Cowpatty
from ..tools.hashcat import Hashcat, HcxPcapTool
from ..tools.john import John
from datetime import datetime
@@ -19,8 +26,8 @@ class CrackHelper:
'''Manages handshake retrieval, selection, and running the cracking commands.'''
TYPES = {
'4-WAY': 'WPA 4-Way Handshake',
'PMKID': 'WPA PKID Hash'
'4-WAY': '4-Way Handshake',
'PMKID': 'PMKID Hash'
}
@@ -28,6 +35,7 @@ class CrackHelper:
def run(cls):
Configuration.initialize(False)
# Get wordlist
if not Configuration.wordlist:
Color.p('\n{+} Enter wordlist file to use for cracking: {G}')
Configuration.wordlist = raw_input()
@@ -36,16 +44,35 @@ class CrackHelper:
return
Color.pl('')
# Get handshakes
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)
# Get tool
available_tools = ['aircrack', 'hashcat', 'john', 'cowpatty']
if not Process.exists(HcxPcapTool.dependency_name):
Color.pl('{!} {R}Unable to use hashcat: {O}missing required hcxpcaptool{W}')
available_tools.remove('hashcat')
if not Process.exists(John.dependency_name):
Color.pl('{!} {R}Unable to use john: {O}missing required "john" program{W}')
available_tools.remove('john')
for hs in hs_to_crack:
cls.crack(hs)
Color.p('{+} Enter the {C}cracking tool{W} to use ({C}%s{W}): {G}' % (
'{W}, {C}'.join(available_tools)))
tool_name = raw_input()
if tool_name not in available_tools:
Color.pl('{!} {O}%s not found, defaulting to aircrack' % tool_name)
tool_name = 'aircrack'
try:
for hs in hs_to_crack:
cls.crack(hs, tool_name)
except KeyboardInterrupt:
Color.pl('\n{!} {O}Interrupted{W}')
@classmethod
def get_handshakes(cls):
@@ -136,7 +163,7 @@ class CrackHelper:
def get_user_selection(cls, handshakes):
cls.print_handshakes(handshakes)
Color.p('{+} Select handshake(s) to crack ({G}%d{W}-{G}%d{W}, select multiple with {C},{W} or {C}-{W}): {G}' % (1, len(handshakes)))
Color.p('{+} Select handshake(s) to crack ({G}%d{W}-{G}%d{W}, select multiple with {C},{W} or {C}-{W} or {C}all{W}): {G}' % (1, len(handshakes)))
choices = raw_input()
selection = []
@@ -145,7 +172,10 @@ class CrackHelper:
first, last = [int(x) for x in choice.split('-')]
for index in range(first, last + 1):
selection.append(handshakes[index-1])
else:
elif choice.strip().lower() == 'all':
selection = handshakes[:]
break
elif [c.isdigit() for c in choice]:
index = int(choice)
selection.append(handshakes[index-1])
@@ -153,12 +183,14 @@ class CrackHelper:
@classmethod
def crack(cls, hs):
Color.pl('\n{+} Cracking {C}%s{W} ({C}%s{W}) using {G}%s{W} method' % (hs['essid'], hs['bssid'], hs['type']))
def crack(cls, hs, tool):
Color.pl('\n{+} Cracking {G}%s {C}%s{W} ({C}%s{W})' % (
cls.TYPES[hs['type']], hs['essid'], hs['bssid']))
if hs['type'] == 'PMKID':
crack_result = cls.crack_pmkid(hs)
crack_result = cls.crack_pmkid(hs, tool)
elif hs['type'] == '4-WAY':
crack_result = cls.crack_4way(hs)
crack_result = cls.crack_4way(hs, tool)
else:
raise ValueError('Cannot crack handshake: Type is not PMKID or 4-WAY. Handshake=%s' % hs)
@@ -174,20 +206,25 @@ class CrackHelper:
@classmethod
def crack_4way(cls, hs):
from ..attack.wpa import AttackWPA
from ..model.handshake import Handshake
from ..model.wpa_result import CrackResultWPA
def crack_4way(cls, hs, tool):
handshake = Handshake(hs['filename'],
bssid=hs['bssid'],
essid=hs['essid'])
key = None
try:
key = AttackWPA.crack_handshake(handshake, Configuration.wordlist, verbose=True)
except KeyboardInterrupt:
Color.pl('\n{!} Interrupted')
handshake.divine_bssid_and_essid()
except ValueError as e:
Color.pl('{!} {R}Error: {O}%s{W}' % e)
return None
if tool == 'aircrack':
key = Aircrack.crack_handshake(handshake, show_command=True)
elif tool == 'hashcat':
key = Hashcat.crack_handshake(handshake, show_command=True)
elif tool == 'john':
key = John.crack_handshake(handshake, show_command=True)
elif tool == 'cowpatty':
key = Cowpatty.crack_handshake(handshake, show_command=True)
if key is not None:
return CrackResultWPA(hs['bssid'], hs['essid'], hs['filename'], key)
@@ -196,15 +233,11 @@ class CrackHelper:
@classmethod
def crack_pmkid(cls, hs):
from ..tools.hashcat import Hashcat
from ..model.pmkid_result import CrackResultPMKID
def crack_pmkid(cls, hs, tool_name):
if tool_name != 'hashcat':
Color.pl('{!} {O}Note: PMKIDs can only be cracked using hashcat{W}')
key = None
try:
key = Hashcat.crack_pmkid(hs['filename'], verbose=True)
except KeyboardInterrupt:
Color.pl('\n{!} Interrupted')
key = Hashcat.crack_pmkid(hs['filename'], verbose=True)
if key is not None:
return CrackResultPMKID(hs['bssid'], hs['essid'], hs['filename'], key)

View File

@@ -81,7 +81,7 @@ class Wifite(object):
# Attack
attacked_targets = AttackAll.attack_multiple(targets)
Color.pl('{+} Finished attacking {C}%d{W} target(s), exiting' % attacked_targets)
Color.pl('\n{+} Finished attacking {C}%d{W} target(s), exiting' % attacked_targets)
##############################################################