diff --git a/py/Arguments.py b/py/Arguments.py index 609d0fe..5560620 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('Seconds to scan before attacking (default: {G}ask{W})')) glob.add_argument('-i', action='store', dest='interface', @@ -71,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/AttackWPA.py b/py/AttackWPA.py index 9da7350..e5eb7de 100644 --- a/py/AttackWPA.py +++ b/py/AttackWPA.py @@ -46,75 +46,81 @@ 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}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) + 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 +206,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..067ffe7 100644 --- a/py/Configuration.py +++ b/py/Configuration.py @@ -28,6 +28,9 @@ 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 Configuration.target_channel = None # User-defined channel to scan @@ -66,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): @@ -138,6 +141,12 @@ 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.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 8ecd423..dc0640e 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 @@ -153,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