22 Commits

Author SHA1 Message Date
derv82
e0ec0e72b2 Change version to 2.1.0
See https://github.com/derv82/wifite/issues/130 for more info.
2018-04-06 15:55:58 -04:00
derv82
699578abed Allow spaces in target selection (e.g. 3, 4, 7-8) 2018-04-01 15:57:58 -04:00
derv82
57ad097d49 --check: Detect BSSID based on Wifite's handshake filename format. 2018-04-01 15:31:12 -04:00
derv82
a4110b4cf9 Put quotes around Tshark filter ... otherwise tshark locks up 2018-04-01 15:23:56 -04:00
derv82
e8c0843bdf Use stdbuf only if it exists 2018-04-01 01:09:57 -04:00
derv82
5db801b414 Cleaning up handshake code 2018-04-01 01:03:10 -04:00
derv82
2cd6116a8c Move Pyrit logic to tools/pyrit.py 2018-04-01 00:55:41 -04:00
derv82
a2dbf4c382 Moving Tshark logic into /tools/tshark.py
Added tests for analyzing handshakes
2018-04-01 00:37:28 -04:00
derv82
acc8e296d5 Dim the [+] box colors 2018-03-31 23:59:50 -04:00
derv82
72382cf381 Updating README for latest developments. 2018-03-31 23:36:53 -04:00
derv82
3eddcaa59f Support for Python3
That was fun.
2018-03-31 23:02:33 -04:00
derv82
1ad17472b2 Tshark is optional, falls-back to Wash for WPS-detection.
Should resolve #77
2018-03-31 18:57:11 -04:00
derv82
528741f89f Fix bully: --force when deriving PSK from PIN 2018-03-31 18:40:04 -04:00
derv82
909b10e517 Make tshark non-required.
* WPS-detection is not required for non-WPS attacks.
* Handshake analysis can be done using aircrack, cowpatty, pyrit, etc.
2018-03-31 18:05:47 -04:00
derv82
b0bd57b1a3 Improve messaging during chopchop & fragment attacks
Bugfix when chopchop fails.
Displays aircrack's error message when chopchop fails.

For improving WEP attacks in #27
2018-03-25 16:03:20 -04:00
derv82
d8da6798de Update README. Add ideas to TODO after reading the first 20 issues on wifite v1 :( 2018-03-25 04:48:55 -04:00
derv82
19c38bd06c Fetch PSK when reaver fails to find it using Bully
Also changed reaver --timeout from 10 seconds (default) to 4 seconds.

Resolves #76
2018-03-24 15:02:11 -04:00
derv82
a488cf86f1 Bugfix: Don't infinitely loop while calculating Hex & ASCII key from WEP attack.
* Simplified HEX/ASCII conversion. Avoids infinite loop 🤔
* Added integration test: python -m wifite.tools.aircrack

Should resolve "hanging" issues during WEP attacks such as #27.
2018-03-24 14:10:48 -04:00
derv82
34d6b69b48 Fixing whitespace (tabs) on 'baddriver' methods 2018-03-17 17:42:44 -04:00
derv82
ad0265cd92 Merge branch 'refactor' 2018-03-17 17:30:11 -04:00
derv
98e1eef3a8 Merge pull request #74 from schoonc/patch-1
Update Dockerfile
2018-03-23 23:20:53 -07:00
schoonc
641dba0e63 Update Dockerfile 2018-03-18 12:45:50 +03:00
28 changed files with 769 additions and 394 deletions

View File

@@ -7,7 +7,7 @@ ENV HASHCAT_VERSION hashcat-3.6.0
RUN echo "deb-src http://deb.debian.org/debian jessie main" >> /etc/apt/sources.list RUN echo "deb-src http://deb.debian.org/debian jessie main" >> /etc/apt/sources.list
RUN apt-get update && apt-get upgrade -y RUN apt-get update && apt-get upgrade -y
RUN apt-get install ca-certificates gcc openssl make kmod nano wget p7zip build-essential libsqlite3-dev libpcap0.8-dev libpcap-dev sqlite3 pkg-config libnl-genl-3-dev libssl-dev net-tools iw ethtool usbutils pciutils wireless-tools git curl wget unzip macchanger pyrit tshark -y RUN apt-get install ca-certificates gcc openssl make kmod nano wget p7zip build-essential libsqlite3-dev libpcap0.8-dev libpcap-dev sqlite3 pkg-config libnl-genl-3-dev libssl-dev net-tools iw ethtool usbutils pciutils wireless-tools git curl wget unzip macchanger pyrit tshark -y
RUN apt-get build-dep aircrack-ng RUN apt-get build-dep aircrack-ng -y
@@ -16,7 +16,7 @@ RUN wget http://download.aircrack-ng.org/aircrack-ng-1.2-rc4.tar.gz
RUN tar xzvf aircrack-ng-1.2-rc4.tar.gz RUN tar xzvf aircrack-ng-1.2-rc4.tar.gz
WORKDIR /aircrack-ng-1.2-rc4/ WORKDIR /aircrack-ng-1.2-rc4/
RUN make RUN make
RUN make installl RUN make install
RUN airodump-ng-oui-update RUN airodump-ng-oui-update
# Workdir / # Workdir /
@@ -24,7 +24,7 @@ WORKDIR /
# Install wps-pixie # Install wps-pixie
RUN git clone https://github.com/wiire/pixiewps RUN git clone https://github.com/wiire/pixiewps
WORKDIR /pixiewps/src/ WORKDIR /pixiewps/
RUN make RUN make
RUN make install RUN make install
@@ -47,7 +47,7 @@ WORKDIR /
#Install and configure hashcat #Install and configure hashcat
RUN mkdir hashcat && \ RUN mkdir hashcat && \
cd hashcat && \ cd hashcat && \
wget http://hashcat.net/files/${HASHCAT_VERSION}.7z && \ wget https://hashcat.net/files_legacy/${HASHCAT_VERSION}.7z && \
7zr e ${HASHCAT_VERSION}.7z 7zr e ${HASHCAT_VERSION}.7z

105
README.md
View File

@@ -1,38 +1,92 @@
Wifite 2 Wifite 2
======== ========
A complete re-write of [`wifite`](https://github.com/derv82/wifite), a Python script for auditing wireless networks. A complete re-write of [`wifite`](https://github.com/derv82/wifite), a Python script for auditing wireless networks.
What's new? Wifite runs existing wireless-auditing tools for you. Stop memorizing command arguments & switches!
-----------
* Lots of files instead of "one big script". What's new in Wifite2?
* Cleaner process management -- No longer leaves processes running in the background. ----------------------
* UX: Target access points are refreshed every second instead of every 5 seconds.
* UX: Displays realtime Power level (in db) of currently-attacked target * **Less bugs**
* Cleaner process management. Does not leave processes running in the background (the old `wifite` was bad about this).
* No longer "one monolithic script". Has working unit tests. Pull requests are less-painful!
* **Speed**
* Target access points are refreshed every second instead of every 5 seconds.
* **Accuracy**
* Displays realtime Power level of currently-attacked target.
* Displays more information during an attack (e.g. % during WEP chopchop attacks, Pixie-Dust step index, etc)
* **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).
* Python 3 support.
* Sweet new ASCII banner.
What's gone in Wifite2?
-----------------------
* No more WPS PIN attack, because it can take days on-average.
* However, the Pixie-Dust attack is still an option.
* Some command-line arguments (`--wept`, `--wpst`, and other confusing switches).
* You can still access some of these, try `./Wifite.py -h -v`
What's not new? What's not new?
--------------- ---------------
* Backwards compatibility with the original `wifite`'s arguments.
* (Mostly) Backwards compatibile with the original `wifite`'s arguments.
* Same text-based interface everyone knows and loves. * Same text-based interface everyone knows and loves.
Full Feature List Brief Feature List
----------------- ------------------
* Reaver Pixie-Dust attack (`--pixie`)
* Reaver WPS PIN attack (`--reaver`)
* WPA handshake capture (`--no-reaver`)
* Validates handshakes against `pyrit`, `tshark`, `cowpatty`, and `aircrack-ng`
* Various WEP attacks (replay, chopchop, fragment, etc)
* 5Ghz support for wireless cards that support 5ghz (use `-5` option)
* Stores cracked passwords and handshakes to the current directory, with metadata about the access point (via `--cracked` command).
* Decloaks hidden access points when channel is fixed (use `-c <channel>` option)
* Provides commands to crack captured WPA handshakes (via `--crack` command)
Support * Reaver (or `-bully`) Pixie-Dust attack (enabled by-default, force with: `--wps-only`)
------- * WPA handshake capture (enabled by-default, force with: `--no-wps`)
Wifite2 is designed entirely for the latest version of Kali Rolling release (tested on Kali 2016.2, updated May 2017). * 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`.
This means only the latest versions of these programs are supported: Aircrack-ng suite, reaver, tshark, cowpatty. Linux Distribution Support
--------------------------
Other pen-testing distributions (such as BackBox) have outdated versions of these suites; these distributions are not supported. Wifite2 is designed specifically for the latest version of **Kali**'s rolling release (tested on Kali 2017.2, updated Jan 2018).
Other pen-testing distributions (such as BackBox) have outdated versions of the tools used by Wifite; these distributions are not supported.
Required Tools
--------------
Only the latest versions of these programs are supported:
**Required:**
* `iwconfig`: For identifying wireless devices already in Monitor Mode.
* `ifconfig`: For starting/stopping wireless devices.
* `Aircrack-ng` suite, includes:
* `aircrack-ng`: For cracking WEP .cap files and and WPA handshake captures.
* `aireplay-ng`: For deauthing access points, replaying capture files, various WEP attacks.
* `airmon-ng`: For enumerating and enabling Monitor Mode on wireless devices.
* `airodump-ng`: For target scanning & capture file generation.
* `packetforge-ng`: For forging capture files.
**Optional, but Recommended:**
* `tshark`: For detecting WPS networks and inspecting handshake capture files.
* `reaver`: For WPS Pixie-Dust attacks.
* Note: Reaver's `wash` tool can be used to detect WPS networks if `tshark` is not found.
* `bully`: For WPS Pixie-Dust attacks.
* Alternative to Reaver. Specify `--bully` to use Bully instead of Reaver.
* Bully is also used to fetch PSK if `reaver` cannot after cracking WPS PIN.
* `cowpatty`: For detecting handshake captures.
* `pyrit`: For detecting handshake captures.
Installing & Running Installing & Running
-------------------- --------------------
@@ -45,6 +99,11 @@ cd wifite2
Screenshots Screenshots
----------- -----------
Cracking WPS PIN using `reaver`'s Pixie-Dust attack, then retrieving WPA PSK using `bully`:
![Pixie-Dust with Reaver to get PIN and Bully to get PSK](https://i.imgur.com/I2W0wND.gif)
-------------
Decloaking & cracking a hidden access point (via the WPA Handshake attack): Decloaking & cracking a hidden access point (via the WPA Handshake attack):
![Decloaking and Cracking a hidden access point](http://i.imgur.com/MTMwSzM.gif) ![Decloaking and Cracking a hidden access point](http://i.imgur.com/MTMwSzM.gif)

46
TODO.md
View File

@@ -4,6 +4,39 @@ This file is a braindump of ideas to improve Wifite2 (or forward-looking to "Wif
------------------------------------------------------ ------------------------------------------------------
### Support Other Distributions (not just Kali x86/64)
Off the top of my head:
* Raspberry Pi (or any Debian distro)
* Raspberry Pi + Kali (?)
* Kali Nethunter
* Various other distributions (backbox, pentoo, blackarch, etc)
Deprecation of "core" programs:
* `iwconfig` is deprecated in favor of `iw`
* `ifconfig` is deprecated in favor of `ip`
Versioning problems:
* Pixiewps output differs depending on version
* Likewise for reaver & bully
* Reaver and bully args have changed significantly over the years (added/removed/required)
* airodump-ng --write-interval=1 doesn't work on older versions
* Same with --wps and a few other options :(
* airmon-ng output differs, wifite sees "phy0" instead of the interface name.
Misc problems:
* Some people have problems with multiple wifi cards plugged in
* Solution: User prompt when no devices are in monitor mode (ask first).
* Some people want wifite to kill network manager, others don't.
* Solution: User prompt to kill processes
* Some people need --ignore-negative-one on some wifi cards.
------------------------------------------------------
### Command-line Arguments ### Command-line Arguments
Wifite is a 'Spray and Pray', 'Big Red Button' script. Wifite should not provide obscure options that only advanced users can understand. Advanced users can simply use Wifite's dependencies directly. Wifite is a 'Spray and Pray', 'Big Red Button' script. Wifite should not provide obscure options that only advanced users can understand. Advanced users can simply use Wifite's dependencies directly.
@@ -54,20 +87,13 @@ And some native Python implementations might be cross-platform, which would allo
Some of Wifite's dependencies work on other OSes (airodump) but some don't (airmon). Some of Wifite's dependencies work on other OSes (airodump) but some don't (airmon).
If it's possible to run these programs on Windows or OSX, Wifite should suporrt that. If it's possible to run these programs on Windows or OSX, Wifite should support that.
------------------------------------------------------
### Backwards Compatibility
* WIFITE: needs command-line parity with older versions (or does it?)
* AIRODUMP: --output-format, --wps, and other flags are only in new versions of Airodump.
------------------------------------------------------ ------------------------------------------------------
### WPS Attacks ### WPS Attacks
Wifite's Pixie-Dust attack status output differs between Reaver & Bully. And the command line switches are... not even used? Wifite's Pixie-Dust attack status output differs between Reaver & Bully. And the command line switches are... not even used by bully?
Ideally for Pixie-Dust, we'd have: Ideally for Pixie-Dust, we'd have:
@@ -98,6 +124,8 @@ Users with that kind of dedication can run bully/reaver themselves.
### Directory structure ### Directory structure
**Note: This was mostly done in the great refactoring of Late March 2018**
Too modular in some places, not modular enough in others. Too modular in some places, not modular enough in others.
Not "/py": Not "/py":

Binary file not shown.

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python2.7 #!/usr/bin/python2.7
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from util.color import Color from .util.color import Color
import argparse, sys import argparse, sys
@@ -366,11 +366,11 @@ class Arguments(object):
help=Color.s('Show commands to crack a captured handshake')) help=Color.s('Show commands to crack a captured handshake'))
if __name__ == '__main__': if __name__ == '__main__':
from util.color import Color from .util.color import Color
from config import Configuration from config import Configuration
Configuration.initialize(False) Configuration.initialize(False)
a = Arguments(Configuration) a = Arguments(Configuration)
args = a.args args = a.args
for (key,value) in sorted(args.__dict__.iteritems()): for (key,value) in sorted(args.__dict__.items()):
Color.pl('{C}%s: {G}%s{W}' % (key.ljust(21),value)) Color.pl('{C}%s: {G}%s{W}' % (key.ljust(21),value))

View File

@@ -8,6 +8,7 @@ from ..tools.aircrack import Aircrack
from ..config import Configuration from ..config import Configuration
from ..model.interface import Interface from ..model.interface import Interface
from ..util.color import Color from ..util.color import Color
from ..util.input import raw_input
from ..model.wep_result import CrackResultWEP from ..model.wep_result import CrackResultWEP
import time import time
@@ -85,6 +86,10 @@ class AttackWEP(Attack):
while True: while True:
airodump_target = self.wait_for_target(airodump) airodump_target = self.wait_for_target(airodump)
if client_mac is None and len(airodump_target.clients) > 0:
client_mac = airodump_target.clients[0].station
status = "%d/{C}%d{W} IVs" % (airodump_target.ivs, Configuration.wep_crack_at_ivs) status = "%d/{C}%d{W} IVs" % (airodump_target.ivs, Configuration.wep_crack_at_ivs)
if fakeauth_proc: if fakeauth_proc:
if fakeauth_proc and fakeauth_proc.status: if fakeauth_proc and fakeauth_proc.status:
@@ -94,12 +99,7 @@ class AttackWEP(Attack):
if aireplay.status is not None: if aireplay.status is not None:
status += ", %s" % aireplay.status status += ", %s" % aireplay.status
Color.clear_entire_line() Color.clear_entire_line()
Color.pattack("WEP", Color.pattack("WEP", airodump_target, "%s" % attack_name, status)
airodump_target,
"%s" % attack_name,
status)
#self.aircrack_check()
# Check if we cracked it. # Check if we cracked it.
if aircrack and aircrack.is_cracked(): if aircrack and aircrack.is_cracked():
@@ -109,8 +109,7 @@ class AttackWEP(Attack):
essid = airodump_target.essid essid = airodump_target.essid
else: else:
essid = None essid = None
Color.pl('\n{+} {C}%s{W} WEP attack {G}successful{W}\n' Color.pl('\n{+} {C}%s{W} WEP attack {G}successful{W}\n' % attack_name)
% attack_name)
if aireplay: aireplay.stop() if aireplay: aireplay.stop()
if fakeauth_proc: fakeauth_proc.stop() if fakeauth_proc: fakeauth_proc.stop()
self.crack_result = CrackResultWEP(self.target.bssid, self.crack_result = CrackResultWEP(self.target.bssid,
@@ -143,9 +142,7 @@ class AttackWEP(Attack):
# Restart aircrack after X seconds # Restart aircrack after X seconds
aircrack.stop() aircrack.stop()
ivs_file = airodump.find_files(endswith='.ivs')[0] ivs_file = airodump.find_files(endswith='.ivs')[0]
Color.pl('\n{+} {C}aircrack{W} ran for more than' + Color.pl('\n{+} {C}aircrack{W} ran for more than {C}%d{W} seconds, restarting' % Configuration.wep_restart_aircrack)
' {C}%d{W} seconds, restarting'
% Configuration.wep_restart_aircrack)
aircrack = Aircrack(ivs_file) aircrack = Aircrack(ivs_file)
@@ -162,7 +159,7 @@ class AttackWEP(Attack):
# If .xor is not there, the process failed. # If .xor is not there, the process failed.
Color.pl('\n{!} {O}%s attack{R} did not generate a .xor file' % attack_name) Color.pl('\n{!} {O}%s attack{R} did not generate a .xor file' % attack_name)
# XXX: For debugging # XXX: For debugging
Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd) Color.pl('{?} {O}Command: {R}%s{W}' % " ".join(aireplay.cmd))
Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output()) Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output())
break break
@@ -187,8 +184,8 @@ class AttackWEP(Attack):
break break
else: else:
Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}') Color.pl('\n{!} {O}aireplay-ng exited unexpectedly{W}')
Color.pl('{?} {O}Command: {R}%s{W}' % aireplay.cmd) Color.pl('{?} {O}Command: {R}%s{W}' % " ".join(aireplay.cmd))
Color.pl('{?} {O}Output:\n%s{W}' % aireplay.get_output()) Color.pl('{?} {O}Output:\n{R}%s{W}' % aireplay.get_output())
break # Continue to other attacks break # Continue to other attacks
# Check if IVs stopped flowing (same for > N seconds) # Check if IVs stopped flowing (same for > N seconds)

View File

@@ -1,8 +1,8 @@
#!/usr/bin/python2.7 #!/usr/bin/python2.7
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from util.color import Color from .util.color import Color
from tools.macchanger import Macchanger from .tools.macchanger import Macchanger
import os import os
@@ -12,7 +12,7 @@ class Configuration(object):
initialized = False # Flag indicating config has been initialized initialized = False # Flag indicating config has been initialized
temp_dir = None # Temporary directory temp_dir = None # Temporary directory
version = 2.00 version = '2.1.0'
@staticmethod @staticmethod
def initialize(load_interface=True): def initialize(load_interface=True):
@@ -108,7 +108,7 @@ class Configuration(object):
def get_interface(): def get_interface():
if Configuration.interface is None: if Configuration.interface is None:
# Interface wasn't defined, select it! # Interface wasn't defined, select it!
from tools.airmon import Airmon from .tools.airmon import Airmon
Configuration.interface = Airmon.ask() Configuration.interface = Airmon.ask()
if Configuration.random_mac: if Configuration.random_mac:
Macchanger.random() Macchanger.random()
@@ -117,7 +117,7 @@ class Configuration(object):
@staticmethod @staticmethod
def load_from_arguments(): def load_from_arguments():
''' Sets configuration values based on Argument.args object ''' ''' Sets configuration values based on Argument.args object '''
from args import Arguments from .args import Arguments
args = Arguments(Configuration).args args = Arguments(Configuration).args
if args.random_mac: if args.random_mac:
@@ -315,7 +315,7 @@ class Configuration(object):
''' Deletes temp and exist with the given code ''' ''' Deletes temp and exist with the given code '''
Configuration.delete_temp() Configuration.delete_temp()
Macchanger.reset_if_changed() Macchanger.reset_if_changed()
from tools.airmon import Airmon from .tools.airmon import Airmon
if hasattr(Configuration, "interface") and Configuration.interface is not None and Airmon.base_interface is not None: if hasattr(Configuration, "interface") and Configuration.interface is not None and Airmon.base_interface is not None:
Airmon.stop(Configuration.interface) Airmon.stop(Configuration.interface)
Airmon.put_interface_up(Airmon.base_interface) Airmon.put_interface_up(Airmon.base_interface)
@@ -328,7 +328,7 @@ class Configuration(object):
@staticmethod @staticmethod
def dump(): def dump():
''' (Colorful) string representation of the configuration ''' ''' (Colorful) string representation of the configuration '''
from util.color import Color from .util.color import Color
max_len = 20 max_len = 20
for key in Configuration.__dict__.keys(): for key in Configuration.__dict__.keys():
@@ -337,7 +337,7 @@ class Configuration(object):
result = Color.s('{W}%s Value{W}\n' % 'Configuration Key'.ljust(max_len)) result = Color.s('{W}%s Value{W}\n' % 'Configuration Key'.ljust(max_len))
result += Color.s('{W}%s------------------{W}\n' % ('-' * max_len)) result += Color.s('{W}%s------------------{W}\n' % ('-' * max_len))
for (key,val) in sorted(Configuration.__dict__.iteritems()): for (key,val) in sorted(Configuration.__dict__.items()):
if key.startswith('__') or type(val) == staticmethod or val is None: if key.startswith('__') or type(val) == staticmethod or val is None:
continue continue
result += Color.s("{G}%s {W} {C}%s{W}\n" % (key.ljust(max_len),val)) result += Color.s("{G}%s {W} {C}%s{W}\n" % (key.ljust(max_len),val))
@@ -345,4 +345,4 @@ class Configuration(object):
if __name__ == '__main__': if __name__ == '__main__':
Configuration.initialize(False) Configuration.initialize(False)
print Configuration.dump() print(Configuration.dump())

View File

@@ -30,7 +30,7 @@ class Client(object):
def __str__(self): def __str__(self):
''' String representation of a Client ''' ''' String representation of a Client '''
result = '' result = ''
for (key,value) in self.__dict__.iteritems(): for (key,value) in self.__dict__.items():
result += key + ': ' + str(value) result += key + ': ' + str(value)
result += ', ' result += ', '
return result return result
@@ -39,4 +39,4 @@ class Client(object):
if __name__ == '__main__': if __name__ == '__main__':
fields = 'AA:BB:CC:DD:EE:FF, 2015-05-27 19:43:47, 2015-05-27 19:43:47, -67, 2, (not associated) ,HOME-ABCD'.split(',') fields = 'AA:BB:CC:DD:EE:FF, 2015-05-27 19:43:47, 2015-05-27 19:43:47, -67, 2, (not associated) ,HOME-ABCD'.split(',')
c = Client(fields) c = Client(fields)
print c print('Client', c)

View File

@@ -3,9 +3,10 @@
from ..util.process import Process from ..util.process import Process
from ..util.color import Color from ..util.color import Color
from ..tools.tshark import Tshark
from ..tools.pyrit import Pyrit
import re import re, os
import os
class Handshake(object): class Handshake(object):
def __init__(self, capfile, bssid=None, essid=None): def __init__(self, capfile, bssid=None, essid=None):
@@ -18,15 +19,21 @@ class Handshake(object):
Tries to find BSSID and ESSID from cap file. Tries to find BSSID and ESSID from cap file.
Sets this instances 'bssid' and 'essid' instance fields. Sets this instances 'bssid' and 'essid' instance fields.
''' '''
if self.bssid is None:
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('-', ':')
# Get list of bssid/essid pairs from cap file # Get list of bssid/essid pairs from cap file
pairs = self.tshark_bssid_essid_pairs() pairs = Tshark.bssid_essid_pairs(self.capfile, bssid=self.bssid)
if len(pairs) == 0: if len(pairs) == 0:
# Find bssid/essid pairs that have handshakes in Pyrit pairs = self.pyrit_handshakes() # Find bssid/essid pairs that have handshakes in Pyrit
pairs = self.pyrit_handshakes()
if len(pairs) == 0 and not self.bssid and not self.essid: if len(pairs) == 0 and not self.bssid and not self.essid:
# Tshark and Pyrit failed us, nothing else we can do. raise Exception("Cannot find BSSID or ESSID in cap file") # Tshark and Pyrit failed us, nothing else we can do.
raise Exception("Cannot find BSSID or ESSID in cap file")
if not self.essid and not self.bssid: if not self.essid and not self.bssid:
# We do not know the bssid nor the essid # We do not know the bssid nor the essid
@@ -34,10 +41,8 @@ class Handshake(object):
# HACK: Just use the first one we see # HACK: Just use the first one we see
self.bssid = pairs[0][0] self.bssid = pairs[0][0]
self.essid = pairs[0][1] self.essid = pairs[0][1]
Color.pl('{!} {O}Warning{W}:' + Color.pl('{!} {O}Warning{W}: {O}Arbitrarily selected ' +
' {O}Arbitrarily selected' + '{R}bssid{O} {C}%s{O} and {R}essid{O} "{C}%s{O}"{W}' % (self.bssid, self.essid))
' {R}bssid{O} {C}%s{O} and {R}essid{O} "{C}%s{O}"{W}'
% (self.bssid, self.essid))
elif not self.bssid: elif not self.bssid:
# We already know essid # We already know essid
@@ -59,142 +64,20 @@ class Handshake(object):
if not self.bssid or not self.essid: if not self.bssid or not self.essid:
self.divine_bssid_and_essid() self.divine_bssid_and_essid()
if len(self.tshark_handshakes()) > 0: if len(self.tshark_handshakes()) > 0: return True
return True if len(self.pyrit_handshakes()) > 0: return True
if len(self.pyrit_handshakes()) > 0: # TODO: Can we trust cowpatty & aircrack?
return True #if len(self.cowpatty_handshakes()) > 0: return True
#if len(self.aircrack_handshakes()) > 0: return True
# XXX: Disabling these checks since I don't think they are reliable.
'''
if len(self.cowpatty_handshakes()) > 0:
return True
if len(self.aircrack_handshakes()) > 0:
return True
'''
return False return False
def tshark_bssid_essid_pairs(self):
'''
Scrapes capfile for beacon frames indicating the ESSID.
Returns list of tuples: (bssid,essid)
'''
if not Process.exists('tshark'):
raise Exception('tshark is required to find ESSID')
essids = set()
# Extract beacon frames from cap file
cmd = [
'tshark',
'-r', self.capfile,
'-R', 'wlan.fc.type_subtype == 0x08 || wlan.fc.type_subtype == 0x05',
'-2', # tshark: -R without -2 is deprecated.
'-n'
]
proc = Process(cmd, devnull=False)
for line in proc.stdout().split('\n'):
# Extract src, dst, and essid
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search('(%s) [^ ]* (%s).*.*SSID=(.*)$'
% (mac_regex, mac_regex), line)
if match is None:
# Line doesn't contain src, dst, ssid
continue
(src, dst, essid) = match.groups()
if dst.lower() == "ff:ff:ff:ff:ff:ff": continue
if self.bssid:
# We know the BSSID, only return the ESSID for this BSSID.
if self.bssid.lower() == src.lower() or self.bssid.lower() == dst.lower():
essids.add((src, essid))
else:
# We do not know BSSID, add it.
essids.add((src, essid))
# Return list of tuples
return [x for x in essids]
def tshark_command(self):
return [
'tshark',
'-r', self.capfile,
'-R', 'eapol',
'-n',
'-2' # 2-pass filtering, required when using -R in newer versions of tshark
]
def tshark_handshakes(self): def tshark_handshakes(self):
''' Returns True if tshark identifies a handshake, False otherwise ''' ''' Returns True if tshark identifies a handshake, False otherwise '''
if not Process.exists('tshark'): tshark_bssids = Tshark.bssids_with_handshakes(self.capfile, bssid=self.bssid)
return [] return [(bssid, None) for bssid in tshark_bssids]
target_client_msg_nums = {}
# Dump EAPOL packets
proc = Process(self.tshark_command(), devnull=False)
for line in proc.stdout().split('\n'):
# Extract source mac, destination mac, and message numbers
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search('(%s) (?:->|→) (%s).*Message.*(\d).*(\d)'
% (mac_regex, mac_regex), line)
if match is None:
# Line doesn't contain src, dst, Message numbers
continue
(src, dst, index, ttl) = match.groups()
# "Message (index) of (ttl)"
index = int(index)
ttl = int(ttl)
if ttl != 4:
# Must be a 4-way handshake
continue
# Identify the client and target MAC addresses
if index % 2 == 1:
# First and Third messages
target = src
client = dst
else:
# Second and Fourth messages
client = src
target = dst
if self.bssid and self.bssid.lower() != target.lower():
# We know the BSSID and this msg was not for the target
continue
target_client_key = '%s,%s' % (target, client)
# Ensure all 4 messages are:
# Between the same client and target
# In numeric & chronological order (1,2,3,4)
if index == 1:
# First message, add to dict
target_client_msg_nums[target_client_key] = 1
elif target_client_key not in target_client_msg_nums:
# Not first message, we haven't gotten the first message yet
continue
elif index - 1 != target_client_msg_nums[target_client_key]:
# Message is not in sequence
continue
else:
# Message is > 1 and is received in-order
target_client_msg_nums[target_client_key] = index
bssids = set()
# Check if we have all 4 messages for the handshake between the same MACs
for (client_target, num) in target_client_msg_nums.iteritems():
if num == 4:
# We got a handshake!
bssid = client_target.split(',')[0]
bssids.add(bssid)
return [(bssid, None) for bssid in bssids]
def cowpatty_command(self): def cowpatty_command(self):
@@ -219,83 +102,33 @@ class Handshake(object):
return [] return []
def pyrit_command(self):
return [
'pyrit',
'-r', self.capfile,
'analyze'
]
def pyrit_handshakes(self): def pyrit_handshakes(self):
''' Returns True if pyrit identifies a handshake, False otherwise ''' ''' Returns list of BSSID,ESSID tuples if pyrit identifies a handshake'''
if not Process.exists('pyrit'): return Pyrit.bssid_essid_with_handshakes(self.capfile, bssid=self.bssid, essid=self.essid)
return []
bssid_essid_pairs = set()
hit_target = False
current_bssid = self.bssid
current_essid = self.essid
proc = Process(self.pyrit_command(), devnull=False)
for line in proc.stdout().split('\n'):
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search("^#\d+: AccessPoint (%s) \('(.*)'\):$"
% (mac_regex), line)
if match:
# We found a BSSID and ESSID
(bssid, essid) = match.groups()
# Compare to what we're searching for
if self.bssid and self.bssid.lower() == bssid.lower():
current_essid = essid
hit_target = True
continue
elif self.essid and self.essid == essid:
current_bssid = bssid
hit_target = True
continue
elif not self.bssid and not self.essid:
# We don't know either
current_bssid = bssid
current_essid = essid
hit_target = True
else:
# This AccessPoint is not what we're looking for
hit_Target = False
else:
# Line does not contain AccessPoint
if hit_target and ', good' in line:
bssid_essid_pairs.add( (current_bssid, current_essid) )
return [x for x in bssid_essid_pairs]
def aircrack_command(self):
return 'echo "" | aircrack-ng -a 2 -w - -b %s "%s"' % (self.bssid, self.capfile)
def aircrack_handshakes(self): def aircrack_handshakes(self):
'''Returns tuple (BSSID,None) if aircrack thinks self.capfile contains a handshake / can be cracked'''
if not self.bssid: if not self.bssid:
return [] return [] # Aircrack requires BSSID
(stdout, stderr) = Process.call(self.aircrack_command())
command = 'echo "" | aircrack-ng -a 2 -w - -b %s "%s"' % (self.bssid, self.capfile)
(stdout, stderr) = Process.call(command)
if 'passphrase not in dictionary' in stdout.lower(): if 'passphrase not in dictionary' in stdout.lower():
return [(self.bssid, None)] return [(self.bssid, None)]
else: else:
return [] return []
def analyze(self): def analyze(self):
'''Prints analysis of handshake capfile'''
self.divine_bssid_and_essid() self.divine_bssid_and_essid()
pairs = self.tshark_handshakes() Handshake.print_pairs(self.tshark_handshakes(), self.capfile, 'tshark')
Handshake.print_pairs(pairs, self.capfile, 'tshark') Handshake.print_pairs(self.pyrit_handshakes(), self.capfile, 'pyrit')
Handshake.print_pairs(self.cowpatty_handshakes(), self.capfile, 'cowpatty')
pairs = self.pyrit_handshakes() Handshake.print_pairs(self.aircrack_handshakes(), self.capfile, 'aircrack')
Handshake.print_pairs(pairs, self.capfile, 'pyrit')
pairs = self.cowpatty_handshakes()
Handshake.print_pairs(pairs, self.capfile, 'cowpatty')
pairs = self.aircrack_handshakes()
Handshake.print_pairs(pairs, self.capfile, 'aircrack')
def strip(self, outfile=None): def strip(self, outfile=None):
@@ -316,8 +149,7 @@ class Handshake(object):
cmd = [ cmd = [
'tshark', 'tshark',
'-r', self.capfile, # input file '-r', self.capfile, # input file
'-R', 'wlan.fc.type_subtype == 0x08 || wlan.fc.type_subtype == 0x05 || eapol', # filter '-Y', 'wlan.fc.type_subtype == 0x08 || wlan.fc.type_subtype == 0x05 || eapol', # filter
'-2', # tshark: -R without -2 is deprecated.
'-w', outfile # output file '-w', outfile # output file
] ]
proc = Process(cmd) proc = Process(cmd)
@@ -335,32 +167,41 @@ class Handshake(object):
Prints out BSSID and/or ESSID given a list of tuples (bssid,essid) Prints out BSSID and/or ESSID given a list of tuples (bssid,essid)
''' '''
tool_str = '' tool_str = ''
if tool: if tool is not None:
tool_str = '{C}%s{W}: ' % tool.rjust(8) tool_str = '{C}%s{W}: ' % tool.rjust(8)
if len(pairs) == 0: if len(pairs) == 0:
Color.pl("{!} %s.cap file {R}does not{O} contain a valid handshake{W}" Color.pl("{!} %s.cap file {R}does not{O} contain a valid handshake{W}" % (tool_str))
% (tool_str))
return return
for (bssid, essid) in pairs: for (bssid, essid) in pairs:
out_str = '{+} %s.cap file {G}contains a valid handshake{W} for' % tool_str
if bssid and essid: if bssid and essid:
Color.pl('{+} %s.cap file' % tool_str + Color.pl('%s {G}%s{W} ({G}%s{W})' % (out_str, bssid, essid))
' {G}contains a valid handshake{W}' +
' for {G}%s{W} ({G}%s{W})' % (bssid, essid))
elif bssid: elif bssid:
Color.pl('{+} %s.cap file' % tool_str + Color.pl('%s {G}%s{W}' % (out_str, bssid))
' {G}contains a valid handshake{W}' +
' for {G}%s{W}' % bssid)
elif essid: elif essid:
Color.pl('{+} %s.cap file' % tool_str + Color.pl('%s ({G}%s{W})' % (out_str, essid))
' {G}contains a valid handshake{W}' +
' for ({G}%s{W})' % essid)
if __name__ == '__main__': if __name__ == '__main__':
hs = Handshake('./tests/files/handshake_exists.cap', bssid='A4:2B:8C:16:6B:3A') print('With BSSID & ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18', essid='YZWifi')
hs.analyze() hs.analyze()
print "has_hanshake() =", hs.has_handshake() print("has_hanshake() =", hs.has_handshake())
print('\nWith BSSID, but no ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap', bssid='18:d6:c7:6d:6b:18')
hs.analyze()
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('\nWith neither BSSID nor ESSID specified:')
hs = Handshake('./tests/files/handshake_has_1234.cap')
hs.analyze()
print("has_hanshake() =", hs.has_handshake())

View File

@@ -98,4 +98,4 @@ class Interface(object):
if __name__ == '__main__': if __name__ == '__main__':
mac = Interface.get_mac() mac = Interface.get_mac()
print 'wlan0mon mac address:', mac print('wlan0mon mac address:', mac)

View File

@@ -31,7 +31,7 @@ class CrackResult(object):
text = fid.read() text = fid.read()
try: try:
json = loads(text) json = loads(text)
except Exception, e: except Exception as e:
Color.pl('{!} error while loading %s: %s' % (name, str(e))) Color.pl('{!} error while loading %s: %s' % (name, str(e)))
json.append(self.to_dict()) json.append(self.to_dict())
with open(name, 'w') as fid: with open(name, 'w') as fid:

View File

@@ -147,5 +147,5 @@ if __name__ == '__main__':
t = Target(fields) t = Target(fields)
t.clients.append("asdf") t.clients.append("asdf")
t.clients.append("asdf") t.clients.append("asdf")
print t.to_str() print(t.to_str())

View File

@@ -45,8 +45,8 @@ if __name__ == '__main__':
w.dump() w.dump()
w = CrackResultWPA('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/capfile.cap', 'Key') w = CrackResultWPA('AA:BB:CC:DD:EE:FF', 'Test Router', 'hs/capfile.cap', 'Key')
print '\n' print('\n')
w.dump() w.dump()
w.save() w.save()
print w.__dict__['bssid'] print(w.__dict__['bssid'])

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..util.process import Process from ..util.process import Process
from ..util.input import xrange
from ..config import Configuration from ..config import Configuration
import os import os
@@ -39,44 +40,59 @@ class Aircrack(object):
def get_key_hex_ascii(self): def get_key_hex_ascii(self):
if not self.is_cracked(): if not self.is_cracked():
raise Exception('Cracked file not found') raise Exception('Cracked file not found')
with open(self.cracked_file, 'r') as fid: with open(self.cracked_file, 'r') as fid:
hex_raw = fid.read() hex_raw = fid.read()
hex_key = ''
return self._hex_and_ascii_key(hex_raw)
@staticmethod
def _hex_and_ascii_key(hex_raw):
hex_chars = []
ascii_key = '' ascii_key = ''
while len(hex_raw) > 0: for index in xrange(0, len(hex_raw), 2):
# HEX byt = hex_raw[index:index+2]
if hex_key != '': hex_chars.append(byt)
hex_key += ':' byt_int = int(byt, 16)
hex_key += hex_raw[0:2] if byt_int < 32 or byt_int > 127 or ascii_key is None:
ascii_key = None # Not printable
else:
ascii_key += chr(byt_int)
# ASCII hex_key = ':'.join(hex_chars)
# Convert hex to decimal
code = int(hex_raw[0:2], 16)
if code < 32 or code > 127:
# Hex key is non-printable in ascii
ascii_key = None
continue
elif ascii_key is None:
# We can't generate an Ascii key
continue
# Convert decimal to char
ascii_key += chr(code)
# Trim first two characters
hex_raw = hex_raw[2:]
continue
return (hex_key, ascii_key) return (hex_key, ascii_key)
def __del__(self):
if os.path.exists(self.cracked_file):
os.remove(self.cracked_file)
if __name__ == '__main__': 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
assert asciikey is None, 'asciikey was "%s", expected None' % asciikey
(hexkey, asciikey) = Aircrack._hex_and_ascii_key('6162636465')
assert hexkey == '61:62:63:64:65', 'hexkey was "%s", expected "61:62:63:64:65"' % hexkey
assert asciikey == 'abcde', 'asciikey was "%s", expected "abcde"' % asciikey
from time import sleep from time import sleep
Configuration.initialize(False) Configuration.initialize(False)
a = Aircrack('tests/files/wep-crackable.ivs')
while a.is_running(): ivs_file = 'tests/files/wep-crackable.ivs'
print("Running aircrack on %s ..." % ivs_file)
aircrack = Aircrack(ivs_file)
while aircrack.is_running():
sleep(1) sleep(1)
if a.is_cracked():
print "cracked!" assert aircrack.is_cracked(), "Aircrack should have cracked %s" % ivs_file
print '(hex, ascii) =', a.get_key_hex_ascii() print("aircrack process completed.")
else:
print "Not cracked" (hexkey, asciikey) = aircrack.get_key_hex_ascii()
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
Configuration.exit_gracefully(0) Configuration.exit_gracefully(0)

View File

@@ -29,7 +29,7 @@ class WEPAttackType(object):
self.value = None self.value = None
self.name = None self.name = None
if type(var) is int: if type(var) is int:
for (name,value) in WEPAttackType.__dict__.iteritems(): for (name,value) in WEPAttackType.__dict__.items():
if type(value) is int: if type(value) is int:
if value == var: if value == var:
self.name = name self.name = name
@@ -37,7 +37,7 @@ class WEPAttackType(object):
return return
raise Exception("Attack number %d not found" % var) raise Exception("Attack number %d not found" % var)
elif type(var) is str: elif type(var) is str:
for (name,value) in WEPAttackType.__dict__.iteritems(): for (name,value) in WEPAttackType.__dict__.items():
if type(value) is int: if type(value) is int:
if name == var: if name == var:
self.name = name self.name = name
@@ -90,17 +90,25 @@ class Aireplay(Thread):
def get_output(self): def get_output(self):
''' Returns stdout from aireplay process ''' ''' Returns stdout from aireplay process '''
return self.pid.stdout() return self.stdout
def run(self): def run(self):
self.stdout = ''
self.xor_percent = '0%'
while self.pid.poll() is None: while self.pid.poll() is None:
time.sleep(0.1) time.sleep(0.1)
if not os.path.exists(self.output_file): continue if not os.path.exists(self.output_file): continue
# Read output file & clear output file # Read output file & clear output file
with open(self.output_file, "r+") as fid: with open(self.output_file, "r+") as fid:
lines = fid.read() lines = fid.read()
self.stdout += lines
fid.seek(0) fid.seek(0)
fid.truncate() fid.truncate()
if Configuration.verbose > 1 and lines.strip() != '':
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"): for line in lines.split("\n"):
line = line.replace("\r", "").strip() line = line.replace("\r", "").strip()
if line == "": continue if line == "": continue
@@ -124,33 +132,85 @@ class Aireplay(Thread):
self.status = True self.status = True
elif self.attack_type == WEPAttackType.chopchop: elif self.attack_type == WEPAttackType.chopchop:
# Look for chopchop status. Potential output lines: # Look for chopchop status. Potential output lines:
# (START) Read 178 packets... # (START) Read 178 packets...
read_re = re.compile(r"Read (\d+) packets") read_re = re.compile(r"Read (\d+) packets")
matches = read_re.match(line) matches = read_re.match(line)
if matches: if matches:
self.status = "Waiting for packet (read %s)..." % matches.group(1) self.status = "Waiting for packet (read %s)..." % matches.group(1)
# Sent 1912 packets, current guess: 70...
sent_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))
# (DURING) Offset 52 (54% done) | xor = DE | pt = E0 | 152 frames written in 2782ms # (DURING) Offset 52 (54% done) | xor = DE | pt = E0 | 152 frames written in 2782ms
offset_re = re.compile(r"Offset.*\(\s*(\d+%) done\)") offset_re = re.compile(r"Offset.*\(\s*(\d+%) done\)")
matches = offset_re.match(line) matches = offset_re.match(line)
if matches: if matches:
self.status = "Generating Xor (%s)" % matches.group(1) self.xor_percent = matches.group(1)
self.status = "Generating .xor (%s)..." % matches.group(1)
# (DONE) Saving keystream in replay_dec-0516-202246.xor # (DONE) Saving keystream in replay_dec-0516-202246.xor
saving_re = re.compile(r"Saving keystream in (.*\.xor)") saving_re = re.compile(r"Saving keystream in (.*\.xor)")
matches = saving_re.match(line) matches = saving_re.match(line)
if matches: if matches:
self.status = matches.group(1) self.status = matches.group(1)
pass
# (ERROR) fakeauth required
if 'try running aireplay-ng in authenticated mode' in line:
self.status = 'fakeauth is required and you are not authenticated'
elif self.attack_type == WEPAttackType.fragment: elif self.attack_type == WEPAttackType.fragment:
# TODO: Parse fragment output, update self.status # Parse fragment output, update self.status
# (START) Read 178 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)
# 01:08:15 Waiting for a data packet... # 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")
matches = trying_re.match(line)
if matches:
self.status = 'trying to get %sb of a keystream' % matches.group(1)
# 01:08:17 Sending fragmented packet # 01:08:17 Sending fragmented packet
if 'Sending fragmented packet' in line:
self.status = 'sending packet'
# 01:08:37 Still nothing, trying another packet... # 01:08:37 Still nothing, trying another packet...
if 'Still nothing, trying another packet' in line:
self.status = 'sending another packet'
# XX:XX:XX Trying to get 1500 bytes of a keystream # XX:XX:XX Trying to get 1500 bytes of a keystream
trying_re = re.compile(r"Trying to get (\d+) bytes of a keystream")
matches = trying_re.match(line)
if matches:
self.status = 'trying to get %sb of a keystream' % matches.group(1)
# XX:XX:XX Got RELAYED packet!! # XX:XX:XX Got RELAYED packet!!
if 'Got RELAYED packet' in line:
self.status = 'got relayed packet'
# XX:XX:XX Thats our ARP packet! # XX:XX:XX Thats our ARP packet!
if 'Thats our ARP packet' in line:
self.status = 'relayed packet was our'
# XX:XX:XX Saving keystream in fragment-0124-161129.xor # XX:XX:XX Saving keystream in fragment-0124-161129.xor
saving_re = re.compile(r"Saving keystream in (.*\.xor)")
matches = saving_re.match(line)
if matches:
self.status = 'saving keystream to %s' % saving_re.group(1)
# XX:XX:XX Now you can build a packet with packetforge-ng out of that 1500 bytes keystream # XX:XX:XX Now you can build a packet with packetforge-ng out of that 1500 bytes keystream
pass
else: # Replay, forged replay, etc. else: # Replay, forged replay, etc.
# Parse Packets Sent & PacketsPerSecond. Possible output lines: # Parse Packets Sent & PacketsPerSecond. Possible output lines:
# Read 55 packets (got 0 ARP requests and 0 ACKs), sent 0 packets...(0 pps) # Read 55 packets (got 0 ARP requests and 0 ACKs), sent 0 packets...(0 pps)
@@ -188,7 +248,7 @@ class Aireplay(Thread):
cmd = ["aireplay-ng"] cmd = ["aireplay-ng"]
cmd.append("--ignore-negative-one") cmd.append("--ignore-negative-one")
if not client_mac and len(target.clients) > 0: if client_mac is None and len(target.clients) > 0:
# Client MAC wasn't specified, but there's an associated client. Use that. # Client MAC wasn't specified, but there's an associated client. Use that.
client_mac = target.clients[0].station client_mac = target.clients[0].station
@@ -378,12 +438,12 @@ class Aireplay(Thread):
if __name__ == '__main__': if __name__ == '__main__':
t = WEPAttackType(4) t = WEPAttackType(4)
print t.name, type(t.name), t.value print(t.name, type(t.name), t.value)
t = WEPAttackType('caffelatte') t = WEPAttackType('caffelatte')
print t.name, type(t.name), t.value print(t.name, type(t.name), t.value)
t = WEPAttackType(t) t = WEPAttackType(t)
print t.name, type(t.name), t.value print(t.name, type(t.name), t.value)
from ..model.target import Target 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(',')
@@ -395,13 +455,13 @@ if __name__ == '__main__':
from time import sleep from time import sleep
sleep(0.1) sleep(0.1)
stdout, stderr = aireplay.get_output() stdout, stderr = aireplay.get_output()
print "STDOUT>", stdout print("STDOUT>", stdout)
print "STDERR>", stderr print("STDERR>", stderr)
''' '''
''' '''
forge = Aireplay.forge_packet('/tmp/replay_dec-0605-060243.xor', \ forge = Aireplay.forge_packet('/tmp/replay_dec-0605-060243.xor', \
'A4:2B:8C:16:6B:3A', \ 'A4:2B:8C:16:6B:3A', \
'00:C0:CA:4E:CA:E0') '00:C0:CA:4E:CA:E0')
print forge print(forge)
''' '''

View File

@@ -4,6 +4,7 @@
from ..model.interface import Interface from ..model.interface import Interface
from ..util.process import Process from ..util.process import Process
from ..util.color import Color from ..util.color import Color
from ..util.input import raw_input
from ..config import Configuration from ..config import Configuration
import re import re
@@ -28,7 +29,7 @@ class Airmon(object):
def print_menu(self): def print_menu(self):
''' Prints menu ''' ''' Prints menu '''
print Interface.menu_header() print(Interface.menu_header())
for idx, iface in enumerate(self.interfaces, start=1): for idx, iface in enumerate(self.interfaces, start=1):
Color.pl(" {G}%d{W}. %s" % (idx, iface)) Color.pl(" {G}%d{W}. %s" % (idx, iface))
@@ -114,6 +115,7 @@ class Airmon(object):
if mon_iface is None: if mon_iface is None:
# Airmon did not enable monitor mode on an interface # Airmon did not enable monitor mode on an interface
mon_iface = Airmon.start_baddriver(iface) mon_iface = Airmon.start_baddriver(iface)
if mon_iface is None: if mon_iface is None:
Color.pl("{R}failed{W}") Color.pl("{R}failed{W}")

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .tshark import Tshark from .tshark import Tshark
from .wash import Wash
from ..util.process import Process from ..util.process import Process
from ..config import Configuration from ..config import Configuration
from ..model.target import Target from ..model.target import Target
@@ -142,7 +143,11 @@ class Airodump(object):
# Check targets for WPS # Check targets for WPS
if not self.skip_wps: if not self.skip_wps:
capfile = csv_filename[:-3] + 'cap' capfile = csv_filename[:-3] + 'cap'
try:
Tshark.check_for_wps_and_update_targets(capfile, targets) Tshark.check_for_wps_and_update_targets(capfile, targets)
except Exception as e:
# No tshark, or it failed. Fall-back to wash
Wash.check_for_wps_and_update_targets(capfile, targets)
if apply_filter: if apply_filter:
# Filter targets based on encryption & WPS capability # Filter targets based on encryption & WPS capability
@@ -175,8 +180,13 @@ class Airodump(object):
targets = [] targets = []
import csv import csv
with open(csv_filename, 'rb') as csvopen: with open(csv_filename, 'rb') as csvopen:
lines = (line.replace('\0', '') for line in csvopen) lines = []
for line in csvopen:
if type(line) is bytes: line = line.decode('utf-8')
line = line.replace('\0', '')
lines.append(line)
csv_reader = csv.reader(lines, delimiter=',') csv_reader = csv.reader(lines, delimiter=',')
hit_clients = False hit_clients = False
for row in csv_reader: for row in csv_reader:
# Each "row" is a list of fields for a target/client # Each "row" is a list of fields for a target/client

View File

@@ -2,12 +2,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..model.attack import Attack from ..model.attack import Attack
from ..model.wps_result import CrackResultWPS
from ..tools.airodump import Airodump from ..tools.airodump import Airodump
from ..util.color import Color from ..util.color import Color
from ..util.timer import Timer from ..util.timer import Timer
from ..util.process import Process from ..util.process import Process
from ..config import Configuration from ..config import Configuration
from ..model.wps_result import CrackResultWPS
import os, time, re import os, time, re
from threading import Thread from threading import Thread
@@ -26,8 +26,14 @@ class Bully(Attack):
self.target = target self.target = target
self.cmd = [ self.cmd = []
"stdbuf", "-o0", # No buffer. See https://stackoverflow.com/a/40453613/7510292
if Process.exists('stdbuf'):
self.cmd.extend([
"stdbuf", "-o0" # No buffer. See https://stackoverflow.com/a/40453613/7510292
])
self.cmd.extend([
"bully", "bully",
"--bssid", target.bssid, "--bssid", target.bssid,
"--channel", target.channel, "--channel", target.channel,
@@ -36,7 +42,7 @@ class Bully(Attack):
"-v", "4", "-v", "4",
"--pixiewps", "--pixiewps",
Configuration.interface Configuration.interface
] ])
self.bully_proc = None self.bully_proc = None
@@ -214,7 +220,49 @@ class Bully(Attack):
def __del__(self): def __del__(self):
self.stop() self.stop()
@staticmethod
def get_psk_from_pin(target, pin):
'''
bully --channel 1 --bssid 34:21:09:01:92:7C --pin 01030365 --bruteforce wlan0mon
PIN : '01030365'
KEY : 'password'
BSSID : '34:21:09:01:92:7c'
ESSID : 'AirLink89300'
'''
Color.pl('\n{+} found PIN: {G}%s{W}' % pin)
Color.p('{+} fetching {C}PSK{W} using {C}bully{W}... ')
cmd = [
'bully',
'--channel', target.channel,
'--bssid', target.bssid,
'--pin', pin,
'--bruteforce',
'--force',
Configuration.interface
]
bully_proc = Process(cmd)
for line in bully_proc.stderr().split('\n'):
key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line)
if key_re is not None:
psk = key_re.group(1)
Color.pl('{W}found PSK: {G}%s{W}' % psk)
return psk
Color.pl('{R}failed{W}')
return None
if __name__ == '__main__': if __name__ == '__main__':
Configuration.initialize()
Configuration.interface = 'wlan0mon'
from ..model.target import Target
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)
'''
stdout = " [*] Pin is '11867722', key is '9a6f7997'" stdout = " [*] Pin is '11867722', key is '9a6f7997'"
Configuration.initialize(False) Configuration.initialize(False)
from ..model.target import Target from ..model.target import Target
@@ -222,3 +270,4 @@ if __name__ == '__main__':
target = Target(fields) target = Target(fields)
b = Bully(target) b = Bully(target)
b.parse_line(stdout) b.parse_line(stdout)
'''

59
wifite/tools/pyrit.py Normal file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.process import Process
import re
class Pyrit(object):
''' Wrapper for Pyrit program. '''
def __init__(self):
pass
@staticmethod
def exists():
return Process.exists('pyrit')
@staticmethod
def bssid_essid_with_handshakes(capfile, bssid=None, essid=None):
if not Pyrit.exists():
return []
command = [
'pyrit',
'-r', capfile,
'analyze'
]
pyrit = Process(command, devnull=False)
current_bssid = current_essid = None
bssid_essid_pairs = set()
'''
#1: AccessPoint 18:a6:f7:31:d2:06 ('TP-LINK_D206'):
#1: Station 08:66:98:b2:ab:28, 1 handshake(s):
#1: HMAC_SHA1_AES, good, spread 1
#2: Station ac:63:be:3a:a2:f4
'''
for line in pyrit.stdout().split('\n'):
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search("^#\d+: AccessPoint (%s) \('(.*)'\):$" % (mac_regex), line)
if match:
# We found a new BSSID and ESSID
(current_bssid, current_essid) = match.groups()
if bssid is not None and bssid.lower() != current_bssid:
current_bssid = None
current_essid = None
elif essid is not None and essid != current_essid:
current_bssid = None
current_essid = None
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"
if ', good' in line:
bssid_essid_pairs.add( (current_bssid, current_essid) )
return list(bssid_essid_pairs)

View File

@@ -6,6 +6,7 @@ from ..config import Configuration
from ..util.color import Color from ..util.color import Color
from ..util.process import Process from ..util.process import Process
from ..tools.airodump import Airodump from ..tools.airodump import Airodump
from ..tools.bully import Bully # for PSK retrieval
from ..model.wps_result import CrackResultWPS from ..model.wps_result import CrackResultWPS
import os, time, re import os, time, re
@@ -33,6 +34,7 @@ class Reaver(Attack):
'--bssid', self.target.bssid, '--bssid', self.target.bssid,
'--channel', self.target.channel, '--channel', self.target.channel,
'--pixie-dust', '1', # pixie-dust attack '--pixie-dust', '1', # pixie-dust attack
'--timeout', '4', # Stop waiting after 4 seconds
#'--delay', '0', #'--delay', '0',
#'--no-nacks', #'--no-nacks',
'--session', '/dev/null', # Don't restart session '--session', '/dev/null', # Don't restart session
@@ -80,8 +82,16 @@ class Reaver(Attack):
# Check if we cracked it. # Check if we cracked it.
if pin is not None: if pin is not None:
# We cracked it. # We cracked it.
if psk is None:
# Try to derive PSK from PIN using Bully
psk = Bully.get_psk_from_pin(self.target, pin)
bssid = self.target.bssid bssid = self.target.bssid
Color.clear_entire_line() Color.clear_entire_line()
if psk is None:
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN{W} (but not PSK)")
else:
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}") Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}")
Color.pl("") Color.pl("")
self.crack_result = CrackResultWPS(bssid, ssid, pin, psk) self.crack_result = CrackResultWPS(bssid, ssid, pin, psk)
@@ -258,7 +268,7 @@ executing pixiewps -e d0141b15656e96b85fcead2e8e76330d2b1ac1576bb026e7a328c0e1ba
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk) result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump() result.dump()
print "" print("")
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(new_stdout) (pin, psk, ssid) = Reaver.get_pin_psk_ssid(new_stdout)
assert pin == '11867722', 'pin was "%s", should have been "11867722"' % pin assert pin == '11867722', 'pin was "%s", should have been "11867722"' % pin

View File

@@ -10,6 +10,140 @@ class Tshark(object):
def __init__(self): def __init__(self):
pass pass
@staticmethod
def exists():
return Process.exists('tshark')
@staticmethod
def _extract_src_dst_index_total(line):
# 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:
# Line doesn't contain src, dst, Message numbers
return None, None, None, None
(src, dst, index, total) = match.groups()
return src, dst, index, total
@staticmethod
def _build_target_client_handshake_map(output, bssid=None):
# Map of target_ssid,client_ssid -> handshake #s
# E.g. 12:34:56,21:43:65 -> 3
target_client_msg_nums = {}
for line in output.split('\n'):
src, dst, index, total = Tshark._extract_src_dst_index_total(line)
if src is None: continue # Skip
index = int(index)
total = int(total)
if total != 4: continue # Handshake X of 5? X of 3? Skip it.
# Identify the client and target MAC addresses
if index % 2 == 1:
# First and Third messages
target = src
client = dst
else:
# Second and Fourth messages
client = src
target = dst
if bssid is not None and bssid.lower() != target.lower():
# We know the BSSID and this msg was not for the target
continue
target_client_key = '%s,%s' % (target, client)
# Ensure all 4 messages are:
# Between the same client and target (not different clients connecting).
# In numeric & chronological order (Message 1, then 2, then 3, then 4)
if index == 1:
target_client_msg_nums[target_client_key] = 1 # First message
elif target_client_key not in target_client_msg_nums:
continue # Not first message. We haven't gotten the first message yet. Skip.
elif index - 1 != target_client_msg_nums[target_client_key]:
continue # Message is not in sequence. Skip
else:
# Happy case: Message is > 1 and is received in-order
target_client_msg_nums[target_client_key] = index
return target_client_msg_nums
@staticmethod
def bssids_with_handshakes(capfile, bssid=None):
if not Tshark.exists():
return []
# Returns list of BSSIDs for which we have valid handshakes in the capfile.
command = [
'tshark',
'-r', capfile,
'-n', # Don't resolve addresses
'-Y', 'eapol' # Filter for only handshakes
]
tshark = Process(command, devnull=False)
target_client_msg_nums = Tshark._build_target_client_handshake_map(tshark.stdout(), bssid=bssid)
bssids = set()
# Check if we have all 4 messages for the handshake between the same MACs
for (target_client, num) in target_client_msg_nums.items():
if num == 4:
# We got a handshake!
this_bssid = target_client.split(',')[0]
bssids.add(this_bssid)
return list(bssids)
@staticmethod
def bssid_essid_pairs(capfile, bssid):
# Finds all BSSIDs (with corresponding ESSIDs) from cap file.
# Returns list of tuples(BSSID, ESSID)
if not Tshark.exists():
return []
ssid_pairs = set()
command = [
'tshark',
'-r', capfile, # Path to cap file
'-n', # Don't resolve addresses
# Extract beacon frames
'-Y', '"wlan.fc.type_subtype == 0x08 || wlan.fc.type_subtype == 0x05"',
]
tshark = Process(command, devnull=False)
for line in tshark.stdout().split('\n'):
# Extract src, dst, and essid
mac_regex = ('[a-zA-Z0-9]{2}:' * 6)[:-1]
match = re.search('(%s) [^ ]* (%s).*.*SSID=(.*)$' % (mac_regex, mac_regex), line)
if match is None:
continue # Line doesn't contain src, dst, ssid
(src, dst, essid) = match.groups()
if dst.lower() == "ff:ff:ff:ff:ff:ff":
continue # Skip broadcast packets
if bssid is not None:
# We know the BSSID, only return the ESSID for this BSSID.
if bssid.lower() == src.lower():
ssid_pairs.add((src, essid)) # This is our BSSID, add it
else:
ssid_pairs.add((src, essid)) # We do not know BSSID, add it.
return list(ssid_pairs)
@staticmethod @staticmethod
def check_for_wps_and_update_targets(capfile, targets): def check_for_wps_and_update_targets(capfile, targets):
''' '''
@@ -21,9 +155,9 @@ class Tshark(object):
capfile - .cap file from airodump containing packets capfile - .cap file from airodump containing packets
targets - list of Targets from scan, to be updated targets - list of Targets from scan, to be updated
''' '''
# Tshark is required to detect WPS networks
if not Process.exists('tshark'): if not Tshark.exists():
return raise Exception('Cannot detect WPS networks: Tshark does not exist')
command = [ command = [
'tshark', 'tshark',
@@ -38,7 +172,6 @@ class Tshark(object):
] ]
p = Process(command) p = Process(command)
try: try:
p.wait() p.wait()
lines = p.stdout() lines = p.stdout()
@@ -63,6 +196,7 @@ if __name__ == '__main__':
test_file = './tests/files/contains_wps_network.cap' test_file = './tests/files/contains_wps_network.cap'
target_bssid = 'A4:2B:8C:16:6B:3A' target_bssid = 'A4:2B:8C:16:6B:3A'
'''
from ..model.target import Target from ..model.target import Target
fields = [ fields = [
'A4:2B:8C:16:6B:3A', # BSSID 'A4:2B:8C:16:6B:3A', # BSSID
@@ -79,6 +213,8 @@ if __name__ == '__main__':
# Should update 'wps' field of a target # Should update 'wps' field of a target
Tshark.check_for_wps_and_update_targets(test_file, targets) Tshark.check_for_wps_and_update_targets(test_file, targets)
print 'Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps) print('Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps))
assert targets[0].wps == True assert targets[0].wps == True
'''
print(Tshark.bssids_with_handshakes(test_file, bssid=target_bssid))

75
wifite/tools/wash.py Normal file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from ..util.process import Process
import json
class Wash(object):
''' Wrapper for Wash program. '''
def __init__(self):
pass
@staticmethod
def exists():
return Process.exists('wash')
@staticmethod
def check_for_wps_and_update_targets(capfile, targets):
if not Wash.exists():
return
command = [
'wash',
'-f', capfile,
'-j' # json
]
p = Process(command)
try:
p.wait()
lines = p.stdout()
except:
# Failure is acceptable
return
# Find all BSSIDs
bssids = set()
for line in lines.split('\n'):
try:
obj = json.loads(line)
bssid = obj['bssid']
locked = obj['wps_locked']
if locked != True:
bssids.add(bssid)
except:
pass
# Update targets
for t in targets:
t.wps = t.bssid.upper() in bssids
if __name__ == '__main__':
test_file = './tests/files/contains_wps_network.cap'
target_bssid = 'A4:2B:8C:16:6B:3A'
from ..model.target import Target
fields = [
'A4:2B:8C:16:6B:3A', # BSSID
'2015-05-27 19:28:44', '2015-05-27 19:28:46', # Dates
'11', # Channel
'54', # throughput
'WPA2', 'CCMP TKIP', 'PSK', # AUTH
'-58', '2', '0', '0.0.0.0', '9', # ???
'Test Router Please Ignore', # SSID
]
t = Target(fields)
targets = [t]
# Should update 'wps' field of a target
Wash.check_for_wps_and_update_targets(test_file, targets)
print('Target(BSSID={}).wps = {} (Expected: True)'.format(targets[0].bssid, targets[0].wps))
assert targets[0].wps == True

View File

@@ -21,7 +21,7 @@ class Color(object):
# Helper string replacements # Helper string replacements
replacements = { replacements = {
'{+}': ' {W}[{G}+{W}]', '{+}': ' {W}{D}[{W}{G}+{W}{D}]{W}',
'{!}': ' {O}[{R}!{O}]{W}', '{!}': ' {O}[{R}!{O}]{W}',
'{?}': ' {W}[{C}?{W}]' '{?}': ' {W}[{C}?{W}]'
} }
@@ -63,9 +63,9 @@ class Color(object):
def s(text): def s(text):
''' Returns colored string ''' ''' Returns colored string '''
output = text output = text
for (key,value) in Color.replacements.iteritems(): for (key,value) in Color.replacements.items():
output = output.replace(key, value) output = output.replace(key, value)
for (key,value) in Color.colors.iteritems(): for (key,value) in Color.colors.items():
output = output.replace("{%s}" % key, value) output = output.replace("{%s}" % key, value)
return output return output
@@ -96,7 +96,7 @@ class Color(object):
if __name__ == '__main__': if __name__ == '__main__':
Color.pl("{R}Testing{G}One{C}Two{P}Three{W}Done") Color.pl("{R}Testing{G}One{C}Two{P}Three{W}Done")
print Color.s("{C}Testing{P}String{W}") print(Color.s("{C}Testing{P}String{W}"))
Color.pl("{+} Good line") Color.pl("{+} Good line")
Color.pl("{!} Danger") Color.pl("{!} Danger")

View File

@@ -3,6 +3,7 @@
from ..util.process import Process from ..util.process import Process
from ..util.color import Color from ..util.color import Color
from ..util.input import raw_input
from ..config import Configuration from ..config import Configuration
from ..model.result import CrackResult from ..model.result import CrackResult

17
wifite/util/input.py Normal file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
# Fix for raw_input on python3: https://stackoverflow.com/a/7321970
try:
input = raw_input
except NameError:
pass
raw_input = input
try:
range = xrange
except NameError:
pass
xrange = range

View File

@@ -36,9 +36,14 @@ class Process(object):
pid.wait() pid.wait()
(stdout, stderr) = pid.communicate() (stdout, stderr) = pid.communicate()
if Configuration.verbose > 1 and stdout.strip() != '': # Python 3 compatibility
if type(stdout) is bytes: stdout = stdout.decode('utf-8')
if type(stderr) is bytes: stderr = stderr.decode('utf-8')
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.strip() != '': if Configuration.verbose > 1 and stderr is not None and stderr.strip() != '':
Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(stderr.strip().split('\n'))) Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(stderr.strip().split('\n')))
return (stdout, stderr) return (stdout, stderr)
@@ -91,14 +96,14 @@ class Process(object):
def stdout(self): def stdout(self):
''' Waits for process to finish, returns stdout output ''' ''' Waits for process to finish, returns stdout output '''
self.get_output() self.get_output()
if Configuration.verbose > 1 and self.out.strip() != '': if Configuration.verbose > 1 and self.out is not None and self.out.strip() != '':
Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(self.out.strip().split('\n'))) Color.pe("{P} [stdout] %s{W}" % '\n [stdout] '.join(self.out.strip().split('\n')))
return self.out return self.out
def stderr(self): def stderr(self):
''' Waits for process to finish, returns stderr output ''' ''' Waits for process to finish, returns stderr output '''
self.get_output() self.get_output()
if Configuration.verbose > 1 and self.err.strip() != '': if Configuration.verbose > 1 and self.err is not None and self.err.strip() != '':
Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(self.err.strip().split('\n'))) Color.pe("{P} [stderr] %s{W}" % '\n [stderr] '.join(self.err.strip().split('\n')))
return self.err return self.err
@@ -114,6 +119,13 @@ class Process(object):
self.pid.wait() self.pid.wait()
if self.out is None: if self.out is None:
(self.out, self.err) = self.pid.communicate() (self.out, self.err) = self.pid.communicate()
if type(self.out) is bytes:
self.out = self.out.decode('utf-8')
if type(self.err) is bytes:
self.err = self.err.decode('utf-8')
return (self.out, self.err) return (self.out, self.err)
def poll(self): def poll(self):
@@ -151,7 +163,7 @@ class Process(object):
self.pid.terminate() self.pid.terminate()
break break
except OSError, e: except OSError as e:
if 'No such process' in e.__str__(): if 'No such process' in e.__str__():
return return
raise e # process cannot be killed raise e # process cannot be killed
@@ -159,20 +171,20 @@ class Process(object):
if __name__ == '__main__': if __name__ == '__main__':
p = Process('ls') p = Process('ls')
print p.stdout(), p.stderr() print(p.stdout(), p.stderr())
p.interrupt() p.interrupt()
# Calling as list of arguments # Calling as list of arguments
(out, err) = Process.call(['ls', '-lah']) (out, err) = Process.call(['ls', '-lah'])
print out, err print(out, err)
print '\n---------------------\n' print('\n---------------------\n')
# Calling as string # Calling as string
(out, err) = Process.call('ls -l | head -2') (out, err) = Process.call('ls -l | head -2')
print out, err print(out, err)
print '"reaver" exists:', Process.exists('reaver') print('"reaver" exists:', Process.exists('reaver'))
# Test on never-ending process # Test on never-ending process
p = Process('yes') p = Process('yes')

View File

@@ -3,6 +3,7 @@
from ..tools.airodump import Airodump from ..tools.airodump import Airodump
from ..util.color import Color from ..util.color import Color
from ..util.input import raw_input, xrange
from ..model.target import Target from ..model.target import Target
from ..config import Configuration from ..config import Configuration
@@ -40,7 +41,7 @@ class Scanner(object):
try: try:
self.targets = airodump.get_targets() self.targets = airodump.get_targets()
except Exception, e: except Exception as e:
break break
if self.found_target(): if self.found_target():
@@ -198,7 +199,8 @@ class Scanner(object):
chosen_targets = [] chosen_targets = []
for choice in raw_input(Color.s(input_str)).split(','): for choice in raw_input(Color.s(input_str)).split(','):
if choice == 'all': choice = choice.strip()
if choice.lower() == 'all':
chosen_targets = self.targets chosen_targets = self.targets
break break
if '-' in choice: if '-' in choice:
@@ -219,7 +221,7 @@ if __name__ == '__main__':
try: try:
s = Scanner() s = Scanner()
targets = s.select_targets() targets = s.select_targets()
except Exception, e: except Exception as e:
Color.pl('\r {!} {R}Error{W}: %s' % str(e)) Color.pl('\r {!} {R}Error{W}: %s' % str(e))
Configuration.exit_gracefully(0) Configuration.exit_gracefully(0)
for t in targets: for t in targets:

View File

@@ -2,19 +2,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
try: try:
from config import Configuration from .config import Configuration
except (ValueError, ImportError) as e: except (ValueError, ImportError) as e:
raise Exception('You may need to run wifite from the root directory (which includes README.md)') raise Exception('You may need to run wifite from the root directory (which includes README.md)')
from util.scanner import Scanner from .util.scanner import Scanner
from util.process import Process from .util.process import Process
from util.color import Color from .util.color import Color
from util.crack import CrackHandshake from .util.crack import CrackHandshake
from attack.wep import AttackWEP from .util.input import raw_input
from attack.wpa import AttackWPA from .attack.wep import AttackWEP
from attack.wps import AttackWPS from .attack.wpa import AttackWPA
from model.result import CrackResult from .attack.wps import AttackWPS
from model.handshake import Handshake from .model.result import CrackResult
from .model.handshake import Handshake
import json import json
import os import os
@@ -47,8 +48,8 @@ class Wifite(object):
def dependency_check(self): def dependency_check(self):
''' Check that required programs are installed ''' ''' Check that required programs are installed '''
required_apps = ['airmon-ng', 'iwconfig', 'ifconfig', 'aircrack-ng', 'aireplay-ng', 'airodump-ng', 'tshark'] required_apps = ['airmon-ng', 'iwconfig', 'ifconfig', 'aircrack-ng', 'aireplay-ng', 'airodump-ng']
optional_apps = ['packetforge-ng', 'reaver', 'bully', 'cowpatty', 'pyrit', 'stdbuf', 'macchanger'] optional_apps = ['packetforge-ng', 'reaver', 'bully', 'cowpatty', 'pyrit', 'stdbuf', 'macchanger', 'tshark']
missing_required = False missing_required = False
missing_optional = False missing_optional = False
@@ -95,7 +96,7 @@ class Wifite(object):
Color.pl('{+} checking all handshakes in {G}"./hs"{W} directory\n') Color.pl('{+} checking all handshakes in {G}"./hs"{W} directory\n')
try: try:
capfiles = [os.path.join('hs', x) for x in os.listdir('hs') if x.endswith('.cap')] capfiles = [os.path.join('hs', x) for x in os.listdir('hs') if x.endswith('.cap')]
except OSError, e: except OSError as e:
capfiles = [] capfiles = []
if len(capfiles) == 0: if len(capfiles) == 0:
Color.pl('{!} {R}no .cap files found in {O}"./hs"{W}\n') Color.pl('{!} {R}no .cap files found in {O}"./hs"{W}\n')
@@ -173,7 +174,7 @@ class Wifite(object):
try: try:
attack.run() attack.run()
except Exception, e: except Exception as e:
Color.pl("\n{!} {R}Error: {O}%s" % str(e)) Color.pl("\n{!} {R}Error: {O}%s" % str(e))
if Configuration.verbose > 0 or True: if Configuration.verbose > 0 or True:
Color.pl('\n{!} {O}Full stack trace below') Color.pl('\n{!} {O}Full stack trace below')
@@ -234,7 +235,7 @@ def run():
try: try:
w.main() w.main()
except Exception, e: except Exception as e:
Color.pl('\n{!} {R}Error:{O} %s{W}' % str(e)) Color.pl('\n{!} {R}Error:{O} %s{W}' % str(e))
if Configuration.verbose > 0 or True: if Configuration.verbose > 0 or True: