DF-3970 / trigger.c
/* * DF-3970 trigger sketch โ rt2560_set_chan unbounded RT2560_RF5222 table walk * =========================================================================== * rt2560.c:2209-2215: * case RT2560_RF_5222: * for (i = 0; rt2560_rf5222[i].chan != chan; i++); * * rt2560_rf_write(sc, RAL_RF1, rt2560_rf5222[i].r1); * ... * * The 41-entry table (rt2560reg.h:449-489) has NO terminator and the loop * has NO bound. The default channel lists built by ieee80211_init_channels * (ieee80211_regdomain.c:101-108: 2GHz 1-14, 5GHz 36-64/100-140/149-161) * happen to be fully covered, so out-of-the-box systems never miss. But * SIOCS80211/IEEE80211_IOC_REGDOMAIN (ieee80211_ioctl.c:2126-2162 -> * ieee80211_regdomain.c:342+ -> null_setregdomain accepts ANYTHING, * regdomain.c:59-65) lets an admin install an arbitrary channel table * (only freq/flags/maxregpower are validated, NOT the ieee number against * driver RF tables), and memcpy it into ic->ic_channels. Selecting a * channel whose ic_ieee is not one of the 41 table entries (e.g. 165, 34, * 184-196) makes the scan walk off the end of .rodata until some byte * happens to equal `chan` โ OOB read across kernel rodata + garbage r1/r2/ * r4 programmed into the RF registers. * * On-hardware trigger (needs an RT2560/RT5222 dual-band NIC; root): * # ifconfig wlan0 down # vaps must be down * # regdomain-set <cc> with a channel table containing ieee=165 / 5825MHz * # (or use a small tool issuing SIOCS80211 IEEE80211_IOC_REGDOMAIN * # with reg.chaninfo including {ic_ieee=165, ic_freq=5825, * # ic_flags=IEEE80211_CHAN_A, ic_maxregpower=30}) * # ifconfig wlan0 channel 165 # -> walk off table end * * Kernel .data/.rodata is fully mapped so the walk almost surely terminates * on a coincidental byte match within a few KB; impact is an OOB read * (info not returned to user) plus misprogrammed RF โ Low. */ #include <stdio.h> int main(void) { printf("hardware-gated + root-config: see header comment for the recipe\n"); return 0; } |