diff --git a/TODO.md b/TODO.md index 07cd3e8..ac10678 100644 --- a/TODO.md +++ b/TODO.md @@ -121,13 +121,13 @@ Not "/py": * `tshark.py` <- process * `cowpatty.py` <- process * `pyrit.py` <- process + * `handshake.py` <- tshark, cowpatty, pyrit, aircrack * `output.py` (color/printing) <- config * `process.py` <- config * `scan.py` (airodump output to target) <- config, target, airodump * **target/** * `target.py` (ssid, pcap file) <- airodump, tshark * `result.py` (PIN/PSK/KEY) - * `handshake.py` <- tshark, cowpatty, pyrit, aircrack ------------------------------------------------------ diff --git a/Wifite.py b/Wifite.py index 0a15cd7..246013a 100755 --- a/Wifite.py +++ b/Wifite.py @@ -1,239 +1,5 @@ -#!/usr/bin/python2.7 -# -*- coding: utf-8 -*- +#!/usr/bin/python -from py.Configuration import Configuration -from py.Scanner import Scanner -from py.Color import Color -from py.AttackWEP import AttackWEP -from py.AttackWPA import AttackWPA -from py.AttackWPS import AttackWPS -from py.CrackResult import CrackResult -from py.Handshake import Handshake -from py.CrackHandshake import CrackHandshake -from py.Process import Process +from wifite import wifite -from json import loads -import os -from sys import exit - -class Wifite(object): - - def main(self): - ''' Either performs action based on arguments, or starts attack scanning ''' - - if os.getuid() != 0: - Color.pl('{!} {R}error: {O}wifite{R} must be run as {O}root{W}') - Color.pl('{!} {O}re-run as: sudo ./Wifite.py{W}') - Configuration.exit_gracefully(0) - - self.dependency_check() - - Configuration.initialize(load_interface=False) - - if Configuration.show_cracked: - self.display_cracked() - - elif Configuration.check_handshake: - self.check_handshake(Configuration.check_handshake) - elif Configuration.crack_handshake: - CrackHandshake() - else: - Configuration.get_interface() - self.run() - - def dependency_check(self): - ''' Check that required programs are installed ''' - required_apps = ['airmon-ng', 'iwconfig', 'ifconfig', 'aircrack-ng', 'aireplay-ng', 'airodump-ng', 'tshark'] - optional_apps = ['packetforge-ng', 'reaver', 'bully', 'cowpatty', 'pyrit', 'stdbuf', 'macchanger'] - missing_required = False - missing_optional = False - - for app in required_apps: - if not Process.exists(app): - missing_required = True - Color.pl('{!} {R}error: required app {O}%s{R} was not found' % app) - - for app in optional_apps: - if not Process.exists(app): - missing_optional = True - Color.pl('{!} {O}warning: recommended app {R}%s{O} was not found' % app) - - if missing_required: - Color.pl('{!} {R}required app(s) were not found, exiting.{W}') - exit(-1) - - if missing_optional: - Color.pl('{!} {O}recommended app(s) were not found') - Color.pl('{!} {O}wifite may not work as expected{W}') - - def display_cracked(self): - ''' Show cracked targets from cracked.txt ''' - Color.pl('{+} displaying {C}cracked target(s){W}') - name = CrackResult.cracked_file - if not os.path.exists(name): - Color.pl('{!} {O}file {C}%s{O} not found{W}' % name) - return - with open(name, 'r') as fid: - json = loads(fid.read()) - for idx, item in enumerate(json, start=1): - Color.pl('\n{+} Cracked target #%d:' % (idx)) - cr = CrackResult.load(item) - cr.dump() - - def check_handshake(self, capfile): - ''' Analyzes .cap file for handshake ''' - if capfile == '': - Color.pl('{+} checking all handshakes in {G}"./hs"{W} directory\n') - try: - capfiles = [os.path.join('hs', x) for x in os.listdir('hs') if x.endswith('.cap')] - except OSError, e: - capfiles = [] - if len(capfiles) == 0: - Color.pl('{!} {R}no .cap files found in {O}"./hs"{W}\n') - else: - capfiles = [capfile] - for capfile in capfiles: - Color.pl('{+} checking for handshake in .cap file {C}%s{W}' % capfile) - if not os.path.exists(capfile): - Color.pl('{!} {O}.cap file {C}%s{O} not found{W}' % capfile) - return - hs = Handshake(capfile, bssid=Configuration.target_bssid, essid=Configuration.target_essid) - hs.analyze() - Color.pl('') - - def run(self): - ''' - Main program. - 1) Scans for targets, asks user to select targets - 2) Attacks each target - ''' - s = Scanner() - if s.target: - # We found the target we want - targets = [s.target] - else: - targets = s.select_targets() - - attacked_targets = 0 - targets_remaining = len(targets) - for idx, t in enumerate(targets, start=1): - attacked_targets += 1 - targets_remaining -= 1 - - Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (idx, len(targets)) + - ' starting attacks against {C}%s{W} ({C}%s{W})' - % (t.bssid, t.essid if t.essid_known else "{O}ESSID unknown")) - if 'WEP' in t.encryption: - attack = AttackWEP(t) - elif 'WPA' in t.encryption: - if t.wps: - attack = AttackWPS(t) - result = False - try: - result = attack.run() - except Exception as e: - Color.pl("\n{!} {R}Error: {O}%s" % str(e)) - if Configuration.verbose > 0 or Configuration.print_stack_traces: - Color.pl('\n{!} {O}Full stack trace below') - from traceback import format_exc - Color.p('\n{!} ') - err = format_exc().strip() - err = err.replace('\n', '\n{!} {C} ') - err = err.replace(' File', '{W}File') - err = err.replace(' Exception: ', '{R}Exception: {O}') - Color.pl(err) - except KeyboardInterrupt: - Color.pl('\n{!} {O}interrupted{W}\n') - if not self.user_wants_to_continue(targets_remaining, 1): - break - - if result and attack.success: - # We cracked it. - attack.crack_result.save() - continue - else: - # WPS failed, try WPA handshake. - attack = AttackWPA(t) - else: - # Not using WPS, try WPA handshake. - attack = AttackWPA(t) - else: - Color.pl("{!} {R}Error: {O}unable to attack: encryption not WEP or WPA") - continue - - try: - attack.run() - except Exception, e: - Color.pl("\n{!} {R}Error: {O}%s" % str(e)) - if Configuration.verbose > 0 or True: - Color.pl('\n{!} {O}Full stack trace below') - from traceback import format_exc - Color.p('\n{!} ') - err = format_exc().strip() - err = err.replace('\n', '\n{!} {C} ') - err = err.replace(' File', '{W}File') - err = err.replace(' Exception: ', '{R}Exception: {O}') - Color.pl(err) - except KeyboardInterrupt: - Color.pl('\n{!} {O}interrupted{W}\n') - if not self.user_wants_to_continue(targets_remaining): - break - - if attack.success: - attack.crack_result.save() - Color.pl("{+} Finished attacking {C}%d{W} target(s), exiting" % attacked_targets) - - - def print_banner(self): - """ Displays ASCII art of the highest caliber. """ - Color.pl(r''' -{G} . {GR}{D} {W}{G} . {W} -{G}.´ · .{GR}{D} {W}{G}. · `. {G}wifite {D}%s{W} -{G}: : : {GR}{D} (¯) {W}{G} : : : {W}{D}automated wireless auditor -{G}`. · `{GR}{D} /¯\ {W}{G}´ · .´ {C}{D}https://github.com/derv82/wifite2 -{G} ` {GR}{D}/¯¯¯\{W}{G} ´ {W} -''' % Configuration.version) - - def user_wants_to_continue(self, targets_remaining, attacks_remaining=0): - ''' Asks user if attacks should continue onto other targets ''' - if attacks_remaining == 0 and targets_remaining == 0: - # No targets or attacksleft, drop out - return - - prompt_list = [] - if attacks_remaining > 0: - prompt_list.append(Color.s('{C}%d{W} attack(s)' % attacks_remaining)) - if targets_remaining > 0: - prompt_list.append(Color.s('{C}%d{W} target(s)' % targets_remaining)) - prompt = ' and '.join(prompt_list) - Color.pl('{+} %s remain, do you want to continue?' % prompt) - - prompt = Color.s('{+} type {G}c{W} to {G}continue{W}' + - ' or {R}s{W} to {R}stop{W}: ') - - if raw_input(prompt).lower().startswith('s'): - return False - else: - return True - - -if __name__ == '__main__': - w = Wifite() - try: - w.print_banner() - w.main() - except Exception, e: - Color.pl('\n{!} {R}Error:{O} %s{W}' % str(e)) - if Configuration.verbose > 0 or True: - Color.pl('\n{!} {O}Full stack trace below') - from traceback import format_exc - Color.p('\n{!} ') - err = format_exc().strip() - err = err.replace('\n', '\n{!} {C} ') - err = err.replace(' File', '{W}File') - err = err.replace(' Exception: ', '{R}Exception: {O}') - Color.pl(err) - Color.pl('\n{!} {R}Exiting{W}\n') - except KeyboardInterrupt: - Color.pl('\n{!} {O}interrupted, shutting down...{W}') - Configuration.exit_gracefully(0) +wifite.run() diff --git a/runtests.sh b/runtests.sh index da9b3ed..8844f45 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,2 +1,2 @@ #!/bin/sh -python2.7 -m unittest discover py/tests -v +python2.7 -m unittest discover tests -v diff --git a/py/__init__.py b/tests/__init__.py similarity index 100% rename from py/__init__.py rename to tests/__init__.py diff --git a/py/tests/files/airmon.output b/tests/files/airmon.output similarity index 100% rename from py/tests/files/airmon.output rename to tests/files/airmon.output diff --git a/py/tests/files/airodump.csv b/tests/files/airodump.csv similarity index 100% rename from py/tests/files/airodump.csv rename to tests/files/airodump.csv diff --git a/py/tests/files/contains_wps_network.cap b/tests/files/contains_wps_network.cap similarity index 100% rename from py/tests/files/contains_wps_network.cap rename to tests/files/contains_wps_network.cap diff --git a/py/tests/files/handshake_exists.cap b/tests/files/handshake_exists.cap similarity index 100% rename from py/tests/files/handshake_exists.cap rename to tests/files/handshake_exists.cap diff --git a/py/tests/files/handshake_exists.cap.stripped.tshark b/tests/files/handshake_exists.cap.stripped.tshark similarity index 100% rename from py/tests/files/handshake_exists.cap.stripped.tshark rename to tests/files/handshake_exists.cap.stripped.tshark diff --git a/py/tests/files/handshake_not_exists.cap b/tests/files/handshake_not_exists.cap similarity index 100% rename from py/tests/files/handshake_not_exists.cap rename to tests/files/handshake_not_exists.cap diff --git a/py/tests/files/wep-crackable.ivs b/tests/files/wep-crackable.ivs similarity index 100% rename from py/tests/files/wep-crackable.ivs rename to tests/files/wep-crackable.ivs diff --git a/py/tests/files/wep-uncrackable.ivs b/tests/files/wep-uncrackable.ivs similarity index 100% rename from py/tests/files/wep-uncrackable.ivs rename to tests/files/wep-uncrackable.ivs diff --git a/py/tests/test_Handshake.py b/tests/test_Handshake.py similarity index 97% rename from py/tests/test_Handshake.py rename to tests/test_Handshake.py index b0ac1c9..3ed0566 100644 --- a/py/tests/test_Handshake.py +++ b/tests/test_Handshake.py @@ -4,7 +4,7 @@ import sys sys.path.insert(0, '..') -from py.Handshake import Handshake +from wifite.model.handshake import Handshake import unittest diff --git a/py/tests/test_Target.py b/tests/test_Target.py similarity index 96% rename from py/tests/test_Target.py rename to tests/test_Target.py index d16f1d9..6d2f05f 100644 --- a/py/tests/test_Target.py +++ b/tests/test_Target.py @@ -1,7 +1,7 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from py.Airodump import Airodump +from wifite.tools.airodump import Airodump import unittest diff --git a/py/tests/__init__.py b/wifite/__init__.py similarity index 100% rename from py/tests/__init__.py rename to wifite/__init__.py diff --git a/py/Arguments.py b/wifite/args.py old mode 100644 new mode 100755 similarity index 73% rename from py/Arguments.py rename to wifite/args.py index 3fb6b2e..c78c507 --- a/py/Arguments.py +++ b/wifite/args.py @@ -1,22 +1,55 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -import argparse -from Color import Color +from util.color import Color + +import argparse, sys class Arguments(object): ''' Holds arguments used by the Wifite ''' - def __init__(self, Configuration): - self.args = self.get_arguments(Configuration) - def get_arguments(self, Configuration): + def __init__(self, configuration): + self.verbose = any(['-v' in word for word in sys.argv]) + self.config = configuration + self.args = self.get_arguments() + + def _verbose(self, msg): + if self.verbose: + return Color.s(msg) + else: + return argparse.SUPPRESS + + def get_arguments(self): ''' Returns parser.args() containing all program arguments ''' parser = argparse.ArgumentParser(usage=argparse.SUPPRESS, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=80, width=130)) - # Global variables glob = parser.add_argument_group('SETTINGS') + self._add_global_args(glob) + + wep_group = parser.add_argument_group('WEP') + self._add_wep_args(wep_group) + + wpa_group = parser.add_argument_group('WPA') + self._add_wpa_args(wpa_group) + + wps_group = parser.add_argument_group('WPS') + self._add_wps_args(wps_group) + + commands_group = parser.add_argument_group('COMMANDS') + self._add_command_args(commands_group) + + return parser.parse_args() + + + def _add_global_args(self, glob): + glob.add_argument('-v', + '--verbose', + action='count', + default=0, + dest='verbose', + help=Color.s('Shows more options ({C}-h -v{W}). Prints tool outputs. (default: {G}quiet{W})')) glob.add_argument('-i', action='store', @@ -25,18 +58,13 @@ class Arguments(object): type=str, help=Color.s('Wireless interface to use (default: {G}ask{W})')) - glob.add_argument('--kill', - action='store_true', - dest='kill_conflicting_processes', - help=Color.s('Kill processes that conflict with Airmon/Airodump (default: {G}off{W})')) - - glob.add_argument('--channel', help=argparse.SUPPRESS, action='store', dest='channel', type=int) glob.add_argument('-c', action='store', dest='channel', metavar='[channel]', type=int, help=Color.s('Wireless channel to scan (default: {G}all channels{W})')) + glob.add_argument('--channel', help=argparse.SUPPRESS, action='store', dest='channel', type=int) glob.add_argument('-mac', '---random-mac', @@ -44,37 +72,61 @@ class Arguments(object): dest='random_mac', help=Color.s('Randomize wireless card MAC address (default: {G}off{W})')) + glob.add_argument('-p', + action='store', + dest='scan_time', + nargs='?', + const=10, + metavar='scantime', + type=int, + help=Color.s('{G}Pillage{W}: Attack all targets after {C}scantime{W} seconds')) + glob.add_argument('--pillage', help=argparse.SUPPRESS, action='store', dest='scan_time', nargs='?', const=10, type=int) + + glob.add_argument('--kill', + action='store_true', + dest='kill_conflicting_processes', + help=Color.s('Kill processes that conflict with Airmon/Airodump (default: {G}off{W})')) + glob.add_argument('-5', '--5ghz', action='store_true', dest='five_ghz', - help=Color.s('Include 5Ghz channels (default: {G}off{W})')) + help=self._verbose('Include 5Ghz channels (default: {G}off{W})')) - glob.add_argument('--bssid', help=argparse.SUPPRESS, action='store', dest='target_bssid', type=str) glob.add_argument('-b', action='store', dest='target_bssid', metavar='[bssid]', type=str, - help=Color.s('BSSID (e.g. {GR}AA:BB:CC:DD:EE:FF{W}) of access point to attack')) + help=self._verbose('BSSID (e.g. {GR}AA:BB:CC:DD:EE:FF{W}) of access point to attack')) + glob.add_argument('--bssid', help=argparse.SUPPRESS, action='store', dest='target_bssid', type=str) - glob.add_argument('--essid', help=argparse.SUPPRESS, action='store', dest='target_essid', type=str) glob.add_argument('-e', action='store', dest='target_essid', metavar='[essid]', type=str, - help=Color.s('ESSID (e.g. {GR}NETGEAR07{W}) of access point to attack')) + help=self._verbose('ESSID (e.g. {GR}NETGEAR07{W}) of access point to attack')) + glob.add_argument('--essid', help=argparse.SUPPRESS, action='store', dest='target_essid', type=str) + + glob.add_argument('-E', + action='store', + dest='ignore_essid', + metavar='[text]', + type=str, + default=None, + help=self._verbose('Hides targets with ESSIDs that match the given text')) + glob.add_argument('--ignore-essid', help=argparse.SUPPRESS, action='store', dest='ignore_essid', type=str) glob.add_argument('--showb', action='store_true', dest='show_bssids', - help=Color.s('Show BSSIDs of targets while scanning')) + help=self._verbose('Show BSSIDs of targets while scanning')) glob.add_argument('--nodeauths', action='store_true', dest='no_deauth', - help=Color.s('Do not deauthenticate clients *EVER* (default: {G}off{W})')) + help=Color.s('Passive mode: Never deauthenticates clients (default: {G}deauth targets{W})')) glob.add_argument('--no-deauths', action='store_true', dest='no_deauth', help=argparse.SUPPRESS) glob.add_argument('-nd', action='store_true', dest='no_deauth', help=argparse.SUPPRESS) @@ -84,151 +136,144 @@ class Arguments(object): dest='num_deauths', metavar="[num]", default=None, - help=Color.s('Number of deauth packets to send (default: {G}%d{W})' % Configuration.num_deauths)) + help=self._verbose('Number of deauth packets to send (default: {G}%d{W})' % self.config.num_deauths)) - glob.add_argument('--pillage', help=argparse.SUPPRESS, action='store', dest='scan_time', nargs='?', const=10, type=int) - glob.add_argument('-p', - action='store', - dest='scan_time', - nargs='?', - const=10, - metavar='scantime', - type=int, - help=Color.s('{G}Pillage{W}: Attack all targets after {C}scantime{W} seconds')) - - glob.add_argument('-v', - '--verbose', - action='count', - default=0, - dest='verbose', - help=Color.s('Verbose mode, prints more lines (default: {G}quiet{W})')) + def _add_wep_args(self, wep): # WEP - wep = parser.add_argument_group('WEP-RELATED') wep.add_argument('--wep', action='store_true', dest='wep_filter', help=Color.s('Filter to display only WEP-encrypted networks (default: {G}off{W})')) wep.add_argument('-wep', help=argparse.SUPPRESS, action='store_true', dest='wep_filter') + wep.add_argument('--require-fakeauth', action='store_true', dest='require_fakeauth', help=Color.s('Fails attacks if fake-auth fails (default: {G}off{W})')) wep.add_argument('--nofakeauth', help=argparse.SUPPRESS, action='store_true', dest='require_fakeauth') wep.add_argument('-nofakeauth', help=argparse.SUPPRESS, action='store_true', dest='require_fakeauth') + wep.add_argument('--pps', action='store', dest='wep_pps', metavar='[pps]', type=int, - help=Color.s('Packets Per Second to replay (default: {G}%d pps{W})') - % Configuration.wep_pps) + help=self._verbose('Packets Per Second to replay (default: {G}%d pps{W})' % self.config.wep_pps)) wep.add_argument('-pps', help=argparse.SUPPRESS, action='store', dest='wep_pps', type=int) + wep.add_argument('--wept', action='store', dest='wep_timeout', metavar='[seconds]', type=int, - help=Color.s('Seconds to wait before failing (default: {G}%d sec{W})') - % Configuration.wep_timeout) + help=self._verbose('Seconds to wait before failing (default: {G}%d sec{W})' % self.config.wep_timeout)) wep.add_argument('-wept', help=argparse.SUPPRESS, action='store', dest='wep_timeout', type=int) + wep.add_argument('--wepca', action='store', dest='wep_crack_at_ivs', metavar='[ivs]', type=int, - help=Color.s('Start cracking at this many IVs (default: {G}%d ivs{W})') - % Configuration.wep_crack_at_ivs) + help=self._verbose('Start cracking at this many IVs (default: {G}%d ivs{W})' % self.config.wep_crack_at_ivs)) wep.add_argument('-wepca', help=argparse.SUPPRESS, action='store', dest='wep_crack_at_ivs', type=int) + wep.add_argument('--weprs', action='store', dest='wep_restart_stale_ivs', metavar='[seconds]', type=int, - help=Color.s('Restart aireplay if no new IVs appear (default: {G}%d sec{W})') - % Configuration.wep_restart_stale_ivs) + help=self._verbose('Restart aireplay if no new IVs appear (default: {G}%d sec{W})' % self.config.wep_restart_stale_ivs)) wep.add_argument('-weprs', help=argparse.SUPPRESS, action='store', dest='wep_restart_stale_ivs', type=int) + wep.add_argument('--weprc', action='store', dest='wep_restart_aircrack', metavar='[seconds]', type=int, - help=Color.s('Restart aircrack after this delay (default: {G}%d sec{W})') - % Configuration.wep_restart_aircrack) + help=self._verbose('Restart aircrack after this delay (default: {G}%d sec{W})' % self.config.wep_restart_aircrack)) wep.add_argument('-weprc', help=argparse.SUPPRESS, action='store', dest='wep_restart_aircrack', type=int) + wep.add_argument('--arpreplay', action='store_true', dest='wep_attack_replay', - help=Color.s('Use ARP-replay WEP attack (default: {G}on{W})')) + help=self._verbose('Use ARP-replay WEP attack (default: {G}on{W})')) wep.add_argument('-arpreplay', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_replay') + wep.add_argument('--fragment', action='store_true', dest='wep_attack_fragment', - help=Color.s('Use fragmentation WEP attack (default: {G}on{W})')) + help=self._verbose('Use fragmentation WEP attack (default: {G}on{W})')) wep.add_argument('-fragment', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_fragment') + wep.add_argument('--chopchop', action='store_true', dest='wep_attack_chopchop', - help=Color.s('Use chop-chop WEP attack (default: {G}on{W})')) + help=self._verbose('Use chop-chop WEP attack (default: {G}on{W})')) wep.add_argument('-chopchop', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_chopchop') + wep.add_argument('--caffelatte', action='store_true', dest='wep_attack_caffe', - help=Color.s('Use caffe-latte WEP attack (default: {G}on{W})')) + help=self._verbose('Use caffe-latte WEP attack (default: {G}on{W})')) wep.add_argument('-caffelatte', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_caffelatte') + wep.add_argument('--p0841', action='store_true', dest='wep_attack_p0841', - help=Color.s('Use p0841 WEP attack (default: {G}on{W})')) + help=self._verbose('Use p0841 WEP attack (default: {G}on{W})')) wep.add_argument('-p0841', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_p0841') + wep.add_argument('--hirte', action='store_true', dest='wep_attack_hirte', - help=Color.s('Use ARP-replay WEP attack (default: {G}on{W})')) + help=self._verbose('Use ARP-replay WEP attack (default: {G}on{W})')) wep.add_argument('-hirte', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_hirte') - # WPA - wpa = parser.add_argument_group('WPA-RELATED') + + def _add_wpa_args(self, wpa): wpa.add_argument('--wpa', action='store_true', dest='wpa_filter', help=Color.s('Filter to display only WPA-encrypted networks (includes WPS)')) wpa.add_argument('-wpa', help=argparse.SUPPRESS, action='store_true', dest='wpa_filter') + wpa.add_argument('--wpadt', action='store', dest='wpa_deauth_timeout', metavar='[seconds]', type=int, - help=Color.s('Time to wait between sending Deauths (default: {G}%d sec{W})') - % Configuration.wpa_deauth_timeout) + help=self._verbose('Time to wait between sending Deauths (default: {G}%d sec{W})' % self.config.wpa_deauth_timeout)) wpa.add_argument('-wpadt', help=argparse.SUPPRESS, action='store', dest='wpa_deauth_timeout', type=int) + wpa.add_argument('--wpat', action='store', dest='wpa_attack_timeout', metavar='[seconds]', type=int, - help=Color.s('Time to wait before failing WPA attack (default: {G}%d sec{W})') - % Configuration.wpa_attack_timeout) + help=self._verbose('Time to wait before failing WPA attack (default: {G}%d sec{W})' % self.config.wpa_attack_timeout)) wpa.add_argument('-wpat', help=argparse.SUPPRESS, action='store', dest='wpa_attack_timeout', type=int) + wpa.add_argument('--new-hs', action='store_true', dest='ignore_old_handshakes', help=Color.s('Captures new handshakes, ignores existing handshakes in ./hs (default: {G}off{W})')) + wpa.add_argument('--hs-dir', action='store', dest='wpa_handshake_dir', metavar='[dir]', type=str, - help=Color.s('Directory to store handshake files (default: {G}%s{W})') - % Configuration.wpa_handshake_dir) + help=self._verbose('Directory to store handshake files (default: {G}%s{W})' % self.config.wpa_handshake_dir)) wpa.add_argument('-hs-dir', help=argparse.SUPPRESS, action='store', dest='wpa_handshake_dir', type=str) + wpa.add_argument('--dict', action='store', dest='wordlist', metavar='[file]', type=str, help=Color.s('File containing passwords for cracking (default: {G}%s{W})') - % Configuration.wordlist) + % self.config.wordlist) # TODO: Uncomment the --strip option once it works ''' @@ -240,8 +285,8 @@ class Arguments(object): ''' wpa.add_argument('-strip', help=argparse.SUPPRESS, action='store_true', dest='wpa_strip_handshake') - # WPS - wps = parser.add_argument_group('WPS-RELATED') + + def _add_wps_args(self, wps): wps.add_argument('--wps', action='store_true', dest='wps_filter', @@ -254,11 +299,11 @@ class Arguments(object): wps.add_argument('--no-wps', action='store_true', dest='no_wps', - help=Color.s('{O}NEVER{W} use WPS attacks (Pixie-Dust) on WPA networks (default: {G}off{W})')) + help=Color.s('{O}NEVER{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})')) wps.add_argument('--wps-only', action='store_true', dest='wps_only', - help=Color.s('{G}ALWAYS{W} use WPS attacks (Pixie-Dust) on WPA networks (default: {G}off{W})')) + help=Color.s('{G}ALWAYS{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})')) # Same as --wps-only wps.add_argument('--pixie', @@ -271,30 +316,26 @@ class Arguments(object): dest='wps_pixie_timeout', metavar='[seconds]', type=int, - help=Color.s('Time to wait before failing PixieDust attack (default: {G}%d sec{W})') - % Configuration.wps_pixie_timeout) + help=self._verbose('Time to wait before failing PixieDust attack (default: {G}%d sec{W})' % self.config.wps_pixie_timeout)) wps.add_argument('--pixiest', action='store', dest='wps_pixie_step_timeout', metavar='[seconds]', type=int, - help=Color.s('Time to wait for a step to progress before failing PixieDust attack (default: {G}%d sec{W})') - % Configuration.wps_pixie_step_timeout) + help=self._verbose('Time to wait for a step to progress before failing PixieDust attack (default: {G}%d sec{W})' % self.config.wps_pixie_step_timeout)) wps.add_argument('--wpsmf', action='store', dest='wps_fail_threshold', metavar='[fails]', type=int, - help=Color.s('Maximum number of WPS Failures before failing attack (default: {G}%d{W})') - % Configuration.wps_fail_threshold) + help=self._verbose('Maximum number of WPS Failures before failing attack (default: {G}%d{W})' % self.config.wps_fail_threshold)) wps.add_argument('-wpsmf', help=argparse.SUPPRESS, action='store', dest='wps_fail_threshold', type=int) wps.add_argument('--wpsmt', action='store', dest='wps_timeout_threshold', metavar='[timeouts]', type=int, - help=Color.s('Maximum number of Timeouts before stopping (default: {G}%d{W})') - % Configuration.wps_timeout_threshold) + help=self._verbose('Maximum number of Timeouts before stopping (default: {G}%d{W})' % self.config.wps_timeout_threshold)) wps.add_argument('-wpsmt', help=argparse.SUPPRESS, action='store', dest='wps_timeout_threshold', type=int) wps.add_argument('--ignore-ratelimit', action='store_false', @@ -302,13 +343,14 @@ class Arguments(object): help=Color.s('Ignores attack if WPS is rate-limited (default: {G}on{W})')) wps.add_argument('-ignore-ratelimit', help=argparse.SUPPRESS, action='store_false', dest='wps_skip_rate_limit') - # Commands - commands = parser.add_argument_group('COMMANDS') + + def _add_command_args(self, commands): commands.add_argument('--cracked', action='store_true', dest='cracked', help=Color.s('Display previously-cracked access points')) commands.add_argument('-cracked', help=argparse.SUPPRESS, action='store_true', dest='cracked') + commands.add_argument('--check', action='store', metavar='file', @@ -317,15 +359,15 @@ class Arguments(object): dest='check_handshake', help=Color.s('Check a .cap file (or all hs/*.cap files) for WPA handshakes')) commands.add_argument('-check', help=argparse.SUPPRESS, action='store', nargs='?', const='', dest='check_handshake') + commands.add_argument('--crack', action='store_true', dest='crack_handshake', help=Color.s('Show commands to crack a captured handshake')) - return parser.parse_args() if __name__ == '__main__': - from Color import Color - from Configuration import Configuration + from util.color import Color + from config import Configuration Configuration.initialize(False) a = Arguments(Configuration) args = a.args diff --git a/wifite/attack/__init__.py b/wifite/attack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py/AttackWEP.py b/wifite/attack/wep.py old mode 100644 new mode 100755 similarity index 96% rename from py/AttackWEP.py rename to wifite/attack/wep.py index 1f94dee..bf74f29 --- a/py/AttackWEP.py +++ b/wifite/attack/wep.py @@ -1,14 +1,14 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Attack import Attack -from Airodump import Airodump -from Aireplay import Aireplay, WEPAttackType -from Aircrack import Aircrack -from Configuration import Configuration -from Interface import Interface -from Color import Color -from CrackResultWEP import CrackResultWEP +from ..model.attack import Attack +from ..tools.airodump import Airodump +from ..tools.aireplay import Aireplay, WEPAttackType +from ..tools.aircrack import Aircrack +from ..config import Configuration +from ..model.interface import Interface +from ..util.color import Color +from ..model.wep_result import CrackResultWEP import time @@ -34,6 +34,7 @@ class AttackWEP(Attack): aircrack = None # Aircrack process, not started yet fakeauth_proc = None replay_file = None + airodump_target = None attacks_remaining = list(Configuration.wep_attacks) while len(attacks_remaining) > 0: @@ -245,6 +246,9 @@ class AttackWEP(Attack): Ask user what attack to perform next (re-orders attacks_remaining, returns False), or if we should stop attacking this target (returns True). ''' + if target is None: + Color.pl("") + return True target_name = target.essid if target.essid_known else target.bssid Color.pl("\n\n{!} {O}Interrupted") @@ -322,9 +326,11 @@ class AttackWEP(Attack): if __name__ == '__main__': - from Target import Target + Configuration.initialize(True) + from ..model.target import Target fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 6, 54e,WEP, WEP, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',') target = Target(fields) wep = AttackWEP(target) wep.run() + Configuration.exit_gracefully(0) diff --git a/py/AttackWPA.py b/wifite/attack/wpa.py old mode 100644 new mode 100755 similarity index 95% rename from py/AttackWPA.py rename to wifite/attack/wpa.py index d2e273c..5403c4f --- a/py/AttackWPA.py +++ b/wifite/attack/wpa.py @@ -1,15 +1,15 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Attack import Attack -from Airodump import Airodump -from Aireplay import Aireplay -from Color import Color -from Configuration import Configuration -from Handshake import Handshake -from Process import Process -from CrackResultWPA import CrackResultWPA -from Timer import Timer +from ..model.attack import Attack +from ..tools.airodump import Airodump +from ..tools.aireplay import Aireplay +from ..config import Configuration +from ..util.color import Color +from ..util.process import Process +from ..util.timer import Timer +from ..model.handshake import Handshake +from ..model.wpa_result import CrackResultWPA import time import os @@ -293,8 +293,14 @@ class AttackWPA(Attack): Aireplay.deauth(target.bssid, client_mac=client, timeout=2) if __name__ == '__main__': - from Target import Target + Configuration.initialize(True) + from ..model.target import Target fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 11, 54e,WPA, WPA, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',') target = Target(fields) wpa = AttackWPA(target) - wpa.run() + try: + wpa.run() + except KeyboardInterrupt: + Color.pl("") + pass + Configuration.exit_gracefully(0) diff --git a/py/AttackWPS.py b/wifite/attack/wps.py old mode 100644 new mode 100755 similarity index 89% rename from py/AttackWPS.py rename to wifite/attack/wps.py index 073b102..906a25f --- a/py/AttackWPS.py +++ b/wifite/attack/wps.py @@ -1,11 +1,11 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Attack import Attack -from Color import Color -from Configuration import Configuration -from Bully import Bully -from Reaver import Reaver +from ..model.attack import Attack +from ..util.color import Color +from ..config import Configuration +from ..tools.bully import Bully +from ..tools.reaver import Reaver class AttackWPS(Attack): def __init__(self, target): diff --git a/py/Configuration.py b/wifite/config.py old mode 100644 new mode 100755 similarity index 97% rename from py/Configuration.py rename to wifite/config.py index f5e36c3..e3bad55 --- a/py/Configuration.py +++ b/wifite/config.py @@ -1,8 +1,8 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Color import Color -from Macchanger import Macchanger +from util.color import Color +from tools.macchanger import Macchanger import os @@ -39,6 +39,7 @@ class Configuration(object): Configuration.target_channel = None # User-defined channel to scan Configuration.target_essid = None # User-defined AP name Configuration.target_bssid = None # User-defined AP BSSID + Configuration.ignore_essid = None # ESSIDs to ignore Configuration.five_ghz = False # Scan 5Ghz channels Configuration.show_bssids = False # Show BSSIDs in targets list Configuration.random_mac = False # Should generate a random Mac address at startup. @@ -107,7 +108,7 @@ class Configuration(object): def get_interface(): if Configuration.interface is None: # Interface wasn't defined, select it! - from Airmon import Airmon + from tools.airmon import Airmon Configuration.interface = Airmon.ask() if Configuration.random_mac: Macchanger.random() @@ -116,7 +117,7 @@ class Configuration(object): @staticmethod def load_from_arguments(): ''' Sets configuration values based on Argument.args object ''' - from Arguments import Arguments + from args import Arguments args = Arguments(Configuration).args if args.random_mac: @@ -146,6 +147,9 @@ class Configuration(object): if args.target_essid: Configuration.target_essid = args.target_essid Color.pl('{+} {C}option:{W} targeting ESSID {G}%s{W}' % args.target_essid) + if args.ignore_essid is not None: + Configuration.ignore_essid = args.ignore_essid + Color.pl('{+} {C}option:{W} {O}ignoring ESSIDs that include {R}%s{W}' % args.ignore_essid) if args.scan_time: Configuration.scan_time = args.scan_time Color.pl('{+} {C}option:{W} ({G}pillage{W}) attack all targets after {G}%d{W}s' % args.scan_time) @@ -311,7 +315,7 @@ class Configuration(object): ''' Deletes temp and exist with the given code ''' Configuration.delete_temp() Macchanger.reset_if_changed() - from Airmon import Airmon + from tools.airmon import Airmon if hasattr(Configuration, "interface") and Configuration.interface is not None and Airmon.base_interface is not None: Airmon.stop(Configuration.interface) Airmon.put_interface_up(Airmon.base_interface) @@ -324,7 +328,7 @@ class Configuration(object): @staticmethod def dump(): ''' (Colorful) string representation of the configuration ''' - from Color import Color + from util.color import Color max_len = 20 for key in Configuration.__dict__.keys(): diff --git a/wifite/model/__init__.py b/wifite/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py/Attack.py b/wifite/model/attack.py old mode 100644 new mode 100755 similarity index 100% rename from py/Attack.py rename to wifite/model/attack.py diff --git a/py/Client.py b/wifite/model/client.py old mode 100644 new mode 100755 similarity index 100% rename from py/Client.py rename to wifite/model/client.py diff --git a/py/Handshake.py b/wifite/model/handshake.py old mode 100644 new mode 100755 similarity index 99% rename from py/Handshake.py rename to wifite/model/handshake.py index 78ce6a6..a93df72 --- a/py/Handshake.py +++ b/wifite/model/handshake.py @@ -1,8 +1,8 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Process import Process -from Color import Color +from ..util.process import Process +from ..util.color import Color import re import os diff --git a/py/Interface.py b/wifite/model/interface.py old mode 100644 new mode 100755 similarity index 95% rename from py/Interface.py rename to wifite/model/interface.py index b08b89a..9fd8595 --- a/py/Interface.py +++ b/wifite/model/interface.py @@ -1,7 +1,7 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Color import Color +from ..util.color import Color import re @@ -78,9 +78,8 @@ class Interface(object): @staticmethod def get_mac(iface=None): - from Configuration import Configuration - from Process import Process - import re + from ..config import Configuration + from ..util.process import Process if iface is None: Configuration.initialize() diff --git a/py/CrackResult.py b/wifite/model/result.py old mode 100644 new mode 100755 similarity index 94% rename from py/CrackResult.py rename to wifite/model/result.py index 0e8768c..10d7677 --- a/py/CrackResult.py +++ b/wifite/model/result.py @@ -1,7 +1,7 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Color import Color +from ..util.color import Color import os import time @@ -50,20 +50,20 @@ class CrackResult(object): def load(json): ''' Returns an instance of the appropriate object given a json instance ''' if json['type'] == 'WPA': - from CrackResultWPA import CrackResultWPA + from .wpa_result import CrackResultWPA result = CrackResultWPA(json['bssid'], json['essid'], json['handshake_file'], json['key']) elif json['type'] == 'WEP': - from CrackResultWEP import CrackResultWEP + from .wep_result import CrackResultWEP result = CrackResultWEP(json['bssid'], json['essid'], json['hex_key'], json['ascii_key']) elif json['type'] == 'WPS': - from CrackResultWPS import CrackResultWPS + from .wps_result import CrackResultWPS result = CrackResultWPS(json['bssid'], json['essid'], json['pin'], diff --git a/py/Target.py b/wifite/model/target.py old mode 100644 new mode 100755 similarity index 99% rename from py/Target.py rename to wifite/model/target.py index bd68a99..39c3f97 --- a/py/Target.py +++ b/wifite/model/target.py @@ -1,7 +1,7 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Color import Color +from ..util.color import Color import re diff --git a/py/CrackResultWEP.py b/wifite/model/wep_result.py old mode 100644 new mode 100755 similarity index 94% rename from py/CrackResultWEP.py rename to wifite/model/wep_result.py index 31f73d9..4bbdf98 --- a/py/CrackResultWEP.py +++ b/wifite/model/wep_result.py @@ -1,8 +1,8 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Color import Color -from CrackResult import CrackResult +from ..util.color import Color +from .result import CrackResult import time diff --git a/py/CrackResultWPA.py b/wifite/model/wpa_result.py old mode 100644 new mode 100755 similarity index 96% rename from py/CrackResultWPA.py rename to wifite/model/wpa_result.py index f7b348e..dbdea78 --- a/py/CrackResultWPA.py +++ b/wifite/model/wpa_result.py @@ -1,8 +1,8 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Color import Color -from CrackResult import CrackResult +from ..util.color import Color +from .result import CrackResult class CrackResultWPA(CrackResult): def __init__(self, bssid, essid, handshake_file, key): diff --git a/py/CrackResultWPS.py b/wifite/model/wps_result.py old mode 100644 new mode 100755 similarity index 94% rename from py/CrackResultWPS.py rename to wifite/model/wps_result.py index 87b543c..5673101 --- a/py/CrackResultWPS.py +++ b/wifite/model/wps_result.py @@ -1,8 +1,8 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Color import Color -from CrackResult import CrackResult +from ..util.color import Color +from ..model.result import CrackResult import time diff --git a/wifite/tools/__init__.py b/wifite/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py/Aircrack.py b/wifite/tools/aircrack.py old mode 100644 new mode 100755 similarity index 96% rename from py/Aircrack.py rename to wifite/tools/aircrack.py index 751b128..d0c73f0 --- a/py/Aircrack.py +++ b/wifite/tools/aircrack.py @@ -1,8 +1,8 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Process import Process -from Configuration import Configuration +from ..util.process import Process +from ..config import Configuration import os diff --git a/py/Aireplay.py b/wifite/tools/aireplay.py old mode 100644 new mode 100755 similarity index 98% rename from py/Aireplay.py rename to wifite/tools/aireplay.py index 6d5b799..c0c71d2 --- a/py/Aireplay.py +++ b/wifite/tools/aireplay.py @@ -1,9 +1,9 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Configuration import Configuration -from Process import Process -from Timer import Timer +from ..config import Configuration +from ..util.process import Process +from ..util.timer import Timer import os, time, re from threading import Thread @@ -314,7 +314,7 @@ class Aireplay(Thread): if out.strip() == 'Wrote packet to: %s' % forged_file: return forged_file else: - from Color import Color + from ..util.color import Color Color.pl('{!} {R}failed to forge packet from .xor file{W}') Color.pl('output:\n"%s"' % out) return None @@ -385,7 +385,7 @@ if __name__ == '__main__': t = WEPAttackType(t) print t.name, type(t.name), t.value - from Target import Target + from ..model.target import Target fields = 'A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 6, 54e, WEP, WEP, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, '.split(',') t = Target(fields) diff --git a/py/Airmon.py b/wifite/tools/airmon.py old mode 100644 new mode 100755 similarity index 90% rename from py/Airmon.py rename to wifite/tools/airmon.py index f12706c..352bd0b --- a/py/Airmon.py +++ b/wifite/tools/airmon.py @@ -1,10 +1,10 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Interface import Interface -from Process import Process -from Color import Color -from Configuration import Configuration +from ..model.interface import Interface +from ..util.process import Process +from ..util.color import Color +from ..config import Configuration import re import os @@ -15,6 +15,10 @@ class Airmon(object): base_interface = None killed_network_manager = False + #see if_arp.h + ARPHRD_ETHER = 1 #managed + ARPHRD_IEEE80211_RADIOTAP = 803 #monitor + def __init__(self): self.refresh() @@ -56,6 +60,24 @@ class Airmon(object): interfaces.append(Interface(fields)) return interfaces + @staticmethod + def start_baddriver(iface): #fix for bad drivers like the rtl8812AU + os.system("ifconfig %s down; iwconfig %s mode monitor; ifconfig %s up" % (iface, iface, iface)) + with open("/sys/class/net/" + iface + "/type", "r") as f: + if (int(f.read()) == Airmon.ARPHRD_IEEE80211_RADIOTAP): + return iface + + return None + + @staticmethod + def stop_baddriver(iface): + os.system("ifconfig %s down; iwconfig %s mode managed; ifconfig %s up" % (iface, iface, iface)) + with open("/sys/class/net/" + iface + "/type", "r") as f: + if (int(f.read()) == Airmon.ARPHRD_ETHER): + return iface + + return None + @staticmethod def start(iface): ''' @@ -91,7 +113,9 @@ class Airmon(object): if mon_iface is None: # Airmon did not enable monitor mode on an interface - Color.pl("{R}failed{W}") + mon_iface = Airmon.start_baddriver(iface) + if mon_iface is None: + Color.pl("{R}failed{W}") mon_ifaces = Airmon.get_interfaces_in_monitor_mode() @@ -134,6 +158,9 @@ class Airmon(object): mon_iface = match.groups()[0] break + if not mon_iface: + mon_iface = Airmon.stop_baddriver(iface) + if mon_iface: Color.pl('{R}disabled %s{W}' % mon_iface) else: diff --git a/py/Airodump.py b/wifite/tools/airodump.py old mode 100644 new mode 100755 similarity index 95% rename from py/Airodump.py rename to wifite/tools/airodump.py index 3a5fb46..7e52d7d --- a/py/Airodump.py +++ b/wifite/tools/airodump.py @@ -1,11 +1,11 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Process import Process -from Configuration import Configuration -from Target import Target -from Client import Client -from Tshark import Tshark +from .tshark import Tshark +from ..util.process import Process +from ..config import Configuration +from ..model.target import Target +from ..model.client import Client import os, time @@ -241,7 +241,9 @@ class Airodump(object): essid = Configuration.target_essid i = 0 while i < len(result): - if bssid and result[i].bssid.lower() != bssid.lower(): + if result[i].essid is not None and Configuration.ignore_essid is not None and Configuration.ignore_essid.lower() in result[i].essid.lower(): + result.pop(i) + elif 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) @@ -278,7 +280,7 @@ class Airodump(object): self.decloaking = True self.decloaked_times[target.bssid] = now if Configuration.verbose > 1: - from Color import Color + from ..util.color import Color verbout = " [?] Deauthing %s" % target.bssid verbout += " (broadcast & %d clients)" % len(target.clients) Color.pe("\n{C}" + verbout + "{W}") @@ -296,7 +298,7 @@ if __name__ == '__main__': from time import sleep sleep(7) - from Color import Color + from ..util.color import Color targets = airodump.get_targets() for idx, target in enumerate(targets, start=1): diff --git a/py/Bully.py b/wifite/tools/bully.py old mode 100644 new mode 100755 similarity index 96% rename from py/Bully.py rename to wifite/tools/bully.py index 2cd72fc..c083e02 --- a/py/Bully.py +++ b/wifite/tools/bully.py @@ -1,13 +1,13 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Attack import Attack -from Airodump import Airodump -from Color import Color -from Timer import Timer -from Process import Process -from Configuration import Configuration -from CrackResultWPS import CrackResultWPS +from ..model.attack import Attack +from ..tools.airodump import Airodump +from ..util.color import Color +from ..util.timer import Timer +from ..util.process import Process +from ..config import Configuration +from ..model.wps_result import CrackResultWPS import os, time, re from threading import Thread @@ -217,7 +217,7 @@ class Bully(Attack): if __name__ == '__main__': stdout = " [*] Pin is '11867722', key is '9a6f7997'" Configuration.initialize(False) - from Target import Target + from ..model.target import Target fields = 'AA:BB:CC:DD:EE:FF,2015-05-27 19:28:44,2015-05-27 19:28:46,1,54,WPA2,CCMP TKIP,PSK,-58,2,0,0.0.0.0,9,HOME-ABCD,'.split(',') target = Target(fields) b = Bully(target) diff --git a/py/Macchanger.py b/wifite/tools/macchanger.py old mode 100644 new mode 100755 similarity index 90% rename from py/Macchanger.py rename to wifite/tools/macchanger.py index 5876bdc..46cd81d --- a/py/Macchanger.py +++ b/wifite/tools/macchanger.py @@ -1,8 +1,8 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Interface import Interface -from Color import Color +from ..model.interface import Interface +from ..util.color import Color class Macchanger(object): is_init = False @@ -12,7 +12,7 @@ class Macchanger(object): @classmethod def init(cls): if cls.is_init: return - from Configuration import Configuration + from ..config import Configuration iface = Configuration.interface if type(iface) == Interface: iface = iface.name @@ -21,8 +21,8 @@ class Macchanger(object): @classmethod def down_macch_up(cls, macch_option): cls.init() - from Process import Process - from Configuration import Configuration + from ..util.process import Process + from ..config import Configuration iface = Configuration.interface cmd = ["ifconfig", iface, "down"] @@ -61,7 +61,7 @@ class Macchanger(object): # --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 + from ..config 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) @@ -71,7 +71,7 @@ class Macchanger(object): # Use --permanent to use random MAC address if not cls.down_macch_up("-r"): return cls.is_changed = True - from Configuration import Configuration + from ..config 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) diff --git a/py/Reaver.py b/wifite/tools/reaver.py old mode 100644 new mode 100755 similarity index 98% rename from py/Reaver.py rename to wifite/tools/reaver.py index 4d84373..5a3fe48 --- a/py/Reaver.py +++ b/wifite/tools/reaver.py @@ -1,12 +1,12 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Attack import Attack -from Airodump import Airodump -from Color import Color -from Configuration import Configuration -from CrackResultWPS import CrackResultWPS -from Process import Process +from ..model.attack import Attack +from ..config import Configuration +from ..util.color import Color +from ..util.process import Process +from ..tools.airodump import Airodump +from ..model.wps_result import CrackResultWPS import os, time, re diff --git a/py/Tshark.py b/wifite/tools/tshark.py old mode 100644 new mode 100755 similarity index 97% rename from py/Tshark.py rename to wifite/tools/tshark.py index b55ac73..36a0d0f --- a/py/Tshark.py +++ b/wifite/tools/tshark.py @@ -1,7 +1,7 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Process import Process +from ..util.process import Process import re class Tshark(object): @@ -63,7 +63,7 @@ if __name__ == '__main__': test_file = './tests/files/contains_wps_network.cap' target_bssid = 'A4:2B:8C:16:6B:3A' - from Target import Target + from ..model.target import Target fields = [ 'A4:2B:8C:16:6B:3A', # BSSID '2015-05-27 19:28:44', '2015-05-27 19:28:46', # Dates diff --git a/wifite/util/__init__.py b/wifite/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py/Color.py b/wifite/util/color.py old mode 100644 new mode 100755 similarity index 100% rename from py/Color.py rename to wifite/util/color.py diff --git a/py/CrackHandshake.py b/wifite/util/crack.py old mode 100644 new mode 100755 similarity index 97% rename from py/CrackHandshake.py rename to wifite/util/crack.py index c8eb261..b0c1d6f --- a/py/CrackHandshake.py +++ b/wifite/util/crack.py @@ -1,10 +1,11 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Process import Process -from Color import Color -from Configuration import Configuration -from CrackResult import CrackResult +from ..util.process import Process +from ..util.color import Color +from ..config import Configuration +from ..model.result import CrackResult + from datetime import datetime import os diff --git a/py/Process.py b/wifite/util/process.py old mode 100644 new mode 100755 similarity index 96% rename from py/Process.py rename to wifite/util/process.py index ef78de1..76c22cc --- a/py/Process.py +++ b/wifite/util/process.py @@ -4,8 +4,8 @@ import time from subprocess import Popen, PIPE -from Color import Color -from Configuration import Configuration +from ..util.color import Color +from ..config import Configuration class Process(object): @@ -37,9 +37,9 @@ class Process(object): (stdout, stderr) = pid.communicate() if Configuration.verbose > 1 and stdout.strip() != '': - Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(stdout.split('\n'))) + Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(stdout.strip().split('\n'))) if Configuration.verbose > 1 and stderr.strip() != '': - Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(stderr.split('\n'))) + Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(stderr.strip().split('\n'))) return (stdout, stderr) @@ -92,14 +92,14 @@ class Process(object): ''' Waits for process to finish, returns stdout output ''' self.get_output() if Configuration.verbose > 1 and self.out.strip() != '': - Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(self.out.split('\n'))) + Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(self.out.strip().split('\n'))) return self.out def stderr(self): ''' Waits for process to finish, returns stderr output ''' self.get_output() if Configuration.verbose > 1 and self.err.strip() != '': - Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(self.err.split('\n'))) + Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(self.err.strip().split('\n'))) return self.err def stdoutln(self): diff --git a/py/Scanner.py b/wifite/util/scanner.py old mode 100644 new mode 100755 similarity index 96% rename from py/Scanner.py rename to wifite/util/scanner.py index 3af1f4c..7942a4c --- a/py/Scanner.py +++ b/wifite/util/scanner.py @@ -1,10 +1,10 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from Airodump import Airodump -from Color import Color -from Target import Target -from Configuration import Configuration +from ..tools.airodump import Airodump +from ..util.color import Color +from ..model.target import Target +from ..config import Configuration from time import sleep, time @@ -88,6 +88,8 @@ class Scanner(object): return False for target in self.targets: + if Configuration.wps_only and target.wps != True: + continue if bssid and target.bssid and bssid.lower() == target.bssid.lower(): self.target = target break @@ -120,7 +122,7 @@ class Scanner(object): # 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 + from ..util.process import Process Process.call('clear') else: # We can fit the targets in the terminal without scrolling diff --git a/py/Timer.py b/wifite/util/timer.py old mode 100644 new mode 100755 similarity index 100% rename from py/Timer.py rename to wifite/util/timer.py diff --git a/wifite/wifite.py b/wifite/wifite.py new file mode 100755 index 0000000..196ef8f --- /dev/null +++ b/wifite/wifite.py @@ -0,0 +1,258 @@ +#!/usr/bin/python2.7 +# -*- coding: utf-8 -*- + +try: + from config import Configuration +except (ValueError, ImportError) as e: + raise Exception('You may need to run wifite from the root directory (which includes README.md)') + +from util.scanner import Scanner +from util.process import Process +from util.color import Color +from util.crack import CrackHandshake +from attack.wep import AttackWEP +from attack.wpa import AttackWPA +from attack.wps import AttackWPS +from model.result import CrackResult +from model.handshake import Handshake + +import json +import os +import sys + +class Wifite(object): + + def main(self): + ''' Either performs action based on arguments, or starts attack scanning ''' + + if os.getuid() != 0: + Color.pl('{!} {R}error: {O}wifite{R} must be run as {O}root{W}') + Color.pl('{!} {O}re-run as: sudo ./Wifite.py{W}') + Configuration.exit_gracefully(0) + + self.dependency_check() + + Configuration.initialize(load_interface=False) + + if Configuration.show_cracked: + self.display_cracked() + + elif Configuration.check_handshake: + self.check_handshake(Configuration.check_handshake) + elif Configuration.crack_handshake: + CrackHandshake() + else: + Configuration.get_interface() + self.run() + + def dependency_check(self): + ''' Check that required programs are installed ''' + required_apps = ['airmon-ng', 'iwconfig', 'ifconfig', 'aircrack-ng', 'aireplay-ng', 'airodump-ng', 'tshark'] + optional_apps = ['packetforge-ng', 'reaver', 'bully', 'cowpatty', 'pyrit', 'stdbuf', 'macchanger'] + missing_required = False + missing_optional = False + + for app in required_apps: + if not Process.exists(app): + missing_required = True + Color.pl('{!} {R}error: required app {O}%s{R} was not found' % app) + + for app in optional_apps: + if not Process.exists(app): + missing_optional = True + Color.pl('{!} {O}warning: recommended app {R}%s{O} was not found' % app) + + if missing_required: + Color.pl('{!} {R}required app(s) were not found, exiting.{W}') + sys.exit(-1) + + if missing_optional: + Color.pl('{!} {O}recommended app(s) were not found') + Color.pl('{!} {O}wifite may not work as expected{W}') + + def display_cracked(self): + ''' Show cracked targets from cracked.txt ''' + name = CrackResult.cracked_file + if not os.path.exists(name): + Color.pl('{!} {O}file {C}%s{O} not found{W}' % name) + return + + with open(name, 'r') as fid: + cracked_targets = json.loads(fid.read()) + + if len(cracked_targets) == 0: + Color.pl('{!} {R}no results found in {O}%s{W}' % name) + else: + Color.pl('{+} displaying {G}%d {C}cracked target(s){W}\n' % len(cracked_targets)) + for item in cracked_targets: + cr = CrackResult.load(item) + cr.dump() + Color.pl('') + + def check_handshake(self, capfile): + ''' Analyzes .cap file for handshake ''' + if capfile == '': + Color.pl('{+} checking all handshakes in {G}"./hs"{W} directory\n') + try: + capfiles = [os.path.join('hs', x) for x in os.listdir('hs') if x.endswith('.cap')] + except OSError, e: + capfiles = [] + if len(capfiles) == 0: + Color.pl('{!} {R}no .cap files found in {O}"./hs"{W}\n') + else: + capfiles = [capfile] + + for capfile in capfiles: + Color.pl('{+} checking for handshake in .cap file {C}%s{W}' % capfile) + if not os.path.exists(capfile): + Color.pl('{!} {O}.cap file {C}%s{O} not found{W}' % capfile) + return + hs = Handshake(capfile, bssid=Configuration.target_bssid, essid=Configuration.target_essid) + hs.analyze() + Color.pl('') + + def run(self): + ''' + Main program. + 1) Scans for targets, asks user to select targets + 2) Attacks each target + ''' + s = Scanner() + if s.target: + # We found the target we want + targets = [s.target] + else: + targets = s.select_targets() + + attacked_targets = 0 + targets_remaining = len(targets) + for idx, t in enumerate(targets, start=1): + attacked_targets += 1 + targets_remaining -= 1 + + Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (idx, len(targets)) + + ' starting attacks against {C}%s{W} ({C}%s{W})' + % (t.bssid, t.essid if t.essid_known else "{O}ESSID unknown")) + if 'WEP' in t.encryption: + attack = AttackWEP(t) + elif 'WPA' in t.encryption: + if t.wps: + attack = AttackWPS(t) + result = False + try: + result = attack.run() + except Exception as e: + Color.pl("\n{!} {R}Error: {O}%s" % str(e)) + if Configuration.verbose > 0 or Configuration.print_stack_traces: + Color.pl('\n{!} {O}Full stack trace below') + from traceback import format_exc + Color.p('\n{!} ') + err = format_exc().strip() + err = err.replace('\n', '\n{!} {C} ') + err = err.replace(' File', '{W}File') + err = err.replace(' Exception: ', '{R}Exception: {O}') + Color.pl(err) + except KeyboardInterrupt: + Color.pl('\n{!} {O}interrupted{W}\n') + if not self.user_wants_to_continue(targets_remaining, 1): + break + + if result and attack.success: + # We cracked it. + attack.crack_result.save() + continue + else: + # WPS failed, try WPA handshake. + attack = AttackWPA(t) + else: + # Not using WPS, try WPA handshake. + attack = AttackWPA(t) + else: + Color.pl("{!} {R}Error: {O}unable to attack: encryption not WEP or WPA") + continue + + try: + attack.run() + except Exception, e: + Color.pl("\n{!} {R}Error: {O}%s" % str(e)) + if Configuration.verbose > 0 or True: + Color.pl('\n{!} {O}Full stack trace below') + from traceback import format_exc + Color.p('\n{!} ') + err = format_exc().strip() + err = err.replace('\n', '\n{!} {C} ') + err = err.replace(' File', '{W}File') + err = err.replace(' Exception: ', '{R}Exception: {O}') + Color.pl(err) + except KeyboardInterrupt: + Color.pl('\n{!} {O}interrupted{W}\n') + if not self.user_wants_to_continue(targets_remaining): + break + + if attack.success: + attack.crack_result.save() + Color.pl("{+} Finished attacking {C}%d{W} target(s), exiting" % attacked_targets) + + + def print_banner(self): + """ Displays ASCII art of the highest caliber. """ + Color.pl('''\ +{G} . {GR}{D} {W}{G} . {W} +{G}.´ · .{GR}{D} {W}{G}. · `. {G}wifite {D}%s{W} +{G}: : : {GR}{D} (¯) {W}{G} : : : {W}{D}automated wireless auditor +{G}`. · `{GR}{D} /¯\ {W}{G}´ · .´ {C}{D}https://github.com/derv82/wifite2 +{G} ` {GR}{D}/¯¯¯\{W}{G} ´ {W} +''' % Configuration.version) + + def user_wants_to_continue(self, targets_remaining, attacks_remaining=0): + ''' Asks user if attacks should continue onto other targets ''' + if attacks_remaining == 0 and targets_remaining == 0: + # No targets or attacksleft, drop out + return + + prompt_list = [] + if attacks_remaining > 0: + prompt_list.append(Color.s('{C}%d{W} attack(s)' % attacks_remaining)) + if targets_remaining > 0: + prompt_list.append(Color.s('{C}%d{W} target(s)' % targets_remaining)) + prompt = ' and '.join(prompt_list) + Color.pl('{+} %s remain, do you want to continue?' % prompt) + + prompt = Color.s('{+} type {G}c{W} to {G}continue{W}' + + ' or {R}s{W} to {R}stop{W}: ') + + if raw_input(prompt).lower().startswith('s'): + return False + else: + return True + + +def run(): + w = Wifite() + w.print_banner() + + try: + w.main() + + except Exception, e: + Color.pl('\n{!} {R}Error:{O} %s{W}' % str(e)) + + if Configuration.verbose > 0 or True: + Color.pl('\n{!} {O}Full stack trace below') + from traceback import format_exc + Color.p('\n{!} ') + err = format_exc().strip() + err = err.replace('\n', '\n{!} {C} ') + err = err.replace(' File', '{W}File') + err = err.replace(' Exception: ', '{R}Exception: {O}') + Color.pl(err) + + Color.pl('\n{!} {R}Exiting{W}\n') + + except KeyboardInterrupt: + Color.pl('\n{!} {O}interrupted, shutting down...{W}') + + Configuration.exit_gracefully(0) + +if __name__ == '__main__': + run()