diff --git a/wifite/args.py b/wifite/args.py index 7891e41..e6059fd 100755 --- a/wifite/args.py +++ b/wifite/args.py @@ -37,6 +37,9 @@ class Arguments(object): wps_group = parser.add_argument_group('WPS') self._add_wps_args(wps_group) + eviltwin_group = parser.add_argument_group('EVIL TWIN') + self._add_eviltwin_args(eviltwin_group) + commands_group = parser.add_argument_group('COMMANDS') self._add_command_args(commands_group) @@ -144,6 +147,15 @@ class Arguments(object): help=self._verbose('Number of deauth packets to send (default: {G}%d{W})' % self.config.num_deauths)) + def _add_eviltwin_args(self, group): + group.add_argument('-ev', + '--eviltwin', + action='store_true', + dest='use_eviltwin', + help=Color.s('Use the "Evil Twin" attack against all targets (default: {G}off{W})')) + # TODO: Args to specify deauth interface, server port, etc. + + def _add_wep_args(self, wep): # WEP wep.add_argument('--wep', diff --git a/wifite/config.py b/wifite/config.py index 101adc3..1ac19ad 100755 --- a/wifite/config.py +++ b/wifite/config.py @@ -15,65 +15,73 @@ class Configuration(object): interface = None verbose = 0 - @staticmethod - def initialize(load_interface=True): + @classmethod + def initialize(cls, load_interface=True): ''' Sets up default initial configuration values. Also sets config values based on command-line arguments. ''' + # TODO: categorize configuration into separate classes (under config/*.py) + # E.g. Configuration.wps.enabled, Configuration.wps.timeout, etc # Only initialize this class once - if Configuration.initialized: + if cls.initialized: return - Configuration.initialized = True + cls.initialized = True - Configuration.verbose = 0 # Verbosity level. - Configuration.print_stack_traces = True + cls.verbose = 0 # Verbosity of output. Higher number means more debug info about running processes. + cls.print_stack_traces = True - Configuration.kill_conflicting_processes = False + cls.kill_conflicting_processes = False - Configuration.scan_time = 0 # Time to wait before attacking all targets - Configuration.all_targets = False # Run attacks against all targets automatically + cls.scan_time = 0 # Time to wait before attacking all targets + cls.all_targets = False # Run attacks against all targets automatically - Configuration.tx_power = 0 # Wifi transmit power (0 is default) - Configuration.interface = None - 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.clients_only = False # Only show targets that have associated clients - 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. - Configuration.no_deauth = False # Deauth hidden networks & WPA handshake targets - Configuration.num_deauths = 1 # Number of deauth packets to send to each target. + cls.tx_power = 0 # Wifi transmit power (0 is default) + cls.interface = None + cls.target_channel = None # User-defined channel to scan + cls.target_essid = None # User-defined AP name + cls.target_bssid = None # User-defined AP BSSID + cls.ignore_essid = None # ESSIDs to ignore + cls.clients_only = False # Only show targets that have associated clients + cls.five_ghz = False # Scan 5Ghz channels + cls.show_bssids = False # Show BSSIDs in targets list + cls.random_mac = False # Should generate a random Mac address at startup. + cls.no_deauth = False # Deauth hidden networks & WPA handshake targets + cls.num_deauths = 1 # Number of deauth packets to send to each target. - Configuration.encryption_filter = ['WEP', 'WPA', 'WPS'] + cls.encryption_filter = ['WEP', 'WPA', 'WPS'] + + # EvilTwin variables + cls.use_eviltwin = False + cls.eviltwin_port = 80 + cls.eviltwin_deauth_iface = None + cls.eviltwin_fakeap_iface = None # WEP variables - Configuration.wep_filter = False # Only attack WEP networks - Configuration.wep_pps = 600 # Packets per second - Configuration.wep_timeout = 600 # Seconds to wait before failing - Configuration.wep_crack_at_ivs = 10000 # Minimum IVs to start cracking - Configuration.require_fakeauth = False - Configuration.wep_restart_stale_ivs = 11 # Seconds to wait before restarting + cls.wep_filter = False # Only attack WEP networks + cls.wep_pps = 600 # Packets per second + cls.wep_timeout = 600 # Seconds to wait before failing + cls.wep_crack_at_ivs = 10000 # Minimum IVs to start cracking + cls.require_fakeauth = False + cls.wep_restart_stale_ivs = 11 # Seconds to wait before restarting # Aireplay if IVs don't increaes. # "0" means never restart. - Configuration.wep_restart_aircrack = 30 # Seconds to give aircrack to crack + cls.wep_restart_aircrack = 30 # Seconds to give aircrack to crack # before restarting the process. - Configuration.wep_crack_at_ivs = 10000 # Number of IVS to start cracking - Configuration.wep_keep_ivs = False # Retain .ivs files across multiple attacks. + cls.wep_crack_at_ivs = 10000 # Number of IVS to start cracking + cls.wep_keep_ivs = False # Retain .ivs files across multiple attacks. # WPA variables - Configuration.wpa_filter = False # Only attack WPA networks - Configuration.wpa_deauth_timeout = 15 # Wait time between deauths - Configuration.wpa_attack_timeout = 500 # Wait time before failing - Configuration.wpa_handshake_dir = "hs" # Dir to store handshakes - Configuration.wpa_strip_handshake = False # Strip non-handshake packets - Configuration.ignore_old_handshakes = False # Always fetch a new handshake + cls.wpa_filter = False # Only attack WPA networks + cls.wpa_deauth_timeout = 15 # Wait time between deauths + cls.wpa_attack_timeout = 500 # Wait time before failing + cls.wpa_handshake_dir = "hs" # Dir to store handshakes + cls.wpa_strip_handshake = False # Strip non-handshake packets + cls.ignore_old_handshakes = False # Always fetch a new handshake # Default dictionary for cracking - Configuration.wordlist = None + cls.wordlist = None wordlists = [ '/usr/share/wfuzz/wordlist/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt', '/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt', @@ -81,219 +89,224 @@ class Configuration(object): ] for wlist in wordlists: if os.path.exists(wlist): - Configuration.wordlist = wlist + cls.wordlist = wlist break # WPS variables - Configuration.wps_filter = False # Only attack WPS networks - Configuration.no_wps = False # Do not use WPS attacks (Pixie-Dust & PIN attacks) - Configuration.wps_only = False # ONLY use WPS attacks on non-WEP networks - Configuration.use_bully = False # Use bully instead of reaver - Configuration.wps_pixie_timeout = 300 # Seconds to wait for PIN before WPS Pixie attack fails - Configuration.wps_fail_threshold = 100 # Max number of failures - Configuration.wps_timeout_threshold = 100 # Max number of timeouts + cls.wps_filter = False # Only attack WPS networks + cls.no_wps = False # Do not use WPS attacks (Pixie-Dust & PIN attacks) + cls.wps_only = False # ONLY use WPS attacks on non-WEP networks + cls.use_bully = False # Use bully instead of reaver + cls.wps_pixie_timeout = 300 # Seconds to wait for PIN before WPS Pixie attack fails + cls.wps_fail_threshold = 100 # Max number of failures + cls.wps_timeout_threshold = 100 # Max number of timeouts # Commands - Configuration.show_cracked = False - Configuration.check_handshake = None - Configuration.crack_handshake = False + cls.show_cracked = False + cls.check_handshake = None + cls.crack_handshake = False # Overwrite config values with arguments (if defined) - Configuration.load_from_arguments() + cls.load_from_arguments() if load_interface: - Configuration.get_monitor_mode_interface() + cls.get_monitor_mode_interface() - @staticmethod - def get_monitor_mode_interface(): - if Configuration.interface is None: + @classmethod + def get_monitor_mode_interface(cls): + if cls.interface is None: # Interface wasn't defined, select it! from .tools.airmon import Airmon - Configuration.interface = Airmon.ask() - if Configuration.random_mac: + cls.interface = Airmon.ask() + if cls.random_mac: Macchanger.random() @staticmethod def get_wireless_interface(): pass - @staticmethod - def load_from_arguments(): + @classmethod + def load_from_arguments(cls): ''' Sets configuration values based on Argument.args object ''' from .args import Arguments - args = Arguments(Configuration).args + args = Arguments(cls).args if args.random_mac: - Configuration.random_mac = True + cls.random_mac = True Color.pl('{+} {C}option:{W} using {G}random mac address{W} when scanning & attacking') if args.channel: - Configuration.target_channel = args.channel + cls.target_channel = args.channel Color.pl('{+} {C}option:{W} scanning for targets on channel {G}%s{W}' % args.channel) if args.interface: - Configuration.interface = args.interface + cls.interface = args.interface Color.pl('{+} {C}option:{W} using wireless interface {G}%s{W}' % args.interface) if args.target_bssid: - Configuration.target_bssid = args.target_bssid + cls.target_bssid = args.target_bssid Color.pl('{+} {C}option:{W} targeting BSSID {G}%s{W}' % args.target_bssid) if args.five_ghz == True: - Configuration.five_ghz = True + cls.five_ghz = True Color.pl('{+} {C}option:{W} including {G}5Ghz networks{W} in scans') if args.show_bssids == True: - Configuration.show_bssids = True + cls.show_bssids = True Color.pl('{+} {C}option:{W} showing {G}bssids{W} of targets during scan') if args.no_deauth == True: - Configuration.no_deauth = True + cls.no_deauth = True Color.pl('{+} {C}option:{W} will {R}not{W} {O}deauth{W} clients during scans or captures') if args.num_deauths and args.num_deauths > 0: - Configuration.num_deauths = args.num_deauths - Color.pl('{+} {C}option:{W} will send {G}%d{W} deauth packets when deauthing' % Configuration.num_deauths) + cls.num_deauths = args.num_deauths + Color.pl('{+} {C}option:{W} will send {G}%d{W} deauth packets when deauthing' % cls.num_deauths) if args.target_essid: - Configuration.target_essid = args.target_essid + cls.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 + cls.ignore_essid = args.ignore_essid Color.pl('{+} {C}option:{W} {O}ignoring ESSIDs that include {R}%s{W}' % args.ignore_essid) if args.clients_only == True: - Configuration.clients_only = True + cls.clients_only = True Color.pl('{+} {C}option:{W} {O}ignoring targets that do not have associated clients') if args.scan_time: - Configuration.scan_time = args.scan_time + cls.scan_time = args.scan_time Color.pl('{+} {C}option:{W} ({G}pillage{W}) attack all targets after {G}%d{W}s' % args.scan_time) if args.verbose: - Configuration.verbose = args.verbose + cls.verbose = args.verbose Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose) if args.kill_conflicting_processes: - Configuration.kill_conflicting_processes = True + cls.kill_conflicting_processes = True Color.pl('{+} {C}option:{W} kill conflicting processes {G}enabled{W}') + # EvilTwin + if args.use_eviltwin: + cls.use_eviltwin = True + Color.pl('{+} {C}option:{W} using {G}eviltwin attacks{W} against all targets') + # WEP if args.wep_filter: - Configuration.wep_filter = args.wep_filter + cls.wep_filter = args.wep_filter if args.wep_pps: - Configuration.wep_pps = args.wep_pps + cls.wep_pps = args.wep_pps Color.pl('{+} {C}option:{W} using {G}%d{W} packets-per-second on WEP attacks' % args.wep_pps) if args.wep_timeout: - Configuration.wep_timeout = args.wep_timeout + cls.wep_timeout = args.wep_timeout Color.pl('{+} {C}option:{W} WEP attack timeout set to {G}%d seconds{W}' % args.wep_timeout) if args.require_fakeauth: - Configuration.require_fakeauth = True + cls.require_fakeauth = True Color.pl('{+} {C}option:{W} fake-authentication is {G}required{W} for WEP attacks') if args.wep_crack_at_ivs: - Configuration.wep_crack_at_ivs = args.wep_crack_at_ivs + cls.wep_crack_at_ivs = args.wep_crack_at_ivs Color.pl('{+} {C}option:{W} will start cracking WEP keys at {G}%d IVs{W}' % args.wep_crack_at_ivs) if args.wep_restart_stale_ivs: - Configuration.wep_restart_stale_ivs = args.wep_restart_stale_ivs + cls.wep_restart_stale_ivs = args.wep_restart_stale_ivs Color.pl('{+} {C}option:{W} will restart aireplay after {G}%d seconds{W} of no new IVs' % args.wep_restart_stale_ivs) if args.wep_restart_aircrack: - Configuration.wep_restart_aircrack = args.wep_restart_aircrack + cls.wep_restart_aircrack = args.wep_restart_aircrack Color.pl('{+} {C}option:{W} will restart aircrack every {G}%d seconds{W}' % args.wep_restart_aircrack) if args.wep_keep_ivs: - Configuration.wep_keep_ivs = args.wep_keep_ivs + cls.wep_keep_ivs = args.wep_keep_ivs Color.pl('{+} {C}option:{W} keep .ivs files across multiple WEP attacks') # WPA if args.wpa_filter: - Configuration.wpa_filter = args.wpa_filter + cls.wpa_filter = args.wpa_filter if args.wordlist: if os.path.exists(args.wordlist): - Configuration.wordlist = args.wordlist + cls.wordlist = args.wordlist Color.pl('{+} {C}option:{W} using wordlist {G}%s{W} to crack WPA handshakes' % args.wordlist) else: - Configuration.wordlist = None + cls.wordlist = None Color.pl('{+} {C}option:{O} wordlist {R}%s{O} was not found, wifite will NOT attempt to crack handshakes' % args.wordlist) if args.wpa_deauth_timeout: - Configuration.wpa_deauth_timeout = args.wpa_deauth_timeout + cls.wpa_deauth_timeout = args.wpa_deauth_timeout Color.pl('{+} {C}option:{W} will deauth WPA clients every {G}%d seconds{W}' % args.wpa_deauth_timeout) if args.wpa_attack_timeout: - Configuration.wpa_attack_timeout = args.wpa_attack_timeout + cls.wpa_attack_timeout = args.wpa_attack_timeout Color.pl('{+} {C}option:{W} will stop WPA handshake capture after {G}%d seconds{W}' % args.wpa_attack_timeout) if args.ignore_old_handshakes: - Configuration.ignore_old_handshakes = True + cls.ignore_old_handshakes = True Color.pl("{+} {C}option:{W} will {O}ignore{W} existing handshakes (force capture)") if args.wpa_handshake_dir: - Configuration.wpa_handshake_dir = args.wpa_handshake_dir + cls.wpa_handshake_dir = args.wpa_handshake_dir Color.pl('{+} {C}option:{W} will store handshakes to {G}%s{W}' % args.wpa_handshake_dir) if args.wpa_strip_handshake: - Configuration.wpa_strip_handshake = True + cls.wpa_strip_handshake = True Color.pl("{+} {C}option:{W} will {G}strip{W} non-handshake packets") # WPS if args.wps_filter: - Configuration.wps_filter = args.wps_filter + cls.wps_filter = args.wps_filter if args.wps_only: - Configuration.wps_only = True + cls.wps_only = True Color.pl('{+} {C}option:{W} will *only* attack non-WEP networks with {G}WPS attacks{W} (no handshake capture)') if args.no_wps: - Configuration.no_wps = args.no_wps + cls.no_wps = args.no_wps Color.pl('{+} {C}option:{W} will {O}never{W} use {C}WPS attacks{W} (Pixie-Dust/PIN) on targets') if args.use_bully: - Configuration.use_bully = args.use_bully + cls.use_bully = args.use_bully Color.pl('{+} {C}option:{W} use {C}bully{W} instead of {C}reaver{W} for WPS Attacks') if args.wps_pixie_timeout: - Configuration.wps_pixie_timeout = args.wps_pixie_timeout + cls.wps_pixie_timeout = args.wps_pixie_timeout Color.pl('{+} {C}option:{W} WPS pixie-dust attack will fail after {O}%d seconds{W}' % args.wps_pixie_timeout) if args.wps_fail_threshold: - Configuration.wps_fail_threshold = args.wps_fail_threshold + cls.wps_fail_threshold = args.wps_fail_threshold Color.pl('{+} {C}option:{W} will stop WPS attack after {O}%d failures{W}' % args.wps_fail_threshold) if args.wps_timeout_threshold: - Configuration.wps_timeout_threshold = args.wps_timeout_threshold + cls.wps_timeout_threshold = args.wps_timeout_threshold Color.pl('{+} {C}option:{W} will stop WPS attack after {O}%d timeouts{W}' % args.wps_timeout_threshold) # Adjust encryption filter - Configuration.encryption_filter = [] - if Configuration.wep_filter: Configuration.encryption_filter.append('WEP') - if Configuration.wpa_filter: Configuration.encryption_filter.append('WPA') - if Configuration.wps_filter: Configuration.encryption_filter.append('WPS') + cls.encryption_filter = [] + if cls.wep_filter: cls.encryption_filter.append('WEP') + if cls.wpa_filter: cls.encryption_filter.append('WPA') + if cls.wps_filter: cls.encryption_filter.append('WPS') - if len(Configuration.encryption_filter) == 3: + if len(cls.encryption_filter) == 3: Color.pl('{+} {C}option:{W} targeting {G}all encrypted networks{W}') - elif len(Configuration.encryption_filter) == 0: + elif len(cls.encryption_filter) == 0: # Default to scan all types - Configuration.encryption_filter = ['WEP', 'WPA', 'WPS'] + cls.encryption_filter = ['WEP', 'WPA', 'WPS'] else: Color.pl('{+} {C}option:{W} ' + 'targeting {G}%s-encrypted{W} networks' - % '/'.join(Configuration.encryption_filter)) + % '/'.join(cls.encryption_filter)) # Adjust WEP attack list - Configuration.wep_attacks = [] + cls.wep_attacks = [] import sys seen = set() for arg in sys.argv: if arg in seen: continue seen.add(arg) - if arg == '-arpreplay': Configuration.wep_attacks.append('replay') - if arg == '-fragment': Configuration.wep_attacks.append('fragment') - if arg == '-chopchop': Configuration.wep_attacks.append('chopchop') - if arg == '-caffelatte': Configuration.wep_attacks.append('caffelatte') - if arg == '-p0841': Configuration.wep_attacks.append('p0841') - if arg == '-hirte': Configuration.wep_attacks.append('hirte') + if arg == '-arpreplay': cls.wep_attacks.append('replay') + if arg == '-fragment': cls.wep_attacks.append('fragment') + if arg == '-chopchop': cls.wep_attacks.append('chopchop') + if arg == '-caffelatte': cls.wep_attacks.append('caffelatte') + if arg == '-p0841': cls.wep_attacks.append('p0841') + if arg == '-hirte': cls.wep_attacks.append('hirte') - if len(Configuration.wep_attacks) == 0: + if len(cls.wep_attacks) == 0: # Use all attacks - Configuration.wep_attacks = ['replay', + cls.wep_attacks = ['replay', 'fragment', 'chopchop', 'caffelatte', 'p0841', 'hirte'] - elif len(Configuration.wep_attacks) > 0: + elif len(cls.wep_attacks) > 0: Color.pl('{+} {C}option:{W} using {G}%s{W} WEP attacks' - % '{W}, {G}'.join(Configuration.wep_attacks)) + % '{W}, {G}'.join(cls.wep_attacks)) # Commands - if args.cracked: Configuration.show_cracked = True - if args.check_handshake: Configuration.check_handshake = args.check_handshake - if args.crack_handshake: Configuration.crack_handshake = True + if args.cracked: cls.show_cracked = True + if args.check_handshake: cls.check_handshake = args.check_handshake + if args.crack_handshake: cls.crack_handshake = True - @staticmethod - def temp(subfile=''): + @classmethod + def temp(cls, subfile=''): ''' Creates and/or returns the temporary directory ''' - if Configuration.temp_dir is None: - Configuration.temp_dir = Configuration.create_temp() - return Configuration.temp_dir + subfile + if cls.temp_dir is None: + cls.temp_dir = cls.create_temp() + return cls.temp_dir + subfile @staticmethod def create_temp(): @@ -304,28 +317,28 @@ class Configuration(object): tmp += os.sep return tmp - @staticmethod - def delete_temp(): + @classmethod + def delete_temp(cls): ''' Remove temp files and folder ''' - if Configuration.temp_dir is None: return - if os.path.exists(Configuration.temp_dir): - for f in os.listdir(Configuration.temp_dir): - os.remove(Configuration.temp_dir + f) - os.rmdir(Configuration.temp_dir) + if cls.temp_dir is None: return + if os.path.exists(cls.temp_dir): + for f in os.listdir(cls.temp_dir): + os.remove(cls.temp_dir + f) + os.rmdir(cls.temp_dir) - @staticmethod - def exit_gracefully(code=0): + @classmethod + def exit_gracefully(cls, code=0): ''' Deletes temp and exist with the given code ''' - Configuration.delete_temp() + cls.delete_temp() Macchanger.reset_if_changed() from .tools.airmon import Airmon - if Configuration.interface is not None and Airmon.base_interface is not None: - Color.pl('{!} Leaving interface {C}%s{W} in Monitor Mode.' % Configuration.interface) - Color.pl('{!} You can disable Monitor Mode when finished ({C}airmon-ng stop %s{W})' % Configuration.interface) + if cls.interface is not None and Airmon.base_interface is not None: + Color.pl('{!} Leaving interface {C}%s{W} in Monitor Mode.' % cls.interface) + Color.pl('{!} You can disable Monitor Mode when finished ({C}airmon-ng stop %s{W})' % cls.interface) # Stop monitor mode - #Airmon.stop(Configuration.interface) + #Airmon.stop(cls.interface) # Bring original interface back up #Airmon.put_interface_up(Airmon.base_interface) @@ -335,19 +348,19 @@ class Configuration(object): exit(code) - @staticmethod - def dump(): + @classmethod + def dump(cls): ''' (Colorful) string representation of the configuration ''' from .util.color import Color max_len = 20 - for key in Configuration.__dict__.keys(): + for key in cls.__dict__.keys(): max_len = max(max_len, len(key)) - result = Color.s('{W}%s Value{W}\n' % 'Configuration Key'.ljust(max_len)) + result = Color.s('{W}%s Value{W}\n' % 'cls Key'.ljust(max_len)) result += Color.s('{W}%s------------------{W}\n' % ('-' * max_len)) - for (key,val) in sorted(Configuration.__dict__.items()): + for (key,val) in sorted(cls.__dict__.items()): if key.startswith('__') or type(val) == staticmethod or val is None: continue result += Color.s("{G}%s {W} {C}%s{W}\n" % (key.ljust(max_len),val)) diff --git a/wifite/tools/aircrack.py b/wifite/tools/aircrack.py index 68e9414..71561bd 100755 --- a/wifite/tools/aircrack.py +++ b/wifite/tools/aircrack.py @@ -1,13 +1,18 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- +from .dependency import Dependency from ..util.process import Process from ..util.input import xrange from ..config import Configuration import os -class Aircrack(object): +class Aircrack(Dependency): + dependency_required = True + dependency_name = 'aircrack-ng' + dependency_url = 'https://www.aircrack-ng.org/install.html' + def __init__(self, ivs_file=None): self.cracked_file = os.path.abspath( diff --git a/wifite/tools/aireplay.py b/wifite/tools/aireplay.py index 52d8e93..2c1ed1e 100755 --- a/wifite/tools/aireplay.py +++ b/wifite/tools/aireplay.py @@ -1,6 +1,7 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- +from .dependency import Dependency from ..config import Configuration from ..util.process import Process from ..util.timer import Timer @@ -54,7 +55,11 @@ class WEPAttackType(object): return self.name -class Aireplay(Thread): +class Aireplay(Thread, Dependency): + dependency_required = True + dependency_name = 'aircrack-ng' + dependency_url = 'https://www.aircrack-ng.org/install.html' + def __init__(self, target, attack_type, client_mac=None, replay_file=None): ''' Starts aireplay process. diff --git a/wifite/tools/airmon.py b/wifite/tools/airmon.py index 790a843..1a29d66 100755 --- a/wifite/tools/airmon.py +++ b/wifite/tools/airmon.py @@ -1,8 +1,9 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- -from ..tools.ifconfig import Ifconfig -from ..tools.iwconfig import Iwconfig +from .dependency import Dependency +from .ifconfig import Ifconfig +from .iwconfig import Iwconfig from ..util.process import Process from ..util.color import Color from ..util.input import raw_input @@ -49,8 +50,12 @@ class AirmonIface(object): return s -class Airmon(object): +class Airmon(Dependency): ''' Wrapper around the 'airmon-ng' program ''' + dependency_required = True + dependency_name = 'airmon-ng' + dependency_url = 'https://www.aircrack-ng.org/install.html' + base_interface = None killed_network_manager = False diff --git a/wifite/tools/airodump.py b/wifite/tools/airodump.py index 77dfd14..e00acbc 100755 --- a/wifite/tools/airodump.py +++ b/wifite/tools/airodump.py @@ -1,6 +1,7 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- +from .dependency import Dependency from .tshark import Tshark from .wash import Wash from ..util.process import Process @@ -10,8 +11,11 @@ from ..model.client import Client import os, time -class Airodump(object): +class Airodump(Dependency): ''' Wrapper around airodump-ng program ''' + dependency_required = True + dependency_name = 'airodump-ng' + dependency_url = 'https://www.aircrack-ng.org/install.html' def __init__(self, interface=None, channel=None, encryption=None,\ wps=False, target_bssid=None, output_file_prefix='airodump',\ diff --git a/wifite/tools/dependency.py b/wifite/tools/dependency.py new file mode 100644 index 0000000..335b234 --- /dev/null +++ b/wifite/tools/dependency.py @@ -0,0 +1,30 @@ +from ..util.process import Process +from ..util.color import Color + +class Dependency(object): + required_attr_names = ['dependency_name', 'dependency_url', 'dependency_required'] + + # https://stackoverflow.com/a/49024227 + def __init_subclass__(cls): + for attr_name in cls.required_attr_names: + if not attr_name in cls.__dict__: + raise NotImplementedError( + "Attribute '{}' has not been overriden in class '{}'" \ + .format(attr_name, cls.__name__) + ) + + + @classmethod + def fails_dependency_check(cls): + if Process.exists(cls.dependency_name): + return False + + if cls.dependency_required: + Color.pl('{!} {R}error: required app {O}%s{R} was not found' % cls.dependency_name) + Color.pl(' {W}install @ {C}%s{W}' % cls.dependency_url) + return True + + else: + Color.pl('{!} {O}warning: recommended app {R}%s{O} was not found' % cls.dependency_name) + Color.pl(' {W}install @ {C}%s{W}' % cls.dependency_url) + return False diff --git a/wifite/wifite.py b/wifite/wifite.py index 8aa5f05..1e689c8 100755 --- a/wifite/wifite.py +++ b/wifite/wifite.py @@ -16,6 +16,8 @@ from .attack.wpa import AttackWPA from .attack.wps import AttackWPS from .model.result import CrackResult from .model.handshake import Handshake +from .tools.airmon import Airmon +from .tools.airodump import Airodump import json import os @@ -31,10 +33,10 @@ class Wifite(object): Color.pl('{!} {O}re-run as: sudo ./Wifite.py{W}') Configuration.exit_gracefully(0) - self.dependency_check() - Configuration.initialize(load_interface=False) + self.dependency_check() + if Configuration.show_cracked: self.display_cracked() @@ -46,30 +48,24 @@ class Wifite(object): Configuration.get_monitor_mode_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'] - optional_apps = ['packetforge-ng', 'reaver', 'bully', 'cowpatty', 'pyrit', 'stdbuf', 'macchanger', 'tshark'] - 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) + apps = [Airmon, Airodump] #, Iwconfig, Ifconfig, Aircrack, Aireplay, Airodump] - 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 Configuration.use_eviltwin: + apps.extend([Hostapd, Dnsmasq, Iptables]) + + missing_required = any([app.fails_dependency_check() for app in apps]) 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}') + #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 ''' @@ -134,9 +130,17 @@ class Wifite(object): 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: + + # TODO: Check if Eviltwin attack is selected. + + if Configuration.use_eviltwin: + pass + + elif 'WEP' in t.encryption: attack = AttackWEP(t) + elif 'WPA' in t.encryption: + # TODO: Move WPS+WPA decision to a combined attack if t.wps: attack = AttackWPS(t) result = False