DF-0351 / hwmp_metric_overflow.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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /* * DF-0351 — HWMP uint32 metric-accumulation overflow harness. * * This finding targets the in-kernel 802.11s HWMP routing code in * sys/netproto/802_11/wlan/ieee80211_hwmp.c. It is a LOGIC / PROTOCOL * vulnerability (route poisoning / hijacking via integer overflow), NOT a * kernel memory-corruption primitive, so there is no slab/UAF escalation * chain to develop. The audit guest also has no WiFi hardware and no * mesh-mode VAP, so the live in-kernel path cannot be driven here. This * harness instead reproduces the EXACT arithmetic and route-accept logic of * the cited code paths, proving the overflow is real and attacker-controlled, * and proving the saturating-add fix closes it. * * Why attacker-controlled (no WiFi HW needed to see it): * ieee80211_hwmp.c:458 preq->preq_metric = le32dec(iefrm_t); * The metric field is read verbatim from the on-air HWMP frame with NO * range/sanity check, so an attacker transmitting a PREQ sets it to any * uint32_t, including values crafted to make `preq_metric + link_metric` * wrap modulo 2^32. PREP (:1453) and RANN (:1972) metrics are parsed the * same way (analogous le32dec sites). * * Overflow sites (all plain uint32_t add, no saturation): * :1089 metric = preq->preq_metric + mpm_metric(ni); (PREQ recv) * :1325 ppreq.preq_metric += mpm_metric(ni); (PREQ fwd) * :1453 metric = prep->prep_metric + mpm_metric(ni); (PREP recv) * :1557 pprep.prep_metric += mpm_metric(ni); (PREP fwd) * :1972 metric = rann->rann_metric + mpm_metric(ni); (RANN recv) * * Build: cc -O2 -Wall -o hwmp_metric_overflow hwmp_metric_overflow.c * Run: ./hwmp_metric_overflow */ #include <stdio.h> #include <stdint.h> #include <inttypes.h> /* ---- replicated verbatim from ieee80211_hwmp.c:127-134 ---- */ typedef uint32_t ieee80211_hwmp_seq; #define HWMP_SEQ_GT(a, b) ((int32_t)((a)-(b)) > 0) #define HWMP_SEQ_EQ(a, b) ((a) == (b)) /* ---- route-table fields we mutate (rt_metric / hr_seq / nexthop set) ---- */ static uint32_t stored_rt_metric; /* rtorig->rt_metric (ieee80211_mesh.h:429) */ static ieee80211_hwmp_seq stored_hr_seq;/* hrorig->hr_seq */ static uint32_t attacker_nexthop_set; /* route->rt_nexthop = attacker ? 1:0 */ /* ieee80211_hwmp.c:1089 EXACTLY — plain uint32 add, wraps modulo 2^32 */ static uint32_t hwmp_metric_add_vuln(uint32_t a, uint32_t b) { return a + b; } /* proposed fix: saturating add (clamp at UINT32_MAX), HWMP-spec-bounded */ static uint32_t hwmp_metric_add_fixed(uint32_t a, uint32_t b) { uint64_t s = (uint64_t)a + (uint64_t)b; return s > UINT32_MAX ? UINT32_MAX : (uint32_t)s; } /* * Replicates hwmp_recv_preq() accept logic, ieee80211_hwmp.c:1089-1102. * On a fresh sequence number the first clause is TRUE and the route is * updated UNCONDITIONALLY (metric comparison skipped); on an equal-seq PREQ * the wrapped metric must still beat the stored metric to take effect. */ static int recv_preq(uint32_t (*add)(uint32_t, uint32_t), ieee80211_hwmp_seq preq_origseq, uint32_t preq_metric, uint32_t link_metric, const char *tag) { uint32_t metric = add(preq_metric, link_metric); /* :1089 */ int poisoned = 0; if (HWMP_SEQ_GT(preq_origseq, stored_hr_seq) || (HWMP_SEQ_EQ(preq_origseq, stored_hr_seq) && metric < stored_rt_metric)) { /* :1090-1092 */ stored_hr_seq = preq_origseq; /* :1093 */ stored_rt_metric = metric; /* :1095 */ attacker_nexthop_set = 1; /* :1094/:1102 */ poisoned = 1; } printf(" [%-14s] preq_metric=0x%08" PRIx32 " link=%-6u " "=> accumulated=%-11u (0x%08" PRIx32 ") %s\n", tag, preq_metric, link_metric, metric, metric, poisoned ? "*** ROUTE POISONED: attacker nexthop installed ***" : "rejected (legit route preserved)"); return poisoned; } int main(void) { const uint32_t LINK = 50000; /* ~realistic 1-hop airtime metric */ const uint32_t LEGIT_RT_METRIC = 5000000; /* 5 honest hops * 1e6 each */ printf("DF-0351 harness: HWMP uint32 metric-accumulation overflow\n"); printf("(replicates ieee80211_hwmp.c arithmetic; no WiFi HW needed)\n\n"); printf("== Baseline: honest route to originator, rt_metric=%u, hr_seq=100 ==\n\n", LEGIT_RT_METRIC); /* ---- (1) VULNERABLE: fresh sequence number bypasses metric compare ---- */ stored_rt_metric = LEGIT_RT_METRIC; stored_hr_seq = 100; attacker_nexthop_set = 0; printf("-- VULNERABLE kernel, FRESH seq (HWMP_SEQ_GT true -> update forced) --\n"); recv_preq(hwmp_metric_add_vuln, /*origseq=*/101, /*preq_metric=*/0xFFFFFFFF, LINK, "fresh-seq"); printf(" -> rt_metric=%u (was %u); attacker_nexthop=%u\n\n", stored_rt_metric, LEGIT_RT_METRIC, attacker_nexthop_set); /* ---- (2) VULNERABLE: equal seq, wrapped metric beats stored ---- */ stored_rt_metric = LEGIT_RT_METRIC; stored_hr_seq = 100; attacker_nexthop_set = 0; printf("-- VULNERABLE kernel, EQUAL seq (wrapped metric < stored metric) --\n"); recv_preq(hwmp_metric_add_vuln, /*origseq=*/100, /*preq_metric=*/0xFFFFFFFF, LINK, "equal-seq"); printf(" -> rt_metric=%u (legit was %u); attacker_nexthop=%u\n\n", stored_rt_metric, LEGIT_RT_METRIC, attacker_nexthop_set); /* ---- (3) VULNERABLE: PREQ forwarding accumulation, ieee80211_hwmp.c:1325 ---- */ printf("-- VULNERABLE kernel, PREQ forward accumulation (:1325) --\n"); { /* Attacker seeds ppreq.preq_metric near UINT32_MAX. Each honest * forwarder does ppreq.preq_metric += mpm_metric(ni) and re-broadcasts; * the very first forward hop wraps the metric to a tiny value, which is * then propagated to (and poisons) every downstream mesh node. */ uint32_t fwd = 0xFFFFFFFE; uint32_t l1 = 50000, l2 = 80000; printf(" attacker seeds fwd_metric=0x%08" PRIx32 "\n", fwd); fwd += l1; /* :1325 wraps */ printf(" hop1: 0xFFFFFFFE + %u = %u (0x%08" PRIx32 ") <-- WRAPS\n", l1, fwd, fwd); fwd += l2; /* re-broadcast onward */ printf(" hop2: prev + %u = %u -> propagated to all downstream nodes\n\n", l2, fwd); } /* ---- (4) FIXED: saturating add, no wrap ---- */ stored_rt_metric = LEGIT_RT_METRIC; stored_hr_seq = 100; attacker_nexthop_set = 0; printf("-- FIXED kernel (saturating add -> metric clamps to UINT32_MAX) --\n"); recv_preq(hwmp_metric_add_fixed, /*origseq=*/100, /*preq_metric=*/0xFFFFFFFF, LINK, "equal-seq"); printf(" -> rt_metric=%u (legit PRESERVED); attacker_nexthop=%u\n\n", stored_rt_metric, attacker_nexthop_set); printf("VERDICT: vulnerable kernel accepts wrapped near-0 metric and installs the\n"); printf(" attacker as the nexthop (route poisoning / hijack / MITM / blackhole).\n"); printf(" Fixed kernel saturates the sum at UINT32_MAX, so the wrapped tiny\n"); printf(" value never appears and the legitimate route is preserved.\n"); return 0; } |