diff --git a/py/Arguments.py b/py/Arguments.py index 609d0fe..6a5bb9e 100644 --- a/py/Arguments.py +++ b/py/Arguments.py @@ -71,6 +71,15 @@ 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='scan_time', + nargs='?', + const=10, + metavar='scantime', + type=int, + help=Color.s('{G}Pillage{W}: Attack all targets after {C}scantime{W} seconds')) + glob.add_argument('--pillage', help=argparse.SUPPRESS, action='store', dest='scan_time', nargs='?', const=10, type=int) # WEP wep = parser.add_argument_group('WEP-RELATED') diff --git a/py/AttackWPA.py b/py/AttackWPA.py index 9da7350..30d65a1 100644 --- a/py/AttackWPA.py +++ b/py/AttackWPA.py @@ -34,6 +34,35 @@ class AttackWPA(Attack): self.success = False return self.success + handshake = None + + # Capture the handshake ("do it live!") + if handshake is None: + handshake = self.capture_handshake() + + if handshake is None: + # Failed to capture handshake + self.success = False + return self.success + + # Analyze handshake + Color.pl('\n{+} analysis of captured handshake file:') + handshake.analyze() + + # Crack it + 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 capture_handshake(self): + ''' Returns captured or stored handshake, otherwise None ''' + handshake = None + # First, start Airodump process with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, @@ -46,7 +75,17 @@ class AttackWPA(Attack): self.clients = [] - handshake = None + bssid = airodump_target.bssid + essid = airodump_target.essid if airodump_target.essid_known else None + + # Try to load existing handshake + if Configuration.ignore_old_handshakes == False: + handshake = self.load_handshake(bssid=bssid, essid=essid) + if handshake: + Color.clear_entire_line() + Color.pl('{+} found {G}existing handshake{W} for {C}%s{W}' % handshake.essid) + Color.pl('{+} from {C}%s{W}' % handshake.capfile) + return handshake timeout_timer = Timer(Configuration.wpa_attack_timeout) deauth_timer = Timer(Configuration.wpa_deauth_timeout) @@ -107,28 +146,14 @@ class AttackWPA(Attack): 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 - + if handshake is None: + # No handshake, attack failed. + Color.pl("\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds" % (Configuration.wpa_attack_timeout)) + return handshake + else: # 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 + return handshake def crack_handshake(self, handshake, wordlist): '''Tries to crack a handshake. Returns WPA key if found, otherwise None.''' @@ -200,6 +225,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/Configuration.py b/py/Configuration.py index fc34585..9b032a5 100644 --- a/py/Configuration.py +++ b/py/Configuration.py @@ -28,13 +28,15 @@ class Configuration(object): Configuration.verbose = 0 # Verbosity level. + Configuration.scan_time = 0 # Time to wait before attacking all targets + Configuration.all_targets = False # Run attacks against all targets automatically + Configuration.tx_power = 0 # Wifi transmit power (0 is default) Configuration.interface = None Configuration.target_channel = None # User-defined channel to scan Configuration.target_essid = None # User-defined AP name Configuration.target_bssid = None # User-defined AP BSSID Configuration.five_ghz = False # Scan 5Ghz channels - Configuration.pillage = False # "All" mode to attack everything Configuration.random_mac = False # Should generate a random Mac address at startup. Configuration.no_deauth = False # Deauth hidden networks & WPA handshake targets Configuration.num_deauths = 1 # Number of deauth packets to send to each target. @@ -60,6 +62,7 @@ class Configuration(object): Configuration.wpa_attack_timeout = 500 # Wait time before failing Configuration.wpa_handshake_dir = "hs" # Dir to store handshakes Configuration.wpa_strip_handshake = False # Strip non-handshake packets + Configuration.ignore_old_handshakes = False # Always fetch a new handshake # Default dictionary for cracking Configuration.wordlist = None @@ -138,6 +141,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} ({G}pillage{W}) attack all targets after {G}%d{W}s' % 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 e0211ae..6e48b59 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 ''' @@ -30,8 +30,9 @@ class Scanner(object): try: with Airodump() as airodump: # Loop until interrupted (Ctrl+C) - while True: + scan_start_time = time() + while True: if airodump.pid.poll() is not None: # Airodump process died self.err_msg = '\r{!} {R}Airodump exited unexpectedly (Code: %d){O} Command: {W}%s' % (airodump.pid.poll(), " ".join(airodump.pid.command)) @@ -66,6 +67,10 @@ class Scanner(object): outline += " {G}%s{W}) " % ", ".join([x.essid for x in decloaked]) Color.clear_entire_line() Color.p(outline) + + if Configuration.scan_time > 0 and time() > scan_start_time + Configuration.scan_time: + return + sleep(1) except KeyboardInterrupt: pass @@ -83,10 +88,10 @@ class Scanner(object): return False for target in self.targets: - if bssid and bssid.lower() == target.bssid.lower(): + if bssid and target.bssid and bssid.lower() == target.bssid.lower(): self.target = target break - if essid and essid.lower() == target.essid.lower(): + if essid and target.essid and essid.lower() == target.essid.lower(): self.target = target break @@ -159,6 +164,9 @@ class Scanner(object): + " You may need to wait longer," + " or you may have issues with your wifi card") + if Configuration.scan_time > 0: + return self.targets + self.print_targets() Color.clear_entire_line() @@ -171,6 +179,7 @@ class Scanner(object): 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 @@ -187,7 +196,6 @@ class Scanner(object): pass return chosen_targets - if __name__ == '__main__': # Example displays targets and selects the appropriate one Configuration.initialize()