DF-1510 / harness.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 | /* * DF-1510 harness โ advansys adwcam autosense OOB read/write * * sys/dev/disk/advansys/adwcam.c:383-384 acb->queue.sense_len = * MIN(csio->sense_len, sizeof(acb->sense_data)=32); * ^ firmware-bound value correctly clamped to 32 bytes * * But csio->sense_len is NOT updated, so at completion: * :1321-1322 bcopy(&acb->sense_data, &ccb->csio.sense_data, * ccb->csio.sense_len); <-- raw user value (up to 255) * * Both source (acb->sense_data) and dest (ccb->csio.sense_data) are 32-byte * struct scsi_sense_data. With sense_len=255 the bcopy over-reads 223 bytes * of the ACB past sense_data (acb->links, acb->queue.* โ kernel heap * pointers โ leaked back to the user via ccb->csio.sense_data tail) and * over-writes the ccb->csio fields after sense_data (msg_ptr, resid, * cdb_io, tag_id, init_id). * * csio->sense_len is fully attacker-controlled via the CAM pass-through * /dev/passN + CAMIOCOMMAND path. The guest has no AdvanSys HBA and no * /dev/passN, so this is a harness proof of the genuine MIN/bcopy length * mismatch. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define SENSE_DATA_SIZE 32 /* sizeof(struct scsi_sense_data) */ struct acb { uint8_t sense_data[SENSE_DATA_SIZE]; /* The 223 bytes following sense_data in the real struct acb hold * kernel pointers (links.sle_next, queue.* bus addresses). We model * them as a poison pattern to make the leak visible. */ uint8_t kernel_residue[223]; }; struct csio { uint8_t sense_data[SENSE_DATA_SIZE]; /* dest */ uint8_t sense_len; /* user-controlled */ uint8_t cdb_len; uint16_t sglist_cnt; uint8_t scsi_status; uint8_t sense_resid; uint32_t resid; uint64_t cdb_io; uint8_t *msg_ptr; /* kernel pointer โ over-written if bcopy is big */ uint16_t msg_len; uint8_t tag_action; uint32_t tag_id; uint32_t init_id; }; /* Replicate the MIN() at adwcam.c:383-384. */ static uint8_t clamp_for_firmware(uint8_t sense_len) { return (uint8_t)(sense_len < SENSE_DATA_SIZE ? sense_len : SENSE_DATA_SIZE); } /* Replicate the bcopy at adwcam.c:1321-1322 using the RAW sense_len. */ static void buggy_completion_bcopy(struct acb *acb, struct csio *csio) { /* The bug: bcopy uses csio->sense_len, not acb->queue.sense_len. */ uint8_t n = csio->sense_len; uint8_t *src = acb->sense_data; uint8_t *dst = csio->sense_data; for (uint8_t i = 0; i < n; i++) dst[i] = src[i]; } int main(void) { int bad = 0; uint8_t lens[] = { 0, 18, 32, 33, 64, 128, 200, 255 }; size_t n = sizeof(lens)/sizeof(lens[0]); printf("%-12s %18s %28s\n", "sense_len", "fw-clamped len", "OOB read+write bytes"); for (size_t i = 0; i < n; i++) { struct acb acb; struct csio csio; memset(&acb.sense_data, 0xDD, SENSE_DATA_SIZE); memset(&acb.kernel_residue, 0xCC, sizeof(acb.kernel_residue)); memset(&csio, 0, sizeof(csio)); csio.sense_len = lens[i]; /* firmware clamps (line 383-384) โ this is what the HBA will write */ uint8_t fw_len = clamp_for_firmware(csio.sense_len); /* HBA fills only the first fw_len bytes of acb->sense_data with real * sense bytes; remaining bytes of acb->sense_data stay 0xDD poison * (M_ZERO or stale), then acb->kernel_residue is real kernel data. */ /* Completion uses the raw csio->sense_len (line 1321-1322). */ buggy_completion_bcopy(&acb, &csio); int oob = (int)csio.sense_len - SENSE_DATA_SIZE; printf("%-12u %18u %28d\n", (unsigned)lens[i], (unsigned)fw_len, oob); if (oob > 0) { bad++; /* The bytes [SENSE_DATA_SIZE .. sense_len-1] of csio.sense_data * are now copied from acb.kernel_residue โ i.e. kernel heap. */ int leaked = oob < (int)sizeof(csio) - SENSE_DATA_SIZE ? oob : (int)sizeof(csio) - SENSE_DATA_SIZE; printf(" -> %d bytes of ACB kernel-residue leaked into user ccb; " "%d bytes overwrote csio fields past sense_data\n", oob, leaked); } } printf("\nBuggy sense_lens: %zu/%zu\n", bad, n); if (bad > 0) { printf("\nCONFIRMED: adwcam at adwcam.c:1321-1322 bcopy()s up to %d " "bytes from a %d-byte source (acb->sense_data) into a %d-byte " "dest (ccb->csio.sense_data) when sense_len=255 โ leaking up " "to 223 bytes of kernel heap (ACB pointers) and corrupting " "ccb->csio.msg_ptr/resid/cdb_io/tag_id/init_id. Trigger: " "operator-group CAMIOCOMMAND on /dev/passN against a CHECK-" "CONDITION target on an AdvanSys bus.\n", 255, SENSE_DATA_SIZE, SENSE_DATA_SIZE); return 0; } fprintf(stderr,"NOT CONFIRMED\n"); return 1; } |